]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_assists/src/handlers/auto_import.rs
Merge #11481
[rust.git] / crates / ide_assists / src / handlers / auto_import.rs
index 8df8b060d97b6894719433ddb41d2bcd4696467b..cac736ff850a0f8fe41fbd11b355fdde577c4885 100644 (file)
@@ -3,7 +3,7 @@
     insert_use::{insert_use, ImportScope},
     mod_path_to_ast,
 };
-use syntax::{ast, AstNode, SyntaxNode};
+use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxElement};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 
 // ```
 pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
-    let proposed_imports =
+    let mut proposed_imports =
         import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
     if proposed_imports.is_empty() {
         return None;
     }
 
-    let range = ctx.sema.original_range(&syntax_under_caret).range;
+    let range = match &syntax_under_caret {
+        NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
+        NodeOrToken::Token(token) => token.text_range(),
+    };
     let group_label = group_label(import_assets.import_candidate());
-    let scope = ImportScope::find_insert_use_container_with_macros(&syntax_under_caret, &ctx.sema)?;
+    let scope = ImportScope::find_insert_use_container(
+        &match syntax_under_caret {
+            NodeOrToken::Node(it) => it,
+            NodeOrToken::Token(it) => it.parent()?,
+        },
+        &ctx.sema,
+    )?;
+
+    // we aren't interested in different namespaces
+    proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
     for import in proposed_imports {
         acc.add_group(
             &group_label,
@@ -112,17 +124,24 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     Some(())
 }
 
-pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
+pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxElement)> {
     if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
         ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
-            .zip(Some(path_under_caret.syntax().clone()))
+            .zip(Some(path_under_caret.syntax().clone().into()))
     } else if let Some(method_under_caret) =
         ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
     {
         ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
-            .zip(Some(method_under_caret.syntax().clone()))
+            .zip(Some(method_under_caret.syntax().clone().into()))
+    } else if let Some(pat) = ctx
+        .find_node_at_offset_with_descend::<ast::IdentPat>()
+        .filter(ast::IdentPat::is_simple_ident)
+    {
+        ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone().into()))
     } else {
-        None
+        // FIXME: Descend?
+        let ident = ctx.find_token_at_offset()?;
+        ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(Some(ident.syntax().clone().into()))
     }
 }
 
@@ -142,8 +161,63 @@ fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
 #[cfg(test)]
 mod tests {
     use super::*;
+
     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 
+    #[test]
+    fn not_applicable_if_scope_inside_macro() {
+        check_assist_not_applicable(
+            auto_import,
+            r"
+mod bar {
+    pub struct Baz;
+}
+macro_rules! foo {
+    ($it:ident) => {
+        mod __ {
+            fn __(x: $it) {}
+        }
+    };
+}
+foo! {
+    Baz$0
+}
+",
+        );
+    }
+
+    #[test]
+    fn applicable_in_attributes() {
+        check_assist(
+            auto_import,
+            r"
+//- proc_macros: identity
+#[proc_macros::identity]
+mod foo {
+    mod bar {
+        const _: Baz$0 = ();
+    }
+}
+mod baz {
+    pub struct Baz;
+}
+",
+            r"
+#[proc_macros::identity]
+mod foo {
+    mod bar {
+        use crate::baz::Baz;
+
+        const _: Baz = ();
+    }
+}
+mod baz {
+    pub struct Baz;
+}
+",
+        );
+    }
+
     #[test]
     fn applicable_when_found_an_import_partial() {
         check_assist(
@@ -972,6 +1046,7 @@ fn bar() {
 
     #[test]
     fn uses_abs_path_with_extern_crate_clash() {
+        cov_mark::check!(ambiguous_crate_start);
         check_assist(
             auto_import,
             r#"
@@ -997,59 +1072,52 @@ mod foo {}
     }
 
     #[test]
-    fn respects_cfg_attr() {
+    fn works_on_ident_patterns() {
         check_assist(
             auto_import,
             r#"
-mod bar {
-    pub struct Bar;
+mod foo {
+    pub struct Foo {}
 }
-
-#[cfg(test)]
 fn foo() {
-    Bar$0
+    let Foo$0;
 }
 "#,
             r#"
-mod bar {
-    pub struct Bar;
-}
+use foo::Foo;
 
-#[cfg(test)]
+mod foo {
+    pub struct Foo {}
+}
 fn foo() {
-use bar::Bar;
-
-    Bar
+    let Foo;
 }
 "#,
         );
     }
 
     #[test]
-    fn respects_cfg_attr2() {
+    fn works_in_derives() {
         check_assist(
             auto_import,
             r#"
-mod bar {
-    pub struct Bar;
-}
-
-#[cfg(test)]
-const FOO: Bar = {
-    Bar$0
+//- minicore:derive
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
 }
+#[derive(Copy$0)]
+struct Foo;
 "#,
             r#"
-mod bar {
-    pub struct Bar;
-}
-
-#[cfg(test)]
-const FOO: Bar = {
-use bar::Bar;
+use foo::Copy;
 
-    Bar
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
 }
+#[derive(Copy)]
+struct Foo;
 "#,
         );
     }