Repository: rust-analyzer/rust-analyzer
Pull request #10839 (2021-11-22 17:01:25)

fix: Fix mbe::Shift::new not accounting for non-ident token ids
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/9371
bors r+


Patch:
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 409d0cc66f..bca380a4d6 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -279,6 +279,13 @@ impl HirFileId {
     pub fn is_macro(self) -> bool {
         matches!(self.0, HirFileIdRepr::MacroFile(_))
     }
+
+    pub fn macro_file(self) -> Option<MacroFile> {
+        match self.0 {
+            HirFileIdRepr::FileId(_) => None,
+            HirFileIdRepr::MacroFile(m) => Some(m),
+        }
+    }
 }
 
 impl MacroDefId {
@@ -377,6 +384,8 @@ pub struct ExpansionInfo {
 
     macro_def: Arc<TokenExpander>,
     macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
+    /// A shift built from `macro_arg`'s subtree, relevant for attributes as the item is the macro arg
+    /// and as such we need to shift tokens if they are part of an attributes input instead of their item.
     macro_arg_shift: mbe::Shift,
     exp_map: Arc<mbe::TokenMap>,
 }
@@ -390,6 +399,20 @@ impl ExpansionInfo {
         Some(self.arg.with_value(self.arg.value.parent()?))
     }
 
+    /// Map a token down from macro input into the macro expansion.
+    ///
+    /// The inner workings of this function differ slightly depending on the type of macro we are dealing with:
+    /// - declarative:
+    ///     For declarative macros, we need to accommodate for the macro definition site(which acts as a second unchanging input)
+    ///     , as tokens can mapped in and out of it.
+    ///     To do this we shift all ids in the expansion by the maximum id of the definition site giving us an easy
+    ///     way to map all the tokens.
+    /// - attribute:
+    ///     Attributes have two different inputs, the input tokentree in the attribute node and the item
+    ///     the attribute is annotating. Similarly as for declarative macros we need to do a shift here
+    ///     as well. Currently this is done by shifting the attribute input by the maximum id of the item.
+    /// - function-like and derives:
+    ///     Both of these only have one simple call site input so no special handling is required here.
     pub fn map_token_down(
         &self,
         db: &dyn db::AstDatabase,
@@ -397,12 +420,10 @@ impl ExpansionInfo {
         token: InFile<&SyntaxToken>,
     ) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
         assert_eq!(token.file_id, self.arg.file_id);
-        let token_id = if let Some(item) = item {
+        let token_id_in_attr_input = if let Some(item) = item {
             // check if we are mapping down in an attribute input
-            let call_id = match self.expanded.file_id.0 {
-                HirFileIdRepr::FileId(_) => return None,
-                HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
-            };
+            // this is a special case as attributes can have two inputs
+            let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
             let loc = db.lookup_intern_macro_call(call_id);
 
             let token_range = token.value.text_range();
@@ -415,9 +436,12 @@ impl ExpansionInfo {
                         {
                             let attr_input_start =
                                 token_tree.left_delimiter_token()?.text_range().start();
-                            let range = token.value.text_range().checked_sub(attr_input_start)?;
-                            let token_id =
-                                self.macro_arg_shift.shift(attr_args.1.token_by_range(range)?);
+                            let relative_range =
+                                token.value.text_range().checked_sub(attr_input_start)?;
+                            // shift by the item's tree's max id
+                            let token_id = self
+                                .macro_arg_shift
+                                .shift(attr_args.1.token_by_range(relative_range)?);
                             Some(token_id)
                         }
                         _ => None,
@@ -429,12 +453,14 @@ impl ExpansionInfo {
             None
         };
 
-        let token_id = match token_id {
+        let token_id = match token_id_in_attr_input {
             Some(token_id) => token_id,
+            // the token is not inside an attribute's input so do the lookup in the macro_arg as ususal
             None => {
-                let range =
+                let relative_range =
                     token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
-                let token_id = self.macro_arg.1.token_by_range(range)?;
+                let token_id = self.macro_arg.1.token_by_range(relative_range)?;
+                // conditionally shift the id by a declaratives macro definition
                 self.macro_def.map_id_down(token_id)
             }
         };
@@ -447,28 +473,33 @@ impl ExpansionInfo {
         Some(tokens.map(move |token| self.expanded.with_value(token)))
     }
 
+    /// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion.
     pub fn map_token_up(
         &self,
         db: &dyn db::AstDatabase,
         token: InFile<&SyntaxToken>,
     ) -> Option<(InFile<SyntaxToken>, Origin)> {
+        // Fetch the id through its text range,
         let token_id = self.exp_map.token_by_range(token.value.text_range())?;
+        // conditionally unshifting the id to accommodate for macro-rules def site
         let (mut token_id, origin) = self.macro_def.map_id_up(token_id);
 
-        let call_id = match self.expanded.file_id.0 {
-            HirFileIdRepr::FileId(_) => return None,
-            HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
-        };
+        let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
         let loc = db.lookup_intern_macro_call(call_id);
 
+        // Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
         let (token_map, tt) = match &loc.kind {
-            MacroCallKind::Attr { attr_args, .. } => match self.macro_arg_shift.unshift(token_id) {
-                Some(unshifted) => {
-                    token_id = unshifted;
-                    (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+            MacroCallKind::Attr { attr_args: (_, arg_token_map), .. } => {
+                // try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
+                // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
+                match self.macro_arg_shift.unshift(token_id) {
+                    Some(unshifted) => {
+                        token_id = unshifted;
+                        (arg_token_map, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+                    }
+                    None => (&self.macro_arg.1, self.arg.clone()),
                 }
-                None => (&self.macro_arg.1, self.arg.clone()),
-            },
+            }
             _ => match origin {
                 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
                 mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def) {
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index 94400be7a5..fc117bd9a8 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -80,7 +80,7 @@ use std::fmt$0::{Display, Debug};
 use std::fmt::{Display, Debug};
 ",
             r"
-use std::fmt::{Display, Debug};
+use std::fmt::{Debug, Display};
 ",
         )
     }
@@ -108,7 +108,7 @@ use std::fmt::Debug;
 use std::fmt$0::Display;
 ",
             r"
-use std::fmt::{Display, Debug};
+use std::fmt::{Debug, Display};
 ",
         );
     }
@@ -135,7 +135,7 @@ use std::fmt::{self, Display};
 use std::{fmt, $0fmt::Display};
 ",
             r"
-use std::{fmt::{Display, self}};
+use std::{fmt::{self, Display}};
 ",
         );
     }
@@ -247,7 +247,7 @@ use std::{fmt::{Debug, Display}};
 use std::{fmt::Debug, fmt$0::Display};
 ",
             r"
-use std::{fmt::{Display, Debug}};
+use std::{fmt::{Debug, Display}};
 ",
         );
     }
@@ -341,9 +341,7 @@ use foo::$0{
 };
 ",
             r"
-use foo::{
-    FooBar, bar::baz,
-};
+use foo::{FooBar, bar::baz};
 ",
         )
     }
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
index 6b196681ca..8df8c4b726 100644
--- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -299,7 +299,7 @@ fn main() {
     ",
             r"
 mod std { pub mod fmt { pub trait Display {} } }
-use std::fmt::{Display, self};
+use std::fmt::{self, Display};
 
 fn main() {
     fmt;
diff --git a/crates/ide_assists/src/handlers/split_import.rs b/crates/ide_assists/src/handlers/split_import.rs
index ac67874b31..090df25cbd 100644
--- a/crates/ide_assists/src/handlers/split_import.rs
+++ b/crates/ide_assists/src/handlers/split_import.rs
@@ -19,20 +19,14 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
     let use_tree = path.top_path().syntax().ancestors().find_map(ast::UseTree::cast)?;
 
-    let has_errors = use_tree
-        .syntax()
-        .descendants_with_tokens()
-        .any(|it| it.kind() == syntax::SyntaxKind::ERROR);
-    let last_segment = use_tree.path().and_then(|it| it.segment());
-    if has_errors || last_segment.is_none() {
+    let new_tree = use_tree.split_prefix(&path);
+    if new_tree == use_tree {
         return None;
     }
 
     let target = colon_colon.text_range();
     acc.add(AssistId("split_import", AssistKind::RefactorRewrite), "Split import", target, |edit| {
-        let use_tree = edit.make_mut(use_tree.clone());
-        let path = edit.make_mut(path);
-        use_tree.split_prefix(&path);
+        edit.replace_ast(use_tree, new_tree);
     })
 }
 
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index 8581a83436..6a7a9b8f61 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -44,10 +44,10 @@ pub fn try_merge_imports(
     }
 
     let lhs = lhs.clone_subtree().clone_for_update();
-    let rhs = rhs.clone_subtree().clone_for_update();
     let lhs_tree = lhs.use_tree()?;
     let rhs_tree = rhs.use_tree()?;
-    try_merge_trees_mut(&lhs_tree, &rhs_tree, merge_behavior)?;
+    let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?;
+    ted::replace(lhs_tree.syntax(), merged.syntax());
     Some(lhs)
 }
 
@@ -56,49 +56,57 @@ pub fn try_merge_trees(
     rhs: &ast::UseTree,
     merge: MergeBehavior,
 ) -> Option<ast::UseTree> {
-    let lhs = lhs.clone_subtree().clone_for_update();
-    let rhs = rhs.clone_subtree().clone_for_update();
-    try_merge_trees_mut(&lhs, &rhs, merge)?;
-    Some(lhs)
-}
-
-fn try_merge_trees_mut(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> {
     let lhs_path = lhs.path()?;
     let rhs_path = rhs.path()?;
 
     let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
-    if !(lhs.is_simple_path()
+    let (lhs, rhs) = if lhs.is_simple_path()
         && rhs.is_simple_path()
         && lhs_path == lhs_prefix
-        && rhs_path == rhs_prefix)
+        && rhs_path == rhs_prefix
     {
-        lhs.split_prefix(&lhs_prefix);
-        rhs.split_prefix(&rhs_prefix);
-    }
-    recursive_merge(&lhs, &rhs, merge)
+        (lhs.clone(), rhs.clone())
+    } else {
+        (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix))
+    };
+    recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update())
 }
 
-/// Recursively merges rhs to lhs
-#[must_use]
-fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> {
-    let mut use_trees: Vec<ast::UseTree> = lhs
+/// Recursively "zips" together lhs and rhs.
+fn recursive_merge(
+    lhs: &ast::UseTree,
+    rhs: &ast::UseTree,
+    merge: MergeBehavior,
+) -> Option<ast::UseTree> {
+    let mut use_trees = lhs
         .use_tree_list()
         .into_iter()
         .flat_map(|list| list.use_trees())
         // We use Option here to early return from this function(this is not the
         // same as a `filter` op).
-        .map(|tree| merge.is_tree_allowed(&tree).then(|| tree))
-        .collect::<Option<_>>()?;
+        .map(|tree| match merge.is_tree_allowed(&tree) {
+            true => Some(tree),
+            false => None,
+        })
+        .collect::<Option<Vec<_>>>()?;
     use_trees.sort_unstable_by(|a, b| path_cmp_for_sort(a.path(), b.path()));
     for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) {
         if !merge.is_tree_allowed(&rhs_t) {
             return None;
         }
         let rhs_path = rhs_t.path();
+        match use_trees.binary_search_by(|lhs_t| {
+            let (lhs_t, rhs_t) = match lhs_t
+                .path()
+                .zip(rhs_path.clone())
+                .and_then(|(lhs, rhs)| common_prefix(&lhs, &rhs))
+            {
+                Some((lhs_p, rhs_p)) => (lhs_t.split_prefix(&lhs_p), rhs_t.split_prefix(&rhs_p)),
+                None => (lhs_t.clone(), rhs_t.clone()),
+            };
 
-        match use_trees
-            .binary_search_by(|lhs_t| path_cmp_bin_search(lhs_t.path(), rhs_path.as_ref()))
-        {
+            path_cmp_bin_search(lhs_t.path(), rhs_t.path())
+        }) {
             Ok(idx) => {
                 let lhs_t = &mut use_trees[idx];
                 let lhs_path = lhs_t.path()?;
@@ -120,7 +128,6 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior)
                     match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) {
                         (true, false) => continue,
                         (false, true) => {
-                            ted::replace(lhs_t.syntax(), rhs_t.syntax());
                             *lhs_t = rhs_t;
                             continue;
                         }
@@ -135,27 +142,25 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior)
                     if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() {
                         if tree_is_self(lhs_t) || tree_is_self(&rhs_t) {
                             cov_mark::hit!(merge_self_glob);
-                            let self_tree = make::use_tree(
+                            *lhs_t = make::use_tree(
                                 make::path_unqualified(make::path_segment_self()),
                                 None,
                                 None,
                                 false,
-                            )
-                            .clone_for_update();
-                            ted::replace(lhs_t.syntax(), self_tree.syntax());
-                            *lhs_t = self_tree;
-                            let glob = make::use_tree_glob().clone_for_update();
-                            use_trees.insert(idx, glob.clone());
-                            lhs.get_or_create_use_tree_list().add_use_tree(glob);
+                            );
+                            use_trees.insert(idx, make::use_tree_glob());
                             continue;
                         }
                     } else if lhs_t.use_tree_list().is_none() && rhs_t.use_tree_list().is_none() {
                         continue;
                     }
                 }
-                lhs_t.split_prefix(&lhs_prefix);
-                rhs_t.split_prefix(&rhs_prefix);
-                recursive_merge(&lhs_t, &rhs_t, merge)?;
+                let lhs = lhs_t.split_prefix(&lhs_prefix);
+                let rhs = rhs_t.split_prefix(&rhs_prefix);
+                match recursive_merge(&lhs, &rhs, merge) {
+                    Some(use_tree) => use_trees[idx] = use_tree,
+                    None => return None,
+                }
             }
             Err(_)
                 if merge == MergeBehavior::Module
@@ -165,12 +170,16 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior)
                 return None
             }
             Err(idx) => {
-                use_trees.insert(idx, rhs_t.clone());
-                lhs.get_or_create_use_tree_list().add_use_tree(rhs_t);
+                use_trees.insert(idx, rhs_t);
             }
         }
     }
