diff --git a/src/formatting/imports.rs b/src/formatting/imports.rs index 0a1f0d95c23..294698b3225 100644 --- a/src/formatting/imports.rs +++ b/src/formatting/imports.rs @@ -139,6 +139,29 @@ impl UseSegment { } } + // 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, + _ => false, + } + } + + fn get_alias(&self) -> Option<&str> { + match self { + UseSegment::Ident(_, a) + | UseSegment::Slf(a) + | UseSegment::Super(a) + | UseSegment::Crate(a) => a.as_deref(), + _ => None, + } + } + fn from_path_segment( context: &RewriteContext<'_>, path_seg: &ast::PathSegment, @@ -163,14 +186,6 @@ impl UseSegment { pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) -> Vec { let mut result = Vec::with_capacity(use_trees.len()); - // If `merge_by` is `SharedPrefix::One`, we firstly compute the result as if it's - // `SharedPrefix::Crate`, and then merge the result one more time. - let temp_merge_by = if merge_by == SharedPrefix::One { - SharedPrefix::Crate - } else { - merge_by - }; - for use_tree in use_trees { if use_tree.has_comment() || use_tree.attrs.is_some() { result.push(use_tree); @@ -180,19 +195,15 @@ pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) - for flattened in use_tree.flatten() { if let Some(tree) = result .iter_mut() - .find(|tree| tree.share_prefix(&flattened, temp_merge_by)) + .find(|tree| tree.share_prefix(&flattened, merge_by)) { - tree.merge(&flattened, temp_merge_by); + tree.merge(&flattened, merge_by); } else { result.push(flattened); } } } - if merge_by == SharedPrefix::One { - result = merge_use_trees_into_one(result); - } - result } @@ -574,7 +585,7 @@ impl UseTree { SharedPrefix::Module => { self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1] } - SharedPrefix::One => unreachable!(), + SharedPrefix::One => true, } } } @@ -612,7 +623,7 @@ impl 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 { + if a.equal_except_alias(b) { prefix += 1; } else { break; @@ -647,14 +658,20 @@ fn merge_rest( return Some(new_path); } } else if len == 1 { - let rest = if a.len() == len { &b[1..] } else { &a[1..] }; - 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), - ]), - ]); + let (common, rest) = if a.len() == len { + (&a[0], &b[1..]) + } else { + (&b[0], &a[1..]) + }; + let mut list = vec![UseTree::from_path( + vec![UseSegment::Slf(common.get_alias().map(ToString::to_string))], + DUMMY_SP, + )]; + match rest { + [UseSegment::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)]); } else { len -= 1; } @@ -669,18 +686,48 @@ fn merge_rest( } fn merge_use_trees_inner(trees: &mut Vec, use_tree: UseTree, merge_by: SharedPrefix) { - let similar_trees = trees - .iter_mut() - .filter(|tree| tree.share_prefix(&use_tree, merge_by)); + 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) { + let similarity = tree + .path + .iter() + .zip(&use_tree.path) + .take_while(|(a, b)| a.equal_except_alias(b)) + .count(); + 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 { + if let Some(tree) = similar_trees.min_by_key(|tree| tree.path_len) { + if tree.path_len == 1 { + return; + } + } + } 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(tree) = similar_trees.max_by_key(|tree| tree.path.len()) { - if tree.path.len() > 1 { - tree.merge(&use_tree, merge_by); + } else if let Some(sim_tree) = similar_trees.max_by_key(|tree| (tree.path_len, tree.similarity)) + { + if sim_tree.path_len > 1 { + sim_tree.tree.merge(&use_tree, merge_by); return; } } @@ -688,39 +735,6 @@ fn merge_use_trees_inner(trees: &mut Vec, use_tree: UseTree, merge_by: trees.sort(); } -fn merge_use_trees_into_one(trees: Vec) -> Vec { - let mut result = vec![]; - for mut tree in trees { - if tree.attrs.is_some() { - result.push(tree); - } else if let Some(UseSegment::List(list)) = result.iter_mut().find_map(|r| { - if r.same_visibility(&tree) && r.attrs.is_none() { - r.path.get_mut(0) - } else { - None - } - }) { - list.push(tree); - } else { - tree.path = vec![UseSegment::List(vec![tree.clone()])]; - result.push(tree); - } - } - - // As a result of merging into one, if a `UseTree` has only one crate path just like - // `use {a::{b, c}}`, outmost curly braces can be removed. - for r in result.iter_mut() { - match r.path.get(0) { - Some(UseSegment::List(list)) if r.path.len() == 1 && list.len() == 1 => { - *r = list[0].clone(); - } - _ => {} - } - } - - result -} - impl PartialOrd for UseSegment { fn partial_cmp(&self, other: &UseSegment) -> Option { Some(self.cmp(other))