Repository: rust-analyzer/rust-analyzer (rust)

Issue:
rename doesn't see occurences inside macros
Doing `Rename Symbol/F2` in VS Code doesn't affect `a` in `println!`.

```
fn main() {
    let a = "test";
    println!("b: {}", a)
}
```

Pull request:
Handle macro token cases for rename
Fixes #2957

Patch:
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/add_import.rs
similarity index 90%
rename from crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
rename to crates/ra_assists/src/handlers/add_import.rs
index b70c88ec29..f03dddac8f 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/add_import.rs
@@ -12,10 +12,10 @@ use crate::{
     AssistId,
 };
 
-/// Creates and inserts a use statement for the given path to import.
-/// The use statement is inserted in the scope most appropriate to the
-/// the cursor position given, additionally merged with the existing use imports.
-pub fn insert_use_statement(
+/// This function produces sequence of text edits into edit
+/// to import the target path in the most appropriate scope given
+/// the cursor position
+pub fn auto_import_text_edit(
     // Ideally the position of the cursor, used to
     position: &SyntaxNode,
     // The statement to use as anchor (last resort)
@@ -37,9 +37,9 @@ pub fn insert_use_statement(
     }
 }
 
-// Assist: replace_qualified_name_with_use
+// Assist: add_import
 //
-// Adds a use statement for a given fully-qualified name.
+// Adds a use statement for a given fully-qualified path.
 //
 // ```
 // fn process(map: std::collections::<|>HashMap<String, String>) {}
@@ -50,7 +50,7 @@ pub fn insert_use_statement(
 //
 // fn process(map: HashMap<String, String>) {}
 // ```
-pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist> {
+pub(crate) fn add_import(ctx: AssistCtx) -> Option<Assist> {
     let path: ast::Path = ctx.find_node_at_offset()?;
     // We don't want to mess with use statements
     if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
@@ -72,13 +72,9 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist>
         }
     };
 
-    ctx.add_assist(
-        AssistId("replace_qualified_name_with_use"),
-        "Replace qualified path with use",
-        |edit| {
-            replace_with_use(&position, &path, &segments, edit.text_edit_builder());
-        },
-    )
+    ctx.add_assist(AssistId("add_import"), format!("Import {}", fmt_segments(&segments)), |edit| {
+        apply_auto_import(&position, &path, &segments, edit.text_edit_builder());
+    })
 }
 
 fn collect_path_segments_raw(
@@ -111,6 +107,12 @@ fn collect_path_segments_raw(
     Some(segments.len() - oldlen)
 }
 
+fn fmt_segments(segments: &[SmolStr]) -> String {
+    let mut buf = String::new();
+    fmt_segments_raw(segments, &mut buf);
+    buf
+}
+
 fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
     let mut iter = segments.iter();
     if let Some(s) = iter.next() {
@@ -556,7 +558,7 @@ fn make_assist_add_nested_import(
     }
 }
 
-fn replace_with_use(
+fn apply_auto_import(
     container: &SyntaxNode,
     path: &ast::Path,
     target: &[SmolStr],
@@ -565,7 +567,7 @@ fn replace_with_use(
     let action = best_action_for_target(container.clone(), path.syntax().clone(), target);
     make_assist(&action, target, edit);
     if let Some(last) = path.segment() {
-        // Here we are assuming the assist will provide a correct use statement
+        // Here we are assuming the assist will provide a  correct use statement
         // so we can delete the path qualifier
         edit.delete(TextRange::from_to(
             path.syntax().text_range().start(),
@@ -601,9 +603,9 @@ mod tests {
     use super::*;
 
     #[test]
-    fn test_replace_add_use_no_anchor() {
+    fn test_auto_import_add_use_no_anchor() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 std::fmt::Debug<|>
     ",
@@ -615,9 +617,9 @@ Debug<|>
         );
     }
     #[test]
-    fn test_replace_add_use_no_anchor_with_item_below() {
+    fn test_auto_import_add_use_no_anchor_with_item_below() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 std::fmt::Debug<|>
 
@@ -636,9 +638,9 @@ fn main() {
     }
 
     #[test]
-    fn test_replace_add_use_no_anchor_with_item_above() {
+    fn test_auto_import_add_use_no_anchor_with_item_above() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 fn main() {
 }
@@ -657,9 +659,9 @@ Debug<|>
     }
 
     #[test]
-    fn test_replace_add_use_no_anchor_2seg() {
+    fn test_auto_import_add_use_no_anchor_2seg() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 std::fmt<|>::Debug
     ",
@@ -672,9 +674,9 @@ fmt<|>::Debug
     }
 
     #[test]
-    fn test_replace_add_use() {
+    fn test_auto_import_add_use() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use stdx;
 
@@ -692,9 +694,9 @@ impl Debug<|> for Foo {
     }
 
     #[test]
-    fn test_replace_file_use_other_anchor() {
+    fn test_auto_import_file_use_other_anchor() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 impl std::fmt::Debug<|> for Foo {
 }
@@ -709,9 +711,9 @@ impl Debug<|> for Foo {
     }
 
     #[test]
-    fn test_replace_add_use_other_anchor_indent() {
+    fn test_auto_import_add_use_other_anchor_indent() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
     impl std::fmt::Debug<|> for Foo {
     }
@@ -726,9 +728,9 @@ impl Debug<|> for Foo {
     }
 
     #[test]
-    fn test_replace_split_different() {
+    fn test_auto_import_split_different() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt;
 
@@ -745,9 +747,9 @@ impl io<|> for Foo {
     }
 
     #[test]
-    fn test_replace_split_self_for_use() {
+    fn test_auto_import_split_self_for_use() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt;
 
@@ -764,9 +766,9 @@ impl Debug<|> for Foo {
     }
 
     #[test]
-    fn test_replace_split_self_for_target() {
+    fn test_auto_import_split_self_for_target() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt::Debug;
 
@@ -783,9 +785,9 @@ impl fmt<|> for Foo {
     }
 
     #[test]
-    fn test_replace_add_to_nested_self_nested() {
+    fn test_auto_import_add_to_nested_self_nested() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt::{Debug, nested::{Display}};
 
@@ -802,9 +804,9 @@ impl nested<|> for Foo {
     }
 
     #[test]
-    fn test_replace_add_to_nested_self_already_included() {
+    fn test_auto_import_add_to_nested_self_already_included() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt::{Debug, nested::{self, Display}};
 
@@ -821,9 +823,9 @@ impl nested<|> for Foo {
     }
 
     #[test]
-    fn test_replace_add_to_nested_nested() {
+    fn test_auto_import_add_to_nested_nested() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt::{Debug, nested::{Display}};
 
@@ -840,9 +842,9 @@ impl Debug<|> for Foo {
     }
 
     #[test]
-    fn test_replace_split_common_target_longer() {
+    fn test_auto_import_split_common_target_longer() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt::Debug;
 
@@ -859,9 +861,9 @@ impl Display<|> for Foo {
     }
 
     #[test]
-    fn test_replace_split_common_use_longer() {
+    fn test_auto_import_split_common_use_longer() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt::nested::Debug;
 
@@ -878,9 +880,9 @@ impl Display<|> for Foo {
     }
 
     #[test]
-    fn test_replace_use_nested_import() {
+    fn test_auto_import_use_nested_import() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use crate::{
     ty::{Substs, Ty},
@@ -901,9 +903,9 @@ fn foo() { lower<|>::trait_env() }
     }
 
     #[test]
-    fn test_replace_alias() {
+    fn test_auto_import_alias() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt as foo;
 
@@ -920,9 +922,9 @@ impl Debug<|> for Foo {
     }
 
     #[test]
-    fn test_replace_not_applicable_one_segment() {
+    fn test_auto_import_not_applicable_one_segment() {
         check_assist_not_applicable(
-            replace_qualified_name_with_use,
+            add_import,
             "
 impl foo<|> for Foo {
 }
@@ -931,9 +933,9 @@ impl foo<|> for Foo {
     }
 
     #[test]
-    fn test_replace_not_applicable_in_use() {
+    fn test_auto_import_not_applicable_in_use() {
         check_assist_not_applicable(
-            replace_qualified_name_with_use,
+            add_import,
             "
 use std::fmt<|>;
 ",
@@ -941,9 +943,9 @@ use std::fmt<|>;
     }
 
     #[test]
-    fn test_replace_add_use_no_anchor_in_mod_mod() {
+    fn test_auto_import_add_use_no_anchor_in_mod_mod() {
         check_assist(
-            replace_qualified_name_with_use,
+            add_import,
             "
 mod foo {
     mod bar {
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 4514b86911..84b5474f9d 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -7,7 +7,7 @@ use ra_syntax::{
 
 use crate::{
     assist_ctx::{ActionBuilder, Assist, AssistCtx},
-    insert_use_statement, AssistId,
+    auto_import_text_edit, AssistId,
 };
 use std::collections::BTreeSet;
 
@@ -78,7 +78,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
 fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
     let mut action_builder = ActionBuilder::default();
     action_builder.label(format!("Import `{}`", &import));
-    insert_use_statement(position, anchor, &import, action_builder.text_edit_builder());
+    auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder());
     action_builder
 }
 
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index f79189ae80..eca6dec4b0 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -21,7 +21,7 @@ use ra_syntax::{TextRange, TextUnit};
 use ra_text_edit::TextEdit;
 
 pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler};
-pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement;
+pub use crate::handlers::add_import::auto_import_text_edit;
 
 /// Unique identifier of the assist, should not be shown to the user
 /// directly.
@@ -133,7 +133,7 @@ mod handlers {
     mod replace_if_let_with_match;
     mod split_import;
     mod remove_dbg;
-    pub(crate) mod replace_qualified_name_with_use;
+    pub(crate) mod add_import;
     mod add_missing_impl_members;
     mod move_guard;
     mod move_bounds;
@@ -158,7 +158,7 @@ mod handlers {
             replace_if_let_with_match::replace_if_let_with_match,
             split_import::split_import,
             remove_dbg::remove_dbg,
-            replace_qualified_name_with_use::replace_qualified_name_with_use,
+            add_import::add_import,
             add_missing_impl_members::add_missing_impl_members,
             add_missing_impl_members::add_missing_default_members,
             inline_local_variable::inline_local_variable,
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
index e2ee86dd15..64b04ec2b6 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -1,6 +1,12 @@
 //! FIXME: write short doc here
 
-use crate::completion::{CompletionContext, Completions};
+use ra_assists::auto_import_text_edit;
+use ra_syntax::{ast, AstNode, SmolStr};
+use ra_text_edit::TextEditBuilder;
+use rustc_hash::FxHashMap;
+
+use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
+use hir::{ModPath, PathKind};
 
 pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
     if !ctx.is_trivial_path {
@@ -10,13 +16,137 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
     ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
         acc.add_resolution(ctx, name.to_string(), &res)
     });
+
+    // auto-import
+    // We fetch ident from the original file, because we need to pre-filter auto-imports
+    if ast::NameRef::cast(ctx.token.parent()).is_some() {
+        let import_resolver = ImportResolver::new();
+        let import_names = import_resolver.all_names(ctx.token.text());
+        import_names.into_iter().for_each(|(name, path)| {
+            let edit = {
+                let mut builder = TextEditBuilder::default();
+                builder.replace(ctx.source_range(), name.to_string());
+                auto_import_text_edit(
+                    &ctx.token.parent(),
+                    &ctx.token.parent(),
+                    &path,
+                    &mut builder,
+                );
+                builder.finish()
+            };
+
+            // Hack: copied this check form conv.rs beacause auto import can produce edits
+            // that invalidate assert in conv_with.
+            if edit
+                .as_atoms()
+                .iter()
+                .filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
+                .all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
+            {
+                CompletionItem::new(
+                    CompletionKind::Reference,
+                    ctx.source_range(),
+                    build_import_label(&name, &path),
+                )
+                .text_edit(edit)
+                .add_to(acc);
+            }
+        });
+    }
+}
+
+fn build_import_label(name: &str, path: &ModPath) -> String {
+    let mut buf = String::with_capacity(64);
+    buf.push_str(name);
+    buf.push_str(" (");
+    buf.push_str(&path.to_string());
+    buf.push_str(")");
+    buf
+}
+
+#[derive(Debug, Clone, Default)]
+pub(crate) struct ImportResolver {
+    // todo: use fst crate or something like that
+    dummy_names: Vec<(SmolStr, ModPath)>,
+}
+
+impl ImportResolver {
+    pub(crate) fn new() -> Self {
+        use hir::name;
+
+        let dummy_names = vec![
+            (
+                SmolStr::new("fmt"),
+                ModPath { kind: PathKind::Plain, segments: vec![name![std], name![fmt]] },
+            ),
+            (
+                SmolStr::new("io"),
+                ModPath { kind: PathKind::Plain, segments: vec![name![std], name![io]] },
+            ),
+            (
+                SmolStr::new("iter"),
+                ModPath { kind: PathKind::Plain, segments: vec![name![std], name![iter]] },
+            ),
+            (
+                SmolStr::new("hash"),
+                ModPath { kind: PathKind::Plain, segments: vec![name![std], name![hash]] },
+            ),
+            (
+                SmolStr::new("Debug"),
+                ModPath {
+                    kind: PathKind::Plain,
+                    segments: vec![name![std], name![fmt], name![Debug]],
+                },
+            ),
+            (
+                SmolStr::new("Display"),
+                ModPath {
+                    kind: PathKind::Plain,
+                    segments: vec![name![std], name![fmt], name![Display]],
+                },
+            ),
+            (
+                SmolStr::new("Hash"),
+                ModPath {
+                    kind: PathKind::Plain,
+                    segments: vec![name![std], name![hash], name![Hash]],
+                },
+            ),
+            (
+                SmolStr::new("Hasher"),
+                ModPath {
+                    kind: PathKind::Plain,
+                    segments: vec![name![std], name![hash], name![Hasher]],
+                },
+            ),
+            (
+                SmolStr::new("Iterator"),
+                ModPath {
+                    kind: PathKind::Plain,
+                    segments: vec![name![std], name![iter], name![Iterator]],
+                },
+            ),
+        ];
+
+        ImportResolver { dummy_names }
+    }
+
+    // Returns a map of importable items filtered by name.
+    // The map associates item name with its full path.
+    // todo: should return Resolutions
+    pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, ModPath> {
+        if name.len() > 1 {
+            self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
+        } else {
+            FxHashMap::default()
+        }
+    }
 }
 
 #[cfg(test)]
 mod tests {
-    use insta::assert_debug_snapshot;
-
     use crate::completion::{do_completion, CompletionItem, CompletionKind};
+    use insta::assert_debug_snapshot;
 
     fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
         do_completion(code, CompletionKind::Reference)
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index a6320bd2f5..de924fad27 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -13,6 +13,7 @@ mod classify;
 mod rename;
 mod search_scope;
 
+use crate::expand::descend_into_macros_with_analyzer;
 use hir::{InFile, SourceBinder};
 use once_cell::unsync::Lazy;
 use ra_db::{SourceDatabase, SourceDatabaseExt};
@@ -192,39 +193,62 @@ fn process_definition(
 
         let parse = Lazy::new(|| SourceFile::parse(&text));
         let mut sb = Lazy::new(|| SourceBinder::new(db));
+        let mut analyzer = None;
 
         for (idx, _) in text.match_indices(pat) {
             let offset = TextUnit::from_usize(idx);
 
-            if let Some(name_ref) =
+            let (name_ref, range) = if let Some(name_ref) =
                 find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset)
             {
                 let range = name_ref.syntax().text_range();
-                if let Some(search_range) = search_range {
-                    if !range.is_subrange(&search_range) {
-                        continue;
-                    }
+                (InFile::new(file_id.into(), name_ref), range)
+            } else {
+                // Handle macro token cases
+                let t = match parse.tree().syntax().token_at_offset(offset) {
+                    TokenAtOffset::None => continue,
+                    TokenAtOffset::Single(t) => t,
+                    TokenAtOffset::Between(_, t) => t,
+                };
+                let range = t.text_range();
+                let analyzer = analyzer.get_or_insert_with(|| {
+                    sb.analyze(InFile::new(file_id.into(), parse.tree().syntax()), None)
+                });
+                let expanded = descend_into_macros_with_analyzer(
+                    db,
+                    &analyzer,
+                    InFile::new(file_id.into(), t),
+                );
+                if let Some(token) = ast::NameRef::cast(expanded.value.parent()) {
+                    (expanded.with_value(token), range)
+                } else {
+                    continue;
                 }
-                // FIXME: reuse sb
-                // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
-
-                if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref))
-                {
-                    if d == def {
-                        let kind = if is_record_lit_name_ref(&name_ref)
-                            || is_call_expr_name_ref(&name_ref)
-                        {
-                            ReferenceKind::StructLiteral
-                        } else {
-                            ReferenceKind::Other
-                        };
-
-                        refs.push(Reference {
-                            file_range: FileRange { file_id, range },
-                            kind,
-                            access: reference_access(&d.kind, &name_ref),
-                        });
-                    }
+            };
+
+            if let Some(search_range) = search_range {
+                if !range.is_subrange(&search_range) {
+                    continue;
+                }
+            }
+            // FIXME: reuse sb
+            // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
+
+            if let Some(d) = classify_name_ref(&mut sb, name_ref.as_ref()) {
+                if d == def {
+                    let kind = if is_record_lit_name_ref(&name_ref.value)
+                        || is_call_expr_name_ref(&name_ref.value)
+                    {
+                        ReferenceKind::StructLiteral
+                    } else {
+                        ReferenceKind::Other
+                    };
+
+                    refs.push(Reference {
+                        file_range: FileRange { file_id, range },
+                        kind,
+                        access: reference_access(&d.kind, &name_ref.value),
+                    });
                 }
             }
         }
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 08e77c01fb..c46b78cb6f 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -210,6 +210,25 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_rename_for_macro_args() {
+        test_rename(
+            r#"
+    macro_rules! foo {($i:ident) => {$i} }
+    fn main() {
+        let a<|> = "test";
+        foo!(a);
+    }"#,
+            "b",
+            r#"
+    macro_rules! foo {($i:ident) => {$i} }
+    fn main() {
+        let b = "test";
+        foo!(b);
+    }"#,
+        );
+    }
+
     #[test]
     fn test_rename_for_param_inside() {
         test_rename(
diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs
index e27b27ffab..4b301d67af 100644
--- a/crates/ra_parser/src/syntax_kind/generated.rs
+++ b/crates/ra_parser/src/syntax_kind/generated.rs
@@ -1,4 +1,4 @@
-//! Generated file, do not edit by hand, see `xtask/src/codegen`
+//! Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`
 
 #![allow(bad_style, missing_docs, unreachable_pub)]
 #[doc = r" The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT_DEF`."]
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 435135f92d..33d5578e70 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -1,4 +1,4 @@
-//! Generated file, do not edit by hand, see `xtask/src/codegen`
+//! Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`
 
 use crate::{
     ast::{self, AstChildren, AstNode},
diff --git a/docs/user/assists.md b/docs/user/assists.md
index f737a2fa49..1e2dd74858 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -154,6 +154,20 @@ impl Trait<u32> for () {
 }
 ```
 
+## `add_import`
+
+Adds a use statement for a given fully-qualified path.
+
+```rust
+// BEFORE
+fn process(map: std::collections::┃HashMap<String, String>) {}
+
+// AFTER
+use std::collections::HashMap;
+
+fn process(map: HashMap<String, String>) {}
+```
+
 ## `add_new`
 
 Adds a new inherent impl for a type.
@@ -554,20 +568,6 @@ fn handle(action: Action) {
 }
 ```
 
-## `replace_qualified_name_with_use`
-
-Adds a use statement for a given fully-qualified name.
-
-```rust
-// BEFORE
-fn process(map: std::collections::┃HashMap<String, String>) {}
-
-// AFTER
-use std::collections::HashMap;
-
-fn process(map: HashMap<String, String>) {}
-```
-
 ## `split_import`
 
 Wraps the tail of import into braces.
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index 8fdf43e4a8..9b0afe8e0c 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -53,7 +53,8 @@ fn reformat(text: impl std::fmt::Display) -> Result<String> {
     write!(rustfmt.stdin.take().unwrap(), "{}", text)?;
     let output = rustfmt.wait_with_output()?;
     let stdout = String::from_utf8(output.stdout)?;
-    let preamble = "Generated file, do not edit by hand, see `xtask/src/codegen`";
+    // TODO: update the preable: replace ra_tools with the relevant path
+    let preamble = "Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`";
     Ok(format!("//! {}\n\n{}", preamble, stdout))
 }
 


Test patch:
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 4ab09b167b..0d95b957ba 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -1,4 +1,4 @@
-//! Generated file, do not edit by hand, see `xtask/src/codegen`
+//! Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`
 
 use super::check;
 
@@ -160,6 +160,21 @@ impl Trait<u32> for () {
     )
 }
 
+#[test]
+fn doctest_add_import() {
+    check(
+        "add_import",
+        r#####"
+fn process(map: std::collections::<|>HashMap<String, String>) {}
+"#####,
+        r#####"
+use std::collections::HashMap;
+
+fn process(map: HashMap<String, String>) {}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_add_new() {
     check(
@@ -576,21 +591,6 @@ fn handle(action: Action) {
     )
 }
 
-#[test]
-fn doctest_replace_qualified_name_with_use() {
-    check(
-        "replace_qualified_name_with_use",
-        r#####"
-fn process(map: std::collections::<|>HashMap<String, String>) {}
-"#####,
-        r#####"
-use std::collections::HashMap;
-
-fn process(map: HashMap<String, String>) {}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_split_import() {
     check(