- let should_insert_comma = lhs_tl
- .r_curly_token()
- .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev))
- .map(|it| it.kind())
- != Some(T![,]);
- let mut to_insert: Vec<SyntaxElement> = Vec::new();
- if should_insert_comma {
- to_insert.push(make::token(T![,]).into());
- to_insert.push(make::tokens::single_space().into());
- }
- to_insert.extend(
- rhs_tl.syntax().children_with_tokens().filter(|it| !matches!(it.kind(), T!['{'] | T!['}'])),
- );
- let pos = InsertPosition::Before(lhs_tl.r_curly_token()?.into());
- let use_tree_list = lhs_tl.insert_children(pos, to_insert);
- Some(lhs.with_use_tree_list(use_tree_list))
+/// Recursively "zips" together lhs and rhs.
+fn recursive_merge(
+ lhs: &ast::UseTree,
+ rhs: &ast::UseTree,
+ merge: MergeBehaviour,
+) -> Option<(ast::UseTree, bool)> {
+ let mut use_trees = lhs
+ .use_tree_list()
+ .into_iter()
+ .flat_map(|list| list.use_trees())
+ // check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this
+ // so early exit the iterator by using Option's Intoiterator impl
+ .map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() {
+ true => None,
+ false => Some(tree),
+ })
+ .collect::<Option<Vec<_>>>()?;
+ use_trees.sort_unstable_by(|a, b| path_cmp_opt(a.path(), b.path()));
+ for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) {
+ let rhs_path = rhs_t.path();
+ match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) {
+ Ok(idx) => {
+ let lhs_t = &mut use_trees[idx];
+ let lhs_path = lhs_t.path()?;
+ let rhs_path = rhs_path?;
+ let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
+ if lhs_prefix == lhs_path && rhs_prefix == rhs_path {
+ let tree_is_self = |tree: ast::UseTree| {
+ tree.path().as_ref().map(path_is_self).unwrap_or(false)
+ };
+ // check if only one of the two trees has a tree list, and whether that then contains `self` or not.
+ // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self`
+ let tree_contains_self = |tree: &ast::UseTree| {
+ tree.use_tree_list()
+ .map(|tree_list| tree_list.use_trees().any(tree_is_self))
+ .unwrap_or(false)
+ };
+ match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) {
+ (true, false) => continue,
+ (false, true) => {
+ *lhs_t = rhs_t;
+ continue;
+ }
+ _ => (),
+ }
+
+ // glob imports arent part of the use-tree lists so we need to special handle them here as well
+ // this special handling is only required for when we merge a module import into a glob import of said module
+ // see the `merge_self_glob` or `merge_mod_into_glob` tests
+ if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() {
+ *lhs_t = make::use_tree(
+ make::path_unqualified(make::path_segment_self()),
+ None,
+ None,
+ false,
+ );
+ use_trees.insert(idx, make::glob_use_tree());
+ continue;
+ }
+ }
+ let lhs = lhs_t.split_prefix(&lhs_prefix);
+ let rhs = rhs_t.split_prefix(&rhs_prefix);
+ let this_has_children = use_trees.len() > 0;
+ match recursive_merge(&lhs, &rhs, merge) {
+ Some((_, has_multiple_children))
+ if merge == MergeBehaviour::Last
+ && this_has_children
+ && has_multiple_children =>
+ {
+ return None
+ }
+ Some((use_tree, _)) => use_trees[idx] = use_tree,
+ None => use_trees.insert(idx, rhs_t),
+ }
+ }
+ Err(_)
+ if merge == MergeBehaviour::Last
+ && use_trees.len() > 0
+ && rhs_t.use_tree_list().is_some() =>
+ {
+ return None
+ }
+ Err(idx) => {
+ use_trees.insert(idx, rhs_t);
+ }
+ }
+ }
+ let has_multiple_children = use_trees.len() > 1;
+ Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children))