-    Some(())
+
+    let lhs = lhs.clone_subtree().clone_for_update();
+    if let Some(old) = lhs.use_tree_list() {
+        ted::replace(old.syntax(), make::use_tree_list(use_trees).syntax().clone_for_update());
+    }
+    ast::UseTree::cast(lhs.syntax().clone_subtree())
 }
 
 /// Traverses both paths until they differ, returning the common prefix of both.
@@ -215,9 +224,11 @@ fn path_cmp_for_sort(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering {
 }
 
 /// Path comparison func for binary searching for merging.
-fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<&ast::Path>) -> Ordering {
-    match (lhs.as_ref().and_then(ast::Path::first_segment), rhs.and_then(ast::Path::first_segment))
-    {
+fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering {
+    match (
+        lhs.as_ref().and_then(ast::Path::first_segment),
+        rhs.as_ref().and_then(ast::Path::first_segment),
+    ) {
         (None, None) => Ordering::Equal,
         (None, Some(_)) => Ordering::Less,
         (Some(_), None) => Ordering::Greater,
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 833a67ad99..b58b86b38d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -120,12 +120,15 @@ impl Shift {
                             _ => tree_id,
                         }
                     }
-                    tt::TokenTree::Leaf(tt::Leaf::Ident(ident))
-                        if ident.id != tt::TokenId::unspecified() =>
-                    {
-                        Some(ident.id.0)
+                    tt::TokenTree::Leaf(leaf) => {
+                        let id = match leaf {
+                            tt::Leaf::Literal(it) => it.id,
+                            tt::Leaf::Punct(it) => it.id,
+                            tt::Leaf::Ident(it) => it.id,
+                        };
+
+                        (id != tt::TokenId::unspecified()).then(|| id.0)
                     }
-                    _ => None,
                 })
                 .max()
         }
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 15805dfc86..43a9c6756d 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -4,10 +4,48 @@
 use std::{fmt, iter, ops};
 
 use crate::{
+    algo,
     ast::{self, make, AstNode},
     ted, AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
 };
 
+impl ast::UseTree {
+    /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items.
+    #[must_use]
+    pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree {
+        let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() {
+            make::path_unqualified(make::path_segment_self())
+        } else {
+            match split_path_prefix(prefix) {
+                Some(it) => it,
+                None => return self.clone(),
+            }
+        };
+
+        let use_tree = make::use_tree(
+            suffix,
+            self.use_tree_list(),
+            self.rename(),
+            self.star_token().is_some(),
+        );
+        let nested = make::use_tree_list(iter::once(use_tree));
+        return make::use_tree(prefix.clone(), Some(nested), None, false);
+
+        fn split_path_prefix(prefix: &ast::Path) -> Option<ast::Path> {
+            let parent = prefix.parent_path()?;
+            let segment = parent.segment()?;
+            if algo::has_errors(segment.syntax()) {
+                return None;
+            }
+            let mut res = make::path_unqualified(segment);
+            for p in iter::successors(parent.parent_path(), |it| it.parent_path()) {
+                res = make::path_qualified(res, p.segment()?);
+            }
+            Some(res)
+        }
+    }
+}
+
 #[derive(Debug, Clone, Copy)]
 pub struct IndentLevel(pub u8);
 
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index bf4f9d6d18..552d6fc1e3 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -1,12 +1,12 @@
 //! Structural editing for ast.
 
-use std::iter::{empty, successors};
+use std::iter::empty;
 
 use parser::{SyntaxKind, T};
 use rowan::SyntaxElement;
 
 use crate::{
-    algo::{self, neighbor},
+    algo::neighbor,
     ast::{self, edit::IndentLevel, make, HasGenericParams},
     ted::{self, Position},
     AstNode, AstToken, Direction,
@@ -282,78 +282,6 @@ impl ast::UseTree {
         }
         ted::remove(self.syntax());
     }
-
-    pub fn get_or_create_use_tree_list(&self) -> ast::UseTreeList {
-        match self.use_tree_list() {
-            Some(it) => it,
-            None => {
-                let position = Position::last_child_of(self.syntax());
-                let use_tree_list = make::use_tree_list(empty()).clone_for_update();
-                let mut elements = Vec::with_capacity(2);
-                if self.coloncolon_token().is_none() {
-                    elements.push(make::token(T![::]).into());
-                }
-                elements.push(use_tree_list.syntax().clone().into());
-                ted::insert_all_raw(position, elements);
-                use_tree_list
-            }
-        }
-    }
-
-    /// Splits off the given prefix, making it the path component of the use tree,
-    /// appending the rest of the path to all UseTreeList items.
-    pub fn split_prefix(&self, prefix: &ast::Path) {
-        debug_assert_eq!(self.path(), Some(prefix.top_path()));
-        let path = self.path().unwrap();
-        if &path == prefix && self.use_tree_list().is_none() {
-            let self_suffix = make::path_unqualified(make::path_segment_self()).clone_for_update();
-            ted::replace(path.syntax(), self_suffix.syntax());
-        } else if split_path_prefix(prefix).is_none() {
-            return;
-        }
-
-        let subtree = self.clone_subtree().clone_for_update();
-        ted::remove_all_iter(self.syntax().children_with_tokens());
-        ted::insert(Position::first_child_of(self.syntax()), prefix.syntax());
-        self.get_or_create_use_tree_list().add_use_tree(subtree);
-
-        fn split_path_prefix(prefix: &ast::Path) -> Option<()> {
-            let parent = prefix.parent_path()?;
-            let segment = parent.segment()?;
-            if algo::has_errors(segment.syntax()) {
-                return None;
-            }
-            for p in successors(parent.parent_path(), |it| it.parent_path()) {
-                p.segment()?;
-            }
-            prefix.parent_path().and_then(|p| p.coloncolon_token()).map(ted::remove);
-            ted::remove(prefix.syntax());
-            Some(())
-        }
-    }
-}
-
-impl ast::UseTreeList {
-    pub fn add_use_tree(&self, use_tree: ast::UseTree) {
-        let (position, elements) = match self.use_trees().last() {
-            Some(last_tree) => (
-                Position::after(last_tree.syntax()),
-                vec![
-                    make::token(T![,]).into(),
-                    make::tokens::single_space().into(),
-                    use_tree.syntax.into(),
-                ],
-            ),
-            None => {
-                let position = match self.l_curly_token() {
-                    Some(l_curly) => Position::after(l_curly),
-                    None => Position::last_child_of(self.syntax()),
-                };
-                (position, vec![use_tree.syntax.into()])
-            }
-        };
-        ted::insert_all_raw(position, elements);
-    }
 }
 
 impl ast::Use {
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
index acb13282f6..d0ddf27b4d 100644
--- a/xtask/src/release.rs
+++ b/xtask/src/release.rs
@@ -27,11 +27,6 @@ impl flags::Release {
             .run()?;
 
         let website_root = project_root().join("../rust-analyzer.github.io");
-        {
-            let _dir = pushd(&website_root)?;
-            cmd!("git switch src").run()?;
-            cmd!("git pull").run()?;
-        }
         let changelog_dir = website_root.join("./thisweek/_posts");
 
         let today = date_iso()?;
@@ -81,7 +76,7 @@ impl flags::Promote {
         cmd!("git add src/tools/rust-analyzer").run()?;
         cmd!("git commit -m':arrow_up: rust-analyzer'").run()?;
         if !self.dry_run {
-            cmd!("git push -u origin {branch}").run()?;
+            cmd!("git push -u").run()?;
             cmd!("xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost")
                 .run()?;
         }


Test patch:
diff --git a/crates/hir_def/src/macro_expansion_tests/mbe.rs b/crates/hir_def/src/macro_expansion_tests/mbe.rs
index 962e1e7459..466c85fc5b 100644
--- a/crates/hir_def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir_def/src/macro_expansion_tests/mbe.rs
@@ -27,7 +27,7 @@ macro_rules! f {
 f!(struct MyTraitMap2);
 "#,
         expect![[r##"
-// call ids will be shifted by Shift(27)
+// call ids will be shifted by Shift(30)
 // +tokenids
 macro_rules! f {#0
     (#1 struct#2 $#3ident#4:#5ident#6 )#1 =#7>#8 {#9
@@ -39,7 +39,7 @@ macro_rules! f {#0
 
 // // +tokenids
 // f!(struct#1 MyTraitMap2#2);
-struct#10 MyTraitMap2#29 {#13
+struct#10 MyTraitMap2#32 {#13
     map#14:#15 ::std#18::collections#21::HashSet#24<#25(#26)#26>#27,#28
 }#13
 "##]],
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index a5763eb41c..34a6900e26 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -463,7 +463,7 @@ fn merge_groups_full() {
 
 #[test]
 fn merge_groups_long_full() {
-    check_crate("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Qux, Baz};")
+    check_crate("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
 }
 
 #[test]
@@ -471,7 +471,7 @@ fn merge_groups_long_last() {
     check_module(
         "std::foo::bar::Baz",
         r"use std::foo::bar::Qux;",
-        r"use std::foo::bar::{Qux, Baz};",
+        r"use std::foo::bar::{Baz, Qux};",
     )
 }
 
@@ -480,7 +480,7 @@ fn merge_groups_long_full_list() {
     check_crate(
         "std::foo::bar::Baz",
         r"use std::foo::bar::{Qux, Quux};",
-        r"use std::foo::bar::{Qux, Quux, Baz};",
+        r"use std::foo::bar::{Baz, Quux, Qux};",
     )
 }
 
@@ -489,7 +489,7 @@ fn merge_groups_long_last_list() {
     check_module(
         "std::foo::bar::Baz",
         r"use std::foo::bar::{Qux, Quux};",
-        r"use std::foo::bar::{Qux, Quux, Baz};",
+        r"use std::foo::bar::{Baz, Quux, Qux};",
     )
 }
 
@@ -498,7 +498,7 @@ fn merge_groups_long_full_nested() {
     check_crate(
         "std::foo::bar::Baz",
         r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-        r"use std::foo::bar::{Qux, quux::{Fez, Fizz}, Baz};",
+        r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
     )
 }
 
@@ -517,7 +517,7 @@ fn merge_groups_full_nested_deep() {
     check_crate(
         "std::foo::bar::quux::Baz",
         r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-        r"use std::foo::bar::{Qux, quux::{Fez, Fizz, Baz}};",
+        r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
     )
 }
 
@@ -526,7 +526,7 @@ fn merge_groups_full_nested_long() {
     check_crate(
         "std::foo::bar::Baz",
         r"use std::{foo::bar::Qux};",
-        r"use std::{foo::bar::{Qux, Baz}};",
+        r"use std::{foo::bar::{Baz, Qux}};",
     );
 }
 
@@ -535,7 +535,7 @@ fn merge_groups_last_nested_long() {
     check_crate(
         "std::foo::bar::Baz",
         r"use std::{foo::bar::Qux};",
-        r"use std::{foo::bar::{Qux, Baz}};",
+        r"use std::{foo::bar::{Baz, Qux}};",
     );
 }
 
@@ -600,7 +600,7 @@ fn merge_mod_into_glob() {
     check_with_config(
         "token::TokenKind",
         r"use token::TokenKind::*;",
-        r"use token::TokenKind::{self, *};",
+        r"use token::TokenKind::{*, self};",
         &InsertUseConfig {
             granularity: ImportGranularity::Crate,
             enforce_granularity: true,
@@ -618,7 +618,7 @@ fn merge_self_glob() {
     check_with_config(
         "self",
         r"use self::*;",
-        r"use self::{self, *};",
+        r"use self::{*, self};",
         &InsertUseConfig {
             granularity: ImportGranularity::Crate,
             enforce_granularity: true,
@@ -637,7 +637,7 @@ fn merge_glob() {
         r"
 use syntax::{SyntaxKind::*};",
         r"
-use syntax::{SyntaxKind::{self, *}};",
+use syntax::{SyntaxKind::{*, self}};",
     )
 }