about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-10-28 16:49:55 +0000
committerGitHub <noreply@github.com>2021-10-28 16:49:55 +0000
commitb025bd6e7d52b6041bb1b4e4058e17037e0bb186 (patch)
treec8c0918c1669ad68e7697d0c7b318f111842aed1
parent166c0ac0de96d44a7823ffa2ec79119b65608ec4 (diff)
parent7fdbdc4ab216a3efa759f78e637626092e8a71be (diff)
downloadrust-b025bd6e7d52b6041bb1b4e4058e17037e0bb186.tar.gz
rust-b025bd6e7d52b6041bb1b4e4058e17037e0bb186.zip
Merge #10656
10656: fix: Enable auto-import and qualify-path in derive attributes r=Veykril a=Veykril

cc https://github.com/rust-analyzer/rust-analyzer/issues/10052

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
-rw-r--r--crates/hir/src/semantics/source_to_def.rs8
-rw-r--r--crates/hir_def/src/child_by_source.rs11
-rw-r--r--crates/hir_def/src/keys.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html1
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs1
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs52
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs83
8 files changed, 123 insertions, 37 deletions
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 20e2481af6c..877d385fbd1 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -299,8 +299,14 @@ impl SourceToDefCtx<'_, '_> {
         dyn_map[keys::CONST_PARAM].get(&src).copied()
     }
 
-    // FIXME: use DynMap as well?
     pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
+        let makro = self.dyn_map(src.as_ref()).and_then(|it| it[keys::MACRO].get(&src).copied());
+        if let res @ Some(_) = makro {
+            return res;
+        }
+
+        // Not all macros are recorded in the dyn map, only the ones behaving like items, so fall back
+        // for the non-item like definitions.
         let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
         let ast_id = AstId::new(src.file_id, file_ast_id.upcast());
         let kind = MacroDefKind::Declarative(ast_id);
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index 6d63570428b..1baf74c512b 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -101,6 +101,17 @@ impl ChildBySource for ModuleId {
 impl ChildBySource for ItemScope {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         self.declarations().for_each(|item| add_module_def(db, file_id, res, item));
+        self.macros().for_each(|(_, makro)| {
+            let ast_id = makro.ast_id();
+            if ast_id.either(|it| it.file_id, |it| it.file_id) == file_id {
+                let src = match ast_id {
+                    Either::Left(ast_id) => ast_id.with_value(ast_id.to_node(db.upcast())),
+                    // FIXME: Do we need to add proc-macros into a PROCMACRO dynmap here?
+                    Either::Right(_fn) => return,
+                };
+                res[keys::MACRO].insert(src, makro);
+            }
+        });
         self.unnamed_consts().for_each(|konst| {
             let src = konst.lookup(db).source(db);
             res[keys::CONST].insert(src, konst);
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index c554540391f..07c4d083d63 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -31,7 +31,7 @@ pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
 pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
 pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
 
-pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
+pub const MACRO: Key<ast::Macro, MacroDefId> = Key::new();
 pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
 pub const DERIVE_MACRO: Key<ast::Attr, Box<[MacroCallId]>> = Key::new();
 
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index b048f46f9cb..0349754fbad 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1907,7 +1907,7 @@ impl ModCollector<'_, '_> {
         let mac = &self.item_tree[id];
         let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
 
-        // Case 1: bulitin macros
+        // Case 1: builtin macros
         let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
         if attrs.by_key("rustc_builtin_macro").exists() {
             let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index d9e1a05a036..9c92bd3e742 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -128,6 +128,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="comment documentation">/// ```</span>
+<span class="comment documentation">/// </span><span class="keyword injected">macro_rules</span><span class="punctuation injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="angle injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
 <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 42d34040faf..05158c169e0 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -707,6 +707,7 @@ pub mod module {
 }
 
 /// ```
+/// macro_rules! noop { ($expr:expr) => { $expr }}
 /// noop!(1);
 /// ```
 macro_rules! noop {
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index c877748246e..a5858869c50 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -3,7 +3,7 @@ use ide_db::helpers::{
     insert_use::{insert_use, ImportScope},
     mod_path_to_ast,
 };
-use syntax::{ast, AstNode, AstToken, SyntaxNode};
+use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxElement};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 
@@ -90,9 +90,18 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         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_with_macros(
+        &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);
@@ -115,23 +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()))
+        ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone().into()))
     } else {
+        // FIXME: Descend?
         let ident = ctx.find_token_at_offset()?;
-        ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(ident.syntax().parent())
+        ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(Some(ident.syntax().clone().into()))
     }
 }
 
