use std::cmp::Ordering;
use std::fmt;
-use syntax::ast::{self, UseTreeKind};
-use syntax::source_map::{self, BytePos, Span, DUMMY_SP};
-use syntax::symbol::sym;
+use core::hash::{Hash, Hasher};
+
+use itertools::Itertools;
+
+use rustc_ast::ast::{self, UseTreeKind};
+use rustc_span::{
+ symbol::{self, sym},
+ BytePos, Span, DUMMY_SP,
+};
use crate::comment::combine_strs_with_missing_comments;
use crate::config::lists::*;
-use crate::config::{Edition, IndentStyle};
+use crate::config::ImportGranularity;
+use crate::config::{Edition, IndentStyle, Version};
use crate::lists::{
definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
};
/// Returns a name imported by a `use` declaration.
/// E.g., returns `Ordering` for `std::cmp::Ordering` and `self` for `std::cmp::self`.
-pub(crate) fn path_to_imported_ident(path: &ast::Path) -> ast::Ident {
+pub(crate) fn path_to_imported_ident(path: &ast::Path) -> symbol::Ident {
path.segments.last().unwrap().ident
}
// sorting.
// FIXME we do a lot of allocation to make our own representation.
-#[derive(Clone, Eq, PartialEq)]
-pub(crate) enum UseSegment {
+#[derive(Clone, Eq, Hash, PartialEq)]
+pub(crate) enum UseSegmentKind {
Ident(String, Option<String>),
Slf(Option<String>),
Super(Option<String>),
List(Vec<UseTree>),
}
+#[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<UseSegment>,
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.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.kind {
+ UseSegmentKind::Ident(_, a)
+ | UseSegmentKind::Slf(a)
+ | UseSegmentKind::Super(a)
+ | UseSegmentKind::Crate(a) => a.as_deref(),
+ _ => None,
}
}
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 UseSegmentKind::List(list) = &self.kind {
+ list.iter().any(|subtree| subtree.contains_comment())
+ } else {
+ false
+ }
+ }
}
-pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>) -> Vec<UseTree> {
+pub(crate) fn normalize_use_trees_with_granularity(
+ use_trees: Vec<UseTree>,
+ import_granularity: ImportGranularity,
+) -> Vec<UseTree> {
+ let merge_by = match import_granularity {
+ ImportGranularity::Item => return flatten_use_trees(use_trees, ImportGranularity::Item),
+ ImportGranularity::Preserve => return use_trees,
+ ImportGranularity::Crate => SharedPrefix::Crate,
+ ImportGranularity::Module => SharedPrefix::Module,
+ ImportGranularity::One => SharedPrefix::One,
+ };
+
let mut result = Vec::with_capacity(use_trees.len());
for use_tree in use_trees {
- if use_tree.has_comment() || use_tree.attrs.is_some() {
+ if use_tree.contains_comment() || use_tree.attrs.is_some() {
result.push(use_tree);
continue;
}
- for flattened in use_tree.flatten() {
- if let Some(tree) = result.iter_mut().find(|tree| tree.share_prefix(&flattened)) {
- tree.merge(&flattened);
+ for mut flattened in use_tree.flatten(import_granularity) {
+ if let Some(tree) = result
+ .iter_mut()
+ .find(|tree| tree.share_prefix(&flattened, merge_by))
+ {
+ tree.merge(&flattened, merge_by);
} else {
+ // If this is the first tree with this prefix, handle potential trailing ::self
+ if merge_by == SharedPrefix::Module {
+ flattened = flattened.nest_trailing_self();
+ }
result.push(flattened);
}
}
result
}
+fn flatten_use_trees(
+ use_trees: Vec<UseTree>,
+ import_granularity: ImportGranularity,
+) -> Vec<UseTree> {
+ // Return non-sorted single occurance of the use-trees text string;
+ // order is by first occurance of the use-tree.
+ use_trees
+ .into_iter()
+ .flat_map(|tree| tree.flatten(import_granularity))
+ .map(UseTree::nest_trailing_self)
+ .unique()
+ .collect()
+}
+
impl fmt::Debug for UseTree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
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<H: Hasher>(&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, _) => 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 {
shape: Shape,
) -> Option<String> {
let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| {
- crate::utils::format_visibility(context, &vis)
+ crate::utils::format_visibility(context, vis)
});
let use_str = self
.rewrite(context, shape.offset_left(vis.len())?)
let allow_extend = if attrs.len() == 1 {
let line_len = attr_str.len() + 1 + use_str.len();
- !attrs.first().unwrap().is_sugared_doc
+ !attrs.first().unwrap().is_doc_comment()
&& context.config.inline_attribute_width() >= line_len
} else {
false
};
let leading_modsep =
- context.config.edition() == Edition::Edition2018 && a.prefix.is_global();
+ context.config.edition() >= Edition::Edition2018 && a.prefix.is_global();
let mut modsep = leading_modsep;
}
}
+ 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.
// This needs to be done before sorting use items.
- let items: Vec<_> = itemize_list(
+ let items = itemize_list(
context.snippet_provider,
list.iter().map(|(tree, _)| tree),
"}",
context.snippet_provider.span_after(a.span, "{"),
a.span.hi(),
false,
- )
- .collect();
+ );
+
// 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.into_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
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);
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;
}
}
// 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 {
- match self.path.last() {
- Some(UseSegment::Ident(_, None)) => {
- aliased_self = true;
- }
- _ => {}
+ if let UseSegmentKind::Slf(_) = last.kind {
+ if let Some(UseSegment {
+ kind: UseSegmentKind::Ident(_, None),
+ ..
+ }) = self.path.last()
+ {
+ aliased_self = true;
}
}
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;
}
}
// 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());
}
}
// 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::<Vec<_>>();
list.sort();
- last = UseSegment::List(list);
+ last = UseSegment {
+ kind: UseSegmentKind::List(list),
+ version: last.version,
+ };
}
self.path.push(last);
self.list_item.as_ref().map_or(false, ListItem::has_comment)
}
+ fn contains_comment(&self) -> bool {
+ self.has_comment() || self.path.iter().any(|path| path.contains_comment())
+ }
+
fn same_visibility(&self, other: &UseTree) -> bool {
match (&self.visibility, &other.visibility) {
(
- Some(source_map::Spanned {
- node: ast::VisibilityKind::Inherited,
+ Some(ast::Visibility {
+ kind: ast::VisibilityKind::Inherited,
..
}),
None,
)
| (
None,
- Some(source_map::Spanned {
- node: ast::VisibilityKind::Inherited,
+ Some(ast::Visibility {
+ kind: ast::VisibilityKind::Inherited,
..
}),
)
}
}
- fn share_prefix(&self, other: &UseTree) -> bool {
+ fn share_prefix(&self, other: &UseTree, shared_prefix: SharedPrefix) -> bool {
if self.path.is_empty()
|| other.path.is_empty()
|| self.attrs.is_some()
+ || self.contains_comment()
|| !self.same_visibility(other)
{
false
} else {
- self.path[0] == other.path[0]
+ match shared_prefix {
+ SharedPrefix::Crate => self.path[0] == other.path[0],
+ SharedPrefix::Module => {
+ self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1]
+ }
+ SharedPrefix::One => true,
+ }
}
}
- fn flatten(self) -> Vec<UseTree> {
- if self.path.is_empty() {
+ fn flatten(self, import_granularity: ImportGranularity) -> Vec<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 {
- match list[0].path[0] {
- UseSegment::Slf(..) => return vec![self],
- _ => (),
+ if let UseSegmentKind::Slf(..) = list[0].path[0].kind {
+ return vec![self];
};
}
let prefix = &self.path[..self.path.len() - 1];
let mut result = vec![];
for nested_use_tree in list {
- for flattend in &mut nested_use_tree.clone().flatten() {
+ for flattend in &mut nested_use_tree.clone().flatten(import_granularity) {
let mut new_path = prefix.to_vec();
new_path.append(&mut flattend.path);
result.push(UseTree {
span: self.span,
list_item: None,
visibility: self.visibility.clone(),
- attrs: None,
+ // only retain attributes for `ImportGranularity::Item`
+ attrs: match import_granularity {
+ ImportGranularity::Item => self.attrs.clone(),
+ _ => None,
+ },
});
}
}
}
}
- fn merge(&mut self, other: &UseTree) {
+ fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) {
let mut prefix = 0;
for (a, b) in self.path.iter().zip(other.path.iter()) {
- if *a == *b {
+ // only discard the alias at the root of the tree
+ if (prefix == 0 && a.equal_except_alias(b)) || a == b {
prefix += 1;
} else {
break;
}
}
- if let Some(new_path) = merge_rest(&self.path, &other.path, prefix) {
+ if let Some(new_path) = merge_rest(&self.path, &other.path, prefix, merge_by) {
self.path = new_path;
self.span = self.span.to(other.span);
}
}
+
+ /// If this tree ends in `::self`, rewrite it to `::{self}`.
+ fn nest_trailing_self(mut self) -> UseTree {
+ if let Some(UseSegment {
+ kind: UseSegmentKind::Slf(..),
+ ..
+ }) = self.path.last()
+ {
+ let self_segment = self.path.pop().unwrap();
+ 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
+ }
}
-fn merge_rest(a: &[UseSegment], b: &[UseSegment], mut len: usize) -> Option<Vec<UseSegment>> {
+fn merge_rest(
+ a: &[UseSegment],
+ b: &[UseSegment],
+ mut len: usize,
+ merge_by: SharedPrefix,
+) -> Option<Vec<UseSegment>> {
if a.len() == len && b.len() == len {
return None;
}
if a.len() != len && b.len() != len {
- if let UseSegment::List(mut list) = a[len].clone() {
- merge_use_trees_inner(&mut list, UseTree::from_path(b[len..].to_vec(), DUMMY_SP));
+ 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,
+ UseTree::from_path(b[len..].to_vec(), DUMMY_SP),
+ 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 {
- let rest = if a.len() == len { &b[1..] } else { &a[1..] };
+ let (common, rest) = if a.len() == len {
+ (&a[0], &b[1..])
+ } 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 { kind, version }],
+ DUMMY_SP,
+ )];
+ match rest {
+ [
+ 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(vec![
- UseTree::from_path(vec![UseSegment::Slf(None)], DUMMY_SP),
- UseTree::from_path(rest.to_vec(), DUMMY_SP),
- ]),
+ UseSegment {
+ kind: UseSegmentKind::List(list),
+ version,
+ },
]);
} else {
len -= 1;
];
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)
}
-fn merge_use_trees_inner(trees: &mut Vec<UseTree>, use_tree: UseTree) {
- let similar_trees = trees.iter_mut().filter(|tree| tree.share_prefix(&use_tree));
- if use_tree.path.len() == 1 {
- if let Some(tree) = similar_trees.min_by_key(|tree| tree.path.len()) {
- if tree.path.len() == 1 {
+fn merge_use_trees_inner(trees: &mut Vec<UseTree>, use_tree: UseTree, merge_by: SharedPrefix) {
+ struct SimilarTree<'a> {
+ similarity: usize,
+ path_len: usize,
+ tree: &'a mut UseTree,
+ }
+
+ let similar_trees = trees.iter_mut().filter_map(|tree| {
+ if tree.share_prefix(&use_tree, merge_by) {
+ // In the case of `SharedPrefix::One`, `similarity` is used for deciding with which
+ // tree `use_tree` should be merge.
+ // In other cases `similarity` won't be used, so set it to `0` as a dummy value.
+ let similarity = if merge_by == SharedPrefix::One {
+ tree.path
+ .iter()
+ .zip(&use_tree.path)
+ .take_while(|(a, b)| a.equal_except_alias(b))
+ .count()
+ } else {
+ 0
+ };
+
+ let path_len = tree.path.len();
+ Some(SimilarTree {
+ similarity,
+ tree,
+ path_len,
+ })
+ } else {
+ None
+ }
+ });
+
+ if use_tree.path.len() == 1 && merge_by == SharedPrefix::Crate {
+ if let Some(tree) = similar_trees.min_by_key(|tree| tree.path_len) {
+ if tree.path_len == 1 {
return;
}
}
- } else {
- if let Some(tree) = similar_trees.max_by_key(|tree| tree.path.len()) {
- if tree.path.len() > 1 {
- tree.merge(&use_tree);
+ } else if merge_by == SharedPrefix::One {
+ if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.similarity) {
+ if sim_tree.similarity > 0 {
+ sim_tree.tree.merge(&use_tree, merge_by);
return;
}
}
+ } else if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.path_len) {
+ if sim_tree.path_len > 1 {
+ sim_tree.tree.merge(&use_tree, merge_by);
+ return;
+ }
}
trees.push(use_tree);
trees.sort();
}
+impl Hash for UseTree {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.path.hash(state);
+ }
+}
+
impl PartialOrd for UseSegment {
fn partial_cmp(&self, other: &UseSegment) -> Option<Ordering> {
Some(self.cmp(other))
}
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)) => a.cmp(b),
- (&Glob, &Glob) => Ordering::Equal,
- (&Ident(ref ia, ref aa), &Ident(ref ib, ref ab)) => {
+ 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)) => {
+ 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, 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;
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)) => {
+ if self.version == Version::Two {
+ aas.trim_start_matches("r#")
+ .cmp(abs.trim_start_matches("r#"))
+ } else {
+ aas.cmp(abs)
+ }
+ }
+ (None, None) => Ordering::Equal,
}
- aa.cmp(ab)
}
- (&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 {
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,
}
}
}
}
}
let has_nested_list = use_tree_list.iter().any(|use_segment| {
- use_segment
- .path
- .last()
- .map_or(false, |last_segment| match last_segment {
- UseSegment::List(..) => true,
- _ => false,
- })
+ use_segment.path.last().map_or(false, |last_segment| {
+ matches!(last_segment.kind, UseSegmentKind::List(..))
+ })
});
let remaining_width = if has_nested_list {
impl Rewrite for UseSegment {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
- 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 "}"
fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option<String> {
let mut result = String::with_capacity(256);
let mut iter = self.path.iter().peekable();
- while let Some(ref segment) = iter.next() {
+ while let Some(segment) = iter.next() {
let segment_str = segment.rewrite(context, shape)?;
result.push_str(&segment_str);
if iter.peek().is_some() {
}
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum SharedPrefix {
+ Crate,
+ Module,
+ One,
+}
+
#[cfg(test)]
mod test {
use super::*;
- use syntax::source_map::DUMMY_SP;
+ use rustc_span::DUMMY_SP;
// Parse the path part of an import. This parser is not robust and is only
// suitable for use in a test harness.
struct Parser<'a> {
input: Peekable<Chars<'a>>,
+ version: Version,
}
impl<'a> Parser<'a> {
}
fn eat(&mut self, c: char) {
- assert!(self.input.next().unwrap() == c);
+ assert_eq!(self.input.next().unwrap(), c);
}
fn push_segment(
+ &self,
result: &mut Vec<UseSegment>,
buf: &mut String,
alias_buf: &mut Option<String>,
) {
+ 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 });
}
}
}
'{' => {
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,
}
}
}
- 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,
let mut parser = Parser {
input: s.chars().peekable(),
+ version: Version::One,
};
parser.parse_in_list()
}
}
}
- #[test]
- fn test_use_tree_merge() {
- macro_rules! test_merge {
- ([$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => {
- assert_eq!(
- merge_use_trees(parse_use_trees!($($input,)*)),
- parse_use_trees!($($output,)*),
- );
- }
+ macro_rules! test_merge {
+ ($by:ident, [$($input:expr),* $(,)*], [$($output:expr),* $(,)*]) => {
+ assert_eq!(
+ normalize_use_trees_with_granularity(
+ parse_use_trees!($($input,)*),
+ ImportGranularity::$by,
+ ),
+ parse_use_trees!($($output,)*),
+ );
}
+ }
- test_merge!(["a::b::{c, d}", "a::b::{e, f}"], ["a::b::{c, d, e, f}"]);
- test_merge!(["a::b::c", "a::b"], ["a::{b, b::c}"]);
- test_merge!(["a::b", "a::b"], ["a::b"]);
- test_merge!(["a", "a::b", "a::b::c"], ["a::{self, b, b::c}"]);
+ #[test]
+ fn test_use_tree_merge_crate() {
+ test_merge!(
+ Crate,
+ ["a::b::{c, d}", "a::b::{e, f}"],
+ ["a::b::{c, d, e, f}"]
+ );
+ test_merge!(Crate, ["a::b::c", "a::b"], ["a::{b, b::c}"]);
+ test_merge!(Crate, ["a::b", "a::b"], ["a::b"]);
+ test_merge!(Crate, ["a", "a::b", "a::b::c"], ["a::{self, b, b::c}"]);
test_merge!(
+ Crate,
["a", "a::b", "a::b::c", "a::b::c::d"],
["a::{self, b, b::{c, c::d}}"]
);
- test_merge!(["a", "a::b", "a::b::c", "a::b"], ["a::{self, b, b::c}"]);
test_merge!(
+ Crate,
+ ["a", "a::b", "a::b::c", "a::b"],
+ ["a::{self, b, b::c}"]
+ );
+ test_merge!(
+ Crate,
["a::{b::{self, c}, d::e}", "a::d::f"],
["a::{b::{self, c}, d::{e, f}}"]
);
test_merge!(
+ Crate,
["a::d::f", "a::{b::{self, c}, d::e}"],
["a::{b::{self, c}, d::{e, f}}"]
);
test_merge!(
+ Crate,
["a::{c, d, b}", "a::{d, e, b, a, f}", "a::{f, g, c}"],
["a::{a, b, c, d, e, f, g}"]
);
test_merge!(
+ Crate,
["a::{self}", "b::{self as foo}"],
["a::{self}", "b::{self as foo}"]
);
}
+ #[test]
+ fn test_use_tree_merge_module() {
+ test_merge!(
+ Module,
+ ["foo::b", "foo::{a, c, d::e}"],
+ ["foo::{a, b, c}", "foo::d::e"]
+ );
+
+ test_merge!(
+ Module,
+ ["foo::{a::b, a::c, d::e, d::f}"],
+ ["foo::a::{b, c}", "foo::d::{e, f}"]
+ );
+ }
+
+ #[test]
+ fn test_use_tree_merge_one() {
+ test_merge!(One, ["a", "b"], ["{a, b}"]);
+
+ test_merge!(One, ["a::{aa, ab}", "b", "a"], ["{a::{self, aa, ab}, b}"]);
+
+ test_merge!(One, ["a as x", "b as y"], ["{a as x, b as y}"]);
+
+ test_merge!(
+ One,
+ ["a::{aa as xa, ab}", "b", "a"],
+ ["{a::{self, aa as xa, ab}, b}"]
+ );
+
+ test_merge!(
+ One,
+ ["a", "a::{aa, ab::{aba, abb}}"],
+ ["a::{self, aa, ab::{aba, abb}}"]
+ );
+
+ test_merge!(One, ["a", "b::{ba, *}"], ["{a, b::{ba, *}}"]);
+
+ test_merge!(One, ["a", "b", "a::aa"], ["{a::{self, aa}, b}"]);
+
+ test_merge!(
+ One,
+ ["a::aa::aaa", "a::ac::aca", "a::aa::*"],
+ ["a::{aa::{aaa, *}, ac::aca}"]
+ );
+
+ test_merge!(
+ One,
+ ["a", "b::{ba, bb}", "a::{aa::*, ab::aba}"],
+ ["{a::{self, aa::*, ab::aba}, b::{ba, bb}}"]
+ );
+
+ test_merge!(
+ One,
+ ["b", "a::ac::{aca, acb}", "a::{aa::*, ab}"],
+ ["{a::{aa::*, ab, ac::{aca, acb}}, b}"]
+ );
+ }
+
+ #[test]
+ fn test_flatten_use_trees() {
+ assert_eq!(
+ flatten_use_trees(
+ parse_use_trees!["foo::{a::{b, c}, d::e}"],
+ ImportGranularity::Item
+ ),
+ parse_use_trees!["foo::a::b", "foo::a::c", "foo::d::e"]
+ );
+
+ assert_eq!(
+ flatten_use_trees(
+ parse_use_trees!["foo::{self, a, b::{c, d}, e::*}"],
+ ImportGranularity::Item
+ ),
+ parse_use_trees![
+ "foo::{self}",
+ "foo::a",
+ "foo::b::c",
+ "foo::b::d",
+ "foo::e::*"
+ ]
+ );
+ }
+
#[test]
fn test_use_tree_flatten() {
assert_eq!(
- parse_use_tree("a::b::{c, d, e, f}").flatten(),
+ parse_use_tree("a::b::{c, d, e, f}").flatten(ImportGranularity::Item),
parse_use_trees!("a::b::c", "a::b::d", "a::b::e", "a::b::f",)
);
assert_eq!(
- parse_use_tree("a::b::{c::{d, e, f}, g, h::{i, j, k}}").flatten(),
+ parse_use_tree("a::b::{c::{d, e, f}, g, h::{i, j, k}}")
+ .flatten(ImportGranularity::Item),
parse_use_trees![
"a::b::c::d",
"a::b::c::e",
< parse_use_tree("std::cmp::{b, e, g, f}").normalize()
);
}
+
+ #[test]
+ fn test_use_tree_nest_trailing_self() {
+ assert_eq!(
+ parse_use_tree("a::b::self").nest_trailing_self(),
+ parse_use_tree("a::b::{self}")
+ );
+ assert_eq!(
+ parse_use_tree("a::b::c").nest_trailing_self(),
+ parse_use_tree("a::b::c")
+ );
+ assert_eq!(
+ parse_use_tree("a::b::{c, d}").nest_trailing_self(),
+ parse_use_tree("a::b::{c, d}")
+ );
+ assert_eq!(
+ parse_use_tree("a::b::{self, c}").nest_trailing_self(),
+ parse_use_tree("a::b::{self, c}")
+ );
+ }
}