From 6afa7137ee48023569b6d0ced45c1552d6cfa6b6 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 13 Jun 2022 08:34:47 -0400 Subject: [PATCH 1/3] Backport 3795 fix sorting of use statements with raw identifiers --- src/imports.rs | 24 ++++++++++++++++-------- tests/source/imports_raw_identifiers.rs | 3 +++ tests/target/imports_raw_identifiers.rs | 3 +++ 3 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 tests/source/imports_raw_identifiers.rs create mode 100644 tests/target/imports_raw_identifiers.rs diff --git a/src/imports.rs b/src/imports.rs index 559ed3917db..6742b14c5a3 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -815,9 +815,16 @@ impl Ord for UseSegment { match (self, other) { (&Slf(ref a), &Slf(ref b)) | (&Super(ref a), &Super(ref b)) - | (&Crate(ref a), &Crate(ref b)) => a.cmp(b), + | (&Crate(ref a), &Crate(ref b)) => match (a, b) { + (Some(sa), Some(sb)) => { + sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + } + (_, _) => a.cmp(b), + }, (&Glob, &Glob) => Ordering::Equal, - (&Ident(ref ia, ref aa), &Ident(ref ib, ref ab)) => { + (&Ident(ref pia, ref aa), &Ident(ref pib, ref ab)) => { + let ia = pia.trim_start_matches("r#"); + let ib = pib.trim_start_matches("r#"); // snake_case < CamelCase < UPPER_SNAKE_CASE if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { return Ordering::Greater; @@ -835,13 +842,14 @@ impl Ord for UseSegment { if ident_ord != Ordering::Equal { return ident_ord; } - if aa.is_none() && ab.is_some() { - return Ordering::Less; - } - if aa.is_some() && ab.is_none() { - return Ordering::Greater; + match (aa, ab) { + (None, Some(_)) => Ordering::Less, + (Some(_), None) => Ordering::Greater, + (Some(aas), Some(abs)) => aas + .trim_start_matches("r#") + .cmp(abs.trim_start_matches("r#")), + (None, None) => Ordering::Equal, } - aa.cmp(ab) } (&List(ref a), &List(ref b)) => { for (a, b) in a.iter().zip(b.iter()) { diff --git a/tests/source/imports_raw_identifiers.rs b/tests/source/imports_raw_identifiers.rs new file mode 100644 index 00000000000..d5857290b62 --- /dev/null +++ b/tests/source/imports_raw_identifiers.rs @@ -0,0 +1,3 @@ +use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; +use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers.rs b/tests/target/imports_raw_identifiers.rs new file mode 100644 index 00000000000..6ae085c8a5f --- /dev/null +++ b/tests/target/imports_raw_identifiers.rs @@ -0,0 +1,3 @@ +use websocket::r#async::futures::Stream; +use websocket::client::ClientBuilder; +use websocket::result::WebSocketError; From 406549ae175add6c727c4bd177ace70d629e8f57 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 13 Jun 2022 10:53:53 -0400 Subject: [PATCH 2/3] Add `Version` information to `UseSegment` There are some proposed import sorting changes for raw identifier `r#`. These changes constitute a breaking change, and need to be version gagted. Before version gating those changes we add the version information to the `UseSegment`. --- src/imports.rs | 311 ++++++++++++++++++++++++++++++++----------------- src/reorder.rs | 10 +- 2 files changed, 206 insertions(+), 115 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index 6742b14c5a3..58c5f31995e 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -15,7 +15,7 @@ use rustc_span::{ use crate::comment::combine_strs_with_missing_comments; use crate::config::lists::*; use crate::config::ImportGranularity; -use crate::config::{Edition, IndentStyle}; +use crate::config::{Edition, IndentStyle, Version}; use crate::lists::{ definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, }; @@ -92,7 +92,7 @@ impl<'a> FmtVisitor<'a> { // FIXME we do a lot of allocation to make our own representation. #[derive(Clone, Eq, Hash, PartialEq)] -pub(crate) enum UseSegment { +pub(crate) enum UseSegmentKind { Ident(String, Option), Slf(Option), Super(Option), @@ -101,6 +101,12 @@ pub(crate) enum UseSegment { List(Vec), } +#[derive(Clone, Eq, PartialEq)] +pub(crate) struct UseSegment { + pub(crate) kind: UseSegmentKind, + pub(crate) version: Version, +} + #[derive(Clone)] pub(crate) struct UseTree { pub(crate) path: Vec, @@ -134,34 +140,38 @@ impl Spanned for UseTree { impl UseSegment { // Clone a version of self with any top-level alias removed. fn remove_alias(&self) -> UseSegment { - match *self { - UseSegment::Ident(ref s, _) => UseSegment::Ident(s.clone(), None), - UseSegment::Slf(_) => UseSegment::Slf(None), - UseSegment::Super(_) => UseSegment::Super(None), - UseSegment::Crate(_) => UseSegment::Crate(None), - _ => self.clone(), + let kind = match self.kind { + UseSegmentKind::Ident(ref s, _) => UseSegmentKind::Ident(s.clone(), None), + UseSegmentKind::Slf(_) => UseSegmentKind::Slf(None), + UseSegmentKind::Super(_) => UseSegmentKind::Super(None), + UseSegmentKind::Crate(_) => UseSegmentKind::Crate(None), + _ => return self.clone(), + }; + UseSegment { + kind, + version: self.version, } } // Check if self == other with their aliases removed. fn equal_except_alias(&self, other: &Self) -> bool { - match (self, other) { - (UseSegment::Ident(ref s1, _), UseSegment::Ident(ref s2, _)) => s1 == s2, - (UseSegment::Slf(_), UseSegment::Slf(_)) - | (UseSegment::Super(_), UseSegment::Super(_)) - | (UseSegment::Crate(_), UseSegment::Crate(_)) - | (UseSegment::Glob, UseSegment::Glob) => true, - (UseSegment::List(ref list1), UseSegment::List(ref list2)) => list1 == list2, + match (&self.kind, &other.kind) { + (UseSegmentKind::Ident(ref s1, _), UseSegmentKind::Ident(ref s2, _)) => s1 == s2, + (UseSegmentKind::Slf(_), UseSegmentKind::Slf(_)) + | (UseSegmentKind::Super(_), UseSegmentKind::Super(_)) + | (UseSegmentKind::Crate(_), UseSegmentKind::Crate(_)) + | (UseSegmentKind::Glob, UseSegmentKind::Glob) => true, + (UseSegmentKind::List(ref list1), UseSegmentKind::List(ref list2)) => list1 == list2, _ => false, } } fn get_alias(&self) -> Option<&str> { - match self { - UseSegment::Ident(_, a) - | UseSegment::Slf(a) - | UseSegment::Super(a) - | UseSegment::Crate(a) => a.as_deref(), + match &self.kind { + UseSegmentKind::Ident(_, a) + | UseSegmentKind::Slf(a) + | UseSegmentKind::Super(a) + | UseSegmentKind::Crate(a) => a.as_deref(), _ => None, } } @@ -175,19 +185,24 @@ impl UseSegment { if name.is_empty() || name == "{{root}}" { return None; } - Some(match name { - "self" => UseSegment::Slf(None), - "super" => UseSegment::Super(None), - "crate" => UseSegment::Crate(None), + let kind = match name { + "self" => UseSegmentKind::Slf(None), + "super" => UseSegmentKind::Super(None), + "crate" => UseSegmentKind::Crate(None), _ => { let mod_sep = if modsep { "::" } else { "" }; - UseSegment::Ident(format!("{}{}", mod_sep, name), None) + UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None) } + }; + + Some(UseSegment { + kind, + version: context.config.version(), }) } fn contains_comment(&self) -> bool { - if let UseSegment::List(list) = self { + if let UseSegmentKind::List(list) = &self.kind { list.iter().any(|subtree| subtree.contains_comment()) } else { false @@ -254,20 +269,38 @@ impl fmt::Debug for UseTree { impl fmt::Debug for UseSegment { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) + fmt::Display::fmt(&self.kind, f) } } impl fmt::Display for UseSegment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.kind, f) + } +} + +impl Hash for UseSegment { + fn hash(&self, state: &mut H) { + self.kind.hash(state); + } +} + +impl fmt::Debug for UseSegmentKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for UseSegmentKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - UseSegment::Glob => write!(f, "*"), - UseSegment::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), - UseSegment::Ident(ref s, None) => write!(f, "{}", s), - UseSegment::Slf(..) => write!(f, "self"), - UseSegment::Super(..) => write!(f, "super"), - UseSegment::Crate(..) => write!(f, "crate"), - UseSegment::List(ref list) => { + UseSegmentKind::Glob => write!(f, "*"), + UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), + UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s), + UseSegmentKind::Slf(..) => write!(f, "self"), + UseSegmentKind::Super(..) => write!(f, "super"), + UseSegmentKind::Crate(..) => write!(f, "crate"), + UseSegmentKind::List(ref list) => { write!(f, "{{")?; for (i, item) in list.iter().enumerate() { if i != 0 { @@ -411,13 +444,19 @@ impl UseTree { } } + let version = context.config.version(); + match a.kind { UseTreeKind::Glob => { // in case of a global path and the glob starts at the root, e.g., "::*" if a.prefix.segments.len() == 1 && leading_modsep { - result.path.push(UseSegment::Ident("".to_owned(), None)); + let kind = UseSegmentKind::Ident("".to_owned(), None); + result.path.push(UseSegment { kind, version }); } - result.path.push(UseSegment::Glob); + result.path.push(UseSegment { + kind: UseSegmentKind::Glob, + version, + }); } UseTreeKind::Nested(ref list) => { // Extract comments between nested use items. @@ -438,16 +477,18 @@ impl UseTree { // in case of a global path and the nested list starts at the root, // e.g., "::{foo, bar}" if a.prefix.segments.len() == 1 && leading_modsep { - result.path.push(UseSegment::Ident("".to_owned(), None)); + let kind = UseSegmentKind::Ident("".to_owned(), None); + result.path.push(UseSegment { kind, version }); } - result.path.push(UseSegment::List( + let kind = UseSegmentKind::List( list.iter() .zip(items) .map(|(t, list_item)| { Self::from_ast(context, &t.0, Some(list_item), None, None, None) }) .collect(), - )); + ); + result.path.push(UseSegment { kind, version }); } UseTreeKind::Simple(ref rename, ..) => { // If the path has leading double colons and is composed of only 2 segments, then we @@ -469,13 +510,15 @@ impl UseTree { Some(rewrite_ident(context, ident).to_owned()) } }); - let segment = match name.as_ref() { - "self" => UseSegment::Slf(alias), - "super" => UseSegment::Super(alias), - "crate" => UseSegment::Crate(alias), - _ => UseSegment::Ident(name, alias), + let kind = match name.as_ref() { + "self" => UseSegmentKind::Slf(alias), + "super" => UseSegmentKind::Super(alias), + "crate" => UseSegmentKind::Crate(alias), + _ => UseSegmentKind::Ident(name, alias), }; + let segment = UseSegment { kind, version }; + // `name` is already in result. result.path.pop(); result.path.push(segment); @@ -492,13 +535,13 @@ impl UseTree { let mut aliased_self = false; // Remove foo::{} or self without attributes. - match last { + match last.kind { _ if self.attrs.is_some() => (), - UseSegment::List(ref list) if list.is_empty() => { + UseSegmentKind::List(ref list) if list.is_empty() => { self.path = vec![]; return self; } - UseSegment::Slf(None) if self.path.is_empty() && self.visibility.is_some() => { + UseSegmentKind::Slf(None) if self.path.is_empty() && self.visibility.is_some() => { self.path = vec![]; return self; } @@ -506,15 +549,19 @@ impl UseTree { } // Normalise foo::self -> foo. - if let UseSegment::Slf(None) = last { + if let UseSegmentKind::Slf(None) = last.kind { if !self.path.is_empty() { return self; } } // Normalise foo::self as bar -> foo as bar. - if let UseSegment::Slf(_) = last { - if let Some(UseSegment::Ident(_, None)) = self.path.last() { + if let UseSegmentKind::Slf(_) = last.kind { + if let Some(UseSegment { + kind: UseSegmentKind::Ident(_, None), + .. + }) = self.path.last() + { aliased_self = true; } } @@ -522,9 +569,12 @@ impl UseTree { let mut done = false; if aliased_self { match self.path.last_mut() { - Some(UseSegment::Ident(_, ref mut old_rename)) => { + Some(UseSegment { + kind: UseSegmentKind::Ident(_, ref mut old_rename), + .. + }) => { assert!(old_rename.is_none()); - if let UseSegment::Slf(Some(rename)) = last.clone() { + if let UseSegmentKind::Slf(Some(rename)) = last.clone().kind { *old_rename = Some(rename); done = true; } @@ -538,15 +588,15 @@ impl UseTree { } // Normalise foo::{bar} -> foo::bar - if let UseSegment::List(ref list) = last { + if let UseSegmentKind::List(ref list) = last.kind { if list.len() == 1 && list[0].to_string() != "self" { normalize_sole_list = true; } } if normalize_sole_list { - match last { - UseSegment::List(list) => { + match last.kind { + UseSegmentKind::List(list) => { for seg in &list[0].path { self.path.push(seg.clone()); } @@ -557,10 +607,13 @@ impl UseTree { } // Recursively normalize elements of a list use (including sorting the list). - if let UseSegment::List(list) = last { + if let UseSegmentKind::List(list) = last.kind { let mut list = list.into_iter().map(UseTree::normalize).collect::>(); list.sort(); - last = UseSegment::List(list); + last = UseSegment { + kind: UseSegmentKind::List(list), + version: last.version, + }; } self.path.push(last); @@ -620,10 +673,10 @@ impl UseTree { if self.path.is_empty() || self.contains_comment() { return vec![self]; } - match self.path.clone().last().unwrap() { - UseSegment::List(list) => { + match &self.path.clone().last().unwrap().kind { + UseSegmentKind::List(list) => { if list.len() == 1 && list[0].path.len() == 1 { - if let UseSegment::Slf(..) = list[0].path[0] { + if let UseSegmentKind::Slf(..) = list[0].path[0].kind { return vec![self]; }; } @@ -671,12 +724,15 @@ impl UseTree { /// If this tree ends in `::self`, rewrite it to `::{self}`. fn nest_trailing_self(mut self) -> UseTree { - if let Some(UseSegment::Slf(..)) = self.path.last() { + if let Some(UseSegment { + kind: UseSegmentKind::Slf(..), + .. + }) = self.path.last() + { let self_segment = self.path.pop().unwrap(); - self.path.push(UseSegment::List(vec![UseTree::from_path( - vec![self_segment], - DUMMY_SP, - )])); + let version = self_segment.version; + let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]); + self.path.push(UseSegment { kind, version }); } self } @@ -692,7 +748,8 @@ fn merge_rest( return None; } if a.len() != len && b.len() != len { - if let UseSegment::List(ref list) = a[len] { + let version = a[len].version; + if let UseSegmentKind::List(ref list) = a[len].kind { let mut list = list.clone(); merge_use_trees_inner( &mut list, @@ -700,7 +757,8 @@ fn merge_rest( merge_by, ); let mut new_path = b[..len].to_vec(); - new_path.push(UseSegment::List(list)); + let kind = UseSegmentKind::List(list); + new_path.push(UseSegment { kind, version }); return Some(new_path); } } else if len == 1 { @@ -709,15 +767,28 @@ fn merge_rest( } else { (&b[0], &a[1..]) }; + let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string)); + let version = a[0].version; let mut list = vec![UseTree::from_path( - vec![UseSegment::Slf(common.get_alias().map(ToString::to_string))], + vec![UseSegment { kind, version }], DUMMY_SP, )]; match rest { - [UseSegment::List(rest_list)] => list.extend(rest_list.clone()), + [ + UseSegment { + kind: UseSegmentKind::List(rest_list), + .. + }, + ] => list.extend(rest_list.clone()), _ => list.push(UseTree::from_path(rest.to_vec(), DUMMY_SP)), } - return Some(vec![b[0].clone(), UseSegment::List(list)]); + return Some(vec![ + b[0].clone(), + UseSegment { + kind: UseSegmentKind::List(list), + version, + }, + ]); } else { len -= 1; } @@ -727,7 +798,9 @@ fn merge_rest( ]; list.sort(); let mut new_path = b[..len].to_vec(); - new_path.push(UseSegment::List(list)); + let kind = UseSegmentKind::List(list); + let version = a[0].version; + new_path.push(UseSegment { kind, version }); Some(new_path) } @@ -805,24 +878,24 @@ impl PartialOrd for UseTree { } impl Ord for UseSegment { fn cmp(&self, other: &UseSegment) -> Ordering { - use self::UseSegment::*; + use self::UseSegmentKind::*; fn is_upper_snake_case(s: &str) -> bool { s.chars() .all(|c| c.is_uppercase() || c == '_' || c.is_numeric()) } - match (self, other) { - (&Slf(ref a), &Slf(ref b)) - | (&Super(ref a), &Super(ref b)) - | (&Crate(ref a), &Crate(ref b)) => match (a, b) { + match (&self.kind, &other.kind) { + (Slf(ref a), Slf(ref b)) + | (Super(ref a), Super(ref b)) + | (Crate(ref a), Crate(ref b)) => match (a, b) { (Some(sa), Some(sb)) => { sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) } (_, _) => a.cmp(b), }, - (&Glob, &Glob) => Ordering::Equal, - (&Ident(ref pia, ref aa), &Ident(ref pib, ref ab)) => { + (Glob, Glob) => Ordering::Equal, + (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => { let ia = pia.trim_start_matches("r#"); let ib = pib.trim_start_matches("r#"); // snake_case < CamelCase < UPPER_SNAKE_CASE @@ -851,7 +924,7 @@ impl Ord for UseSegment { (None, None) => Ordering::Equal, } } - (&List(ref a), &List(ref b)) => { + (List(ref a), List(ref b)) => { for (a, b) in a.iter().zip(b.iter()) { let ord = a.cmp(b); if ord != Ordering::Equal { @@ -861,16 +934,16 @@ impl Ord for UseSegment { a.len().cmp(&b.len()) } - (&Slf(_), _) => Ordering::Less, - (_, &Slf(_)) => Ordering::Greater, - (&Super(_), _) => Ordering::Less, - (_, &Super(_)) => Ordering::Greater, - (&Crate(_), _) => Ordering::Less, - (_, &Crate(_)) => Ordering::Greater, - (&Ident(..), _) => Ordering::Less, - (_, &Ident(..)) => Ordering::Greater, - (&Glob, _) => Ordering::Less, - (_, &Glob) => Ordering::Greater, + (Slf(_), _) => Ordering::Less, + (_, Slf(_)) => Ordering::Greater, + (Super(_), _) => Ordering::Less, + (_, Super(_)) => Ordering::Greater, + (Crate(_), _) => Ordering::Less, + (_, Crate(_)) => Ordering::Greater, + (Ident(..), _) => Ordering::Less, + (_, Ident(..)) => Ordering::Greater, + (Glob, _) => Ordering::Less, + (_, Glob) => Ordering::Greater, } } } @@ -914,7 +987,7 @@ fn rewrite_nested_use_tree( } let has_nested_list = use_tree_list.iter().any(|use_segment| { use_segment.path.last().map_or(false, |last_segment| { - matches!(last_segment, UseSegment::List(..)) + matches!(last_segment.kind, UseSegmentKind::List(..)) }) }); @@ -965,17 +1038,19 @@ fn rewrite_nested_use_tree( impl Rewrite for UseSegment { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - Some(match self { - UseSegment::Ident(ref ident, Some(ref rename)) => format!("{} as {}", ident, rename), - UseSegment::Ident(ref ident, None) => ident.clone(), - UseSegment::Slf(Some(ref rename)) => format!("self as {}", rename), - UseSegment::Slf(None) => "self".to_owned(), - UseSegment::Super(Some(ref rename)) => format!("super as {}", rename), - UseSegment::Super(None) => "super".to_owned(), - UseSegment::Crate(Some(ref rename)) => format!("crate as {}", rename), - UseSegment::Crate(None) => "crate".to_owned(), - UseSegment::Glob => "*".to_owned(), - UseSegment::List(ref use_tree_list) => rewrite_nested_use_tree( + Some(match self.kind { + UseSegmentKind::Ident(ref ident, Some(ref rename)) => { + format!("{} as {}", ident, rename) + } + UseSegmentKind::Ident(ref ident, None) => ident.clone(), + UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename), + UseSegmentKind::Slf(None) => "self".to_owned(), + UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename), + UseSegmentKind::Super(None) => "super".to_owned(), + UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename), + UseSegmentKind::Crate(None) => "crate".to_owned(), + UseSegmentKind::Glob => "*".to_owned(), + UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( context, use_tree_list, // 1 = "{" and "}" @@ -1024,6 +1099,7 @@ mod test { struct Parser<'a> { input: Peekable>, + version: Version, } impl<'a> Parser<'a> { @@ -1036,34 +1112,40 @@ mod test { } fn push_segment( + &self, result: &mut Vec, buf: &mut String, alias_buf: &mut Option, ) { + let version = self.version; if !buf.is_empty() { let mut alias = None; swap(alias_buf, &mut alias); match buf.as_ref() { "self" => { - result.push(UseSegment::Slf(alias)); + let kind = UseSegmentKind::Slf(alias); + result.push(UseSegment { kind, version }); *buf = String::new(); *alias_buf = None; } "super" => { - result.push(UseSegment::Super(alias)); + let kind = UseSegmentKind::Super(alias); + result.push(UseSegment { kind, version }); *buf = String::new(); *alias_buf = None; } "crate" => { - result.push(UseSegment::Crate(alias)); + let kind = UseSegmentKind::Crate(alias); + result.push(UseSegment { kind, version }); *buf = String::new(); *alias_buf = None; } _ => { let mut name = String::new(); swap(buf, &mut name); - result.push(UseSegment::Ident(name, alias)); + let kind = UseSegmentKind::Ident(name, alias); + result.push(UseSegment { kind, version }); } } } @@ -1078,21 +1160,29 @@ mod test { '{' => { assert!(buf.is_empty()); self.bump(); - result.push(UseSegment::List(self.parse_list())); + let kind = UseSegmentKind::List(self.parse_list()); + result.push(UseSegment { + kind, + version: self.version, + }); self.eat('}'); } '*' => { assert!(buf.is_empty()); self.bump(); - result.push(UseSegment::Glob); + let kind = UseSegmentKind::Glob; + result.push(UseSegment { + kind, + version: self.version, + }); } ':' => { self.bump(); self.eat(':'); - Self::push_segment(&mut result, &mut buf, &mut alias_buf); + self.push_segment(&mut result, &mut buf, &mut alias_buf); } '}' | ',' => { - Self::push_segment(&mut result, &mut buf, &mut alias_buf); + self.push_segment(&mut result, &mut buf, &mut alias_buf); return UseTree { path: result, span: DUMMY_SP, @@ -1118,7 +1208,7 @@ mod test { } } } - Self::push_segment(&mut result, &mut buf, &mut alias_buf); + self.push_segment(&mut result, &mut buf, &mut alias_buf); UseTree { path: result, span: DUMMY_SP, @@ -1144,6 +1234,7 @@ mod test { let mut parser = Parser { input: s.chars().peekable(), + version: Version::One, }; parser.parse_in_list() } diff --git a/src/reorder.rs b/src/reorder.rs index 8ae297de25b..9e4a668aa49 100644 --- a/src/reorder.rs +++ b/src/reorder.rs @@ -12,7 +12,7 @@ use rustc_ast::ast; use rustc_span::{symbol::sym, Span}; use crate::config::{Config, GroupImportsTactic}; -use crate::imports::{normalize_use_trees_with_granularity, UseSegment, UseTree}; +use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree}; use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; use crate::rewrite::RewriteContext; @@ -182,16 +182,16 @@ fn group_imports(uts: Vec) -> Vec> { external_imports.push(ut); continue; } - match &ut.path[0] { - UseSegment::Ident(id, _) => match id.as_ref() { + match &ut.path[0].kind { + UseSegmentKind::Ident(id, _) => match id.as_ref() { "std" | "alloc" | "core" => std_imports.push(ut), _ => external_imports.push(ut), }, - UseSegment::Slf(_) | UseSegment::Super(_) | UseSegment::Crate(_) => { + UseSegmentKind::Slf(_) | UseSegmentKind::Super(_) | UseSegmentKind::Crate(_) => { local_imports.push(ut) } // These are probably illegal here - UseSegment::Glob | UseSegment::List(_) => external_imports.push(ut), + UseSegmentKind::Glob | UseSegmentKind::List(_) => external_imports.push(ut), } } From bd3630d469f4eb4f543f99bbad49f376a9eff252 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 13 Jun 2022 11:36:20 -0400 Subject: [PATCH 3/3] Version gate raw identifier use statement sorting When useing `version=One` rustfmt will treat the leading `r#` as part of the `UseSegment` used for sorting. When using `version=Two` rustfmt will ignore the leading `r#` and only consider the name of the identifier when sorting the `UseSegment`. --- src/imports.rs | 24 ++++++++++++++----- .../version_One.rs} | 2 ++ .../imports_raw_identifiers/version_Two.rs} | 4 +++- .../imports_raw_identifiers/version_One.rs | 5 ++++ .../imports_raw_identifiers/version_Two.rs | 5 ++++ 5 files changed, 33 insertions(+), 7 deletions(-) rename tests/source/{imports_raw_identifiers.rs => imports_raw_identifiers/version_One.rs} (83%) rename tests/{target/imports_raw_identifiers.rs => source/imports_raw_identifiers/version_Two.rs} (83%) create mode 100644 tests/target/imports_raw_identifiers/version_One.rs create mode 100644 tests/target/imports_raw_identifiers/version_Two.rs diff --git a/src/imports.rs b/src/imports.rs index 58c5f31995e..8d41c881589 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -890,14 +890,21 @@ impl Ord for UseSegment { | (Super(ref a), Super(ref b)) | (Crate(ref a), Crate(ref b)) => match (a, b) { (Some(sa), Some(sb)) => { - sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + if self.version == Version::Two { + sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + } else { + a.cmp(b) + } } (_, _) => a.cmp(b), }, (Glob, Glob) => Ordering::Equal, (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => { - let ia = pia.trim_start_matches("r#"); - let ib = pib.trim_start_matches("r#"); + let (ia, ib) = if self.version == Version::Two { + (pia.trim_start_matches("r#"), pib.trim_start_matches("r#")) + } else { + (pia.as_str(), pib.as_str()) + }; // snake_case < CamelCase < UPPER_SNAKE_CASE if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { return Ordering::Greater; @@ -918,9 +925,14 @@ impl Ord for UseSegment { match (aa, ab) { (None, Some(_)) => Ordering::Less, (Some(_), None) => Ordering::Greater, - (Some(aas), Some(abs)) => aas - .trim_start_matches("r#") - .cmp(abs.trim_start_matches("r#")), + (Some(aas), Some(abs)) => { + if self.version == Version::Two { + aas.trim_start_matches("r#") + .cmp(abs.trim_start_matches("r#")) + } else { + aas.cmp(abs) + } + } (None, None) => Ordering::Equal, } } diff --git a/tests/source/imports_raw_identifiers.rs b/tests/source/imports_raw_identifiers/version_One.rs similarity index 83% rename from tests/source/imports_raw_identifiers.rs rename to tests/source/imports_raw_identifiers/version_One.rs index d5857290b62..bc4b5b13569 100644 --- a/tests/source/imports_raw_identifiers.rs +++ b/tests/source/imports_raw_identifiers/version_One.rs @@ -1,3 +1,5 @@ +// rustfmt-version:One + use websocket::client::ClientBuilder; use websocket::r#async::futures::Stream; use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers.rs b/tests/source/imports_raw_identifiers/version_Two.rs similarity index 83% rename from tests/target/imports_raw_identifiers.rs rename to tests/source/imports_raw_identifiers/version_Two.rs index 6ae085c8a5f..88e7fbd01ca 100644 --- a/tests/target/imports_raw_identifiers.rs +++ b/tests/source/imports_raw_identifiers/version_Two.rs @@ -1,3 +1,5 @@ -use websocket::r#async::futures::Stream; +// rustfmt-version:Two + use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers/version_One.rs b/tests/target/imports_raw_identifiers/version_One.rs new file mode 100644 index 00000000000..bc4b5b13569 --- /dev/null +++ b/tests/target/imports_raw_identifiers/version_One.rs @@ -0,0 +1,5 @@ +// rustfmt-version:One + +use websocket::client::ClientBuilder; +use websocket::r#async::futures::Stream; +use websocket::result::WebSocketError; diff --git a/tests/target/imports_raw_identifiers/version_Two.rs b/tests/target/imports_raw_identifiers/version_Two.rs new file mode 100644 index 00000000000..22bfe93122f --- /dev/null +++ b/tests/target/imports_raw_identifiers/version_Two.rs @@ -0,0 +1,5 @@ +// rustfmt-version:Two + +use websocket::r#async::futures::Stream; +use websocket::client::ClientBuilder; +use websocket::result::WebSocketError;