@@ -1031,4 +1041,30 @@ fn foo() {
 "#,
         );
     }
+
+    #[test]
+    fn works_in_derives() {
+        check_assist(
+            auto_import,
+            r#"
+//- minicore:derive
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
+}
+#[derive(Copy$0)]
+struct Foo;
+"#,
+            r#"
+use foo::Copy;
+
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
+}
+#[derive(Copy)]
+struct Foo;
+"#,
+        );
+    }
 }
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index 29f2c785bc2..23b642fcb1b 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -9,7 +9,7 @@ use ide_db::RootDatabase;
 use syntax::{
     ast,
     ast::{make, HasArgList},
-    AstNode,
+    AstNode, NodeOrToken,
 };
 
 use crate::{
@@ -42,32 +42,39 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         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 candidate = import_assets.import_candidate();
-    let qualify_candidate = match candidate {
-        ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
-            cov_mark::hit!(qualify_path_qualifier_start);
-            let path = ast::Path::cast(syntax_under_caret)?;
-            let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
-            QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
-        }
-        ImportCandidate::Path(_) => {
-            cov_mark::hit!(qualify_path_unqualified_name);
-            let path = ast::Path::cast(syntax_under_caret)?;
-            let generics = path.segment()?.generic_arg_list();
-            QualifyCandidate::UnqualifiedName(generics)
-        }
-        ImportCandidate::TraitAssocItem(_) => {
-            cov_mark::hit!(qualify_path_trait_assoc_item);
-            let path = ast::Path::cast(syntax_under_caret)?;
-            let (qualifier, segment) = (path.qualifier()?, path.segment()?);
-            QualifyCandidate::TraitAssocItem(qualifier, segment)
-        }
-        ImportCandidate::TraitMethod(_) => {
-            cov_mark::hit!(qualify_path_trait_method);
-            let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
-            QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
-        }
+    let qualify_candidate = match syntax_under_caret {
+        NodeOrToken::Node(syntax_under_caret) => match candidate {
+            ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
+                cov_mark::hit!(qualify_path_qualifier_start);
+                let path = ast::Path::cast(syntax_under_caret)?;
+                let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
+                QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
+            }
+            ImportCandidate::Path(_) => {
+                cov_mark::hit!(qualify_path_unqualified_name);
+                let path = ast::Path::cast(syntax_under_caret)?;
+                let generics = path.segment()?.generic_arg_list();
+                QualifyCandidate::UnqualifiedName(generics)
+            }
+            ImportCandidate::TraitAssocItem(_) => {
+                cov_mark::hit!(qualify_path_trait_assoc_item);
+                let path = ast::Path::cast(syntax_under_caret)?;
+                let (qualifier, segment) = (path.qualifier()?, path.segment()?);
+                QualifyCandidate::TraitAssocItem(qualifier, segment)
+            }
+            ImportCandidate::TraitMethod(_) => {
+                cov_mark::hit!(qualify_path_trait_method);
+                let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
+                QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
+            }
+        },
+        // derive attribute path
+        NodeOrToken::Token(_) => QualifyCandidate::UnqualifiedName(None),
     };
 
     // we aren't interested in different namespaces
@@ -1239,4 +1246,28 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn works_in_derives() {
+        check_assist(
+            qualify_path,
+            r#"
+//- minicore:derive
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
+}
+#[derive(Copy$0)]
+struct Foo;
+"#,
+            r#"
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
+}
+#[derive(foo::Copy)]
+struct Foo;
+"#,
+        );
+    }
 }