about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2021-10-28 19:19:30 +0200
committerLukas Wirth <lukastw97@gmail.com>2021-10-29 14:51:26 +0200
commitebd63ec1cf7f04460481adefa771ddf291cc9ab2 (patch)
treee4c86995e9fdc5780625fb8db33a38ba3bc6a38f
parent7fdbdc4ab216a3efa759f78e637626092e8a71be (diff)
downloadrust-ebd63ec1cf7f04460481adefa771ddf291cc9ab2.tar.gz
rust-ebd63ec1cf7f04460481adefa771ddf291cc9ab2.zip
feat: Make unqualified derive attributes flyimportable
-rw-r--r--Cargo.lock1
-rw-r--r--crates/base_db/Cargo.toml1
-rw-r--r--crates/base_db/src/fixture.rs6
-rw-r--r--crates/hir/src/lib.rs7
-rw-r--r--crates/hir_def/src/macro_expansion_tests/proc_macros.rs4
-rw-r--r--crates/hir_def/src/nameres.rs6
-rw-r--r--crates/hir_expand/src/name.rs4
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs53
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs32
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/tests/attribute.rs39
11 files changed, 124 insertions, 31 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6dab239d388..eff2545268d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -99,6 +99,7 @@ dependencies = [
  "profile",
  "rustc-hash",
  "salsa",
+ "stdx",
  "syntax",
  "test_utils",
  "tt",
diff --git a/crates/base_db/Cargo.toml b/crates/base_db/Cargo.toml
index 20a4b111e3c..5ba8255ddd8 100644
--- a/crates/base_db/Cargo.toml
+++ b/crates/base_db/Cargo.toml
@@ -14,6 +14,7 @@ salsa = "0.17.0-pre.2"
 rustc-hash = "1.1.0"
 
 syntax = { path = "../syntax", version = "0.0.0" }
+stdx = { path = "../stdx", version = "0.0.0" }
 cfg = { path = "../cfg", version = "0.0.0" }
 profile = { path = "../profile", version = "0.0.0" }
 tt = { path = "../tt", version = "0.0.0" }
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 44d3b7ca5bf..84e295e0146 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -270,7 +270,7 @@ fn test_proc_macros(proc_macros: &[String]) -> (Vec<ProcMacro>, String) {
 pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
     item
 }
-#[proc_macro_derive(derive_identity)]
+#[proc_macro_derive(DeriveIdentity)]
 pub fn derive_identity(item: TokenStream) -> TokenStream {
     item
 }
@@ -290,7 +290,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
             expander: Arc::new(IdentityProcMacroExpander),
         },
         ProcMacro {
-            name: "derive_identity".into(),
+            name: "DeriveIdentity".into(),
             kind: crate::ProcMacroKind::CustomDerive,
             expander: Arc::new(IdentityProcMacroExpander),
         },
@@ -306,7 +306,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
         },
     ]
     .into_iter()
-    .filter(|pm| proc_macros.iter().any(|name| name == pm.name))
+    .filter(|pm| proc_macros.iter().any(|name| name == &stdx::to_lower_snake_case(&pm.name)))
     .collect();
     (proc_macros, source.into())
 }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 17b901d5f89..9c6531da204 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1623,7 +1623,12 @@ impl MacroDef {
     pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
         match self.source(db)?.value {
             Either::Left(it) => it.name().map(|it| it.as_name()),
-            Either::Right(it) => it.name().map(|it| it.as_name()),
+            Either::Right(_) => {
+                let krate = self.id.krate;
+                let def_map = db.crate_def_map(krate);
+                let (_, name) = def_map.exported_proc_macros().find(|&(id, _)| id == self.id)?;
+                Some(name)
+            }
         }
     }
 
diff --git a/crates/hir_def/src/macro_expansion_tests/proc_macros.rs b/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
index ef8dc3e3b04..81f58fa2cef 100644
--- a/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
@@ -33,7 +33,7 @@ fn derive_censoring() {
 //- proc_macros: derive_identity
 #[attr1]
 #[derive(Foo)]
-#[derive(proc_macros::derive_identity)]
+#[derive(proc_macros::DeriveIdentity)]
 #[derive(Bar)]
 #[attr2]
 struct S;
@@ -41,7 +41,7 @@ struct S;
         expect![[r##"
 #[attr1]
 #[derive(Foo)]
-#[derive(proc_macros::derive_identity)]
+#[derive(proc_macros::DeriveIdentity)]
 #[derive(Bar)]
 #[attr2]
 struct S;
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 37c57ea63d3..d0b8248a342 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -103,7 +103,7 @@ pub struct DefMap {
     /// Side table with additional proc. macro info, for use by name resolution in downstream
     /// crates.
     ///
-    /// (the primary purpose is to resolve derive helpers)
+    /// (the primary purpose is to resolve derive helpers and fetch a proc-macros name)
     exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
 
     edition: Edition,
@@ -279,7 +279,9 @@ impl DefMap {
     pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
         self.modules.iter()
     }
-
+    pub fn exported_proc_macros(&self) -> impl Iterator<Item = (MacroDefId, Name)> + '_ {
+        self.exported_proc_macros.iter().map(|(id, def)| (*id, def.name.clone()))
+    }
     pub fn root(&self) -> LocalModuleId {
         self.root
     }
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 5022642b0c7..0225ab425fc 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -63,8 +63,8 @@ impl Name {
     /// Ideally, we want a `gensym` semantics for missing names -- each missing
     /// name is equal only to itself. It's not clear how to implement this in
     /// salsa though, so we punt on that bit for a moment.
-    pub fn missing() -> Name {
-        Name::new_text("[missing name]".into())
+    pub const fn missing() -> Name {
+        Name::new_inline("[missing name]")
     }
 
     /// Returns the tuple index this name represents if it is a tuple field.
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index 7f050f25c9b..5c08d12ef24 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -1,14 +1,15 @@
 //! Completion for derives
 use hir::{HasAttrs, MacroDef, MacroKind};
-use ide_db::helpers::FamousDefs;
+use ide_db::helpers::{import_assets::ImportAssets, insert_use::ImportScope, FamousDefs};
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
-use syntax::ast;
+use syntax::{ast, SyntaxKind};
 
 use crate::{
+    completions::flyimport::compute_fuzzy_completion_order_key,
     context::CompletionContext,
     item::{CompletionItem, CompletionItemKind},
-    Completions,
+    Completions, ImportEdit,
 };
 
 pub(super) fn complete_derive(
@@ -66,6 +67,8 @@ pub(super) fn complete_derive(
         }
         item.add_to(acc);
     }
+
+    flyimport_attribute(ctx, acc);
 }
 
 fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> {
@@ -80,6 +83,50 @@ fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> {
     result
 }
 
+fn flyimport_attribute(ctx: &CompletionContext, acc: &mut Completions) -> Option<()> {
+    if ctx.token.kind() != SyntaxKind::IDENT {
+        return None;
+    };
+    let potential_import_name = ctx.token.to_string();
+    let module = ctx.scope.module()?;
+    let parent = ctx.token.parent()?;
+    let user_input_lowercased = potential_import_name.to_lowercase();
+    let import_assets = ImportAssets::for_fuzzy_path(
+        module,
+        None,
+        potential_import_name,
+        &ctx.sema,
+        parent.clone(),
+    )?;
+    let import_scope = ImportScope::find_insert_use_container_with_macros(&parent, &ctx.sema)?;
+    acc.add_all(
+        import_assets
+            .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
+            .into_iter()
+            .filter_map(|import| match import.original_item {
+                hir::ItemInNs::Macros(mac) => Some((import, mac)),
+                _ => None,
+            })
+            .filter(|&(_, mac)| !ctx.is_item_hidden(&hir::ItemInNs::Macros(mac)))
+            .sorted_by_key(|(import, _)| {
+                compute_fuzzy_completion_order_key(&import.import_path, &user_input_lowercased)
+            })
+            .filter_map(|(import, mac)| {
+                let mut item = CompletionItem::new(
+                    CompletionItemKind::Attribute,
+                    ctx.source_range(),
+                    mac.name(ctx.db)?.to_string(),
+                );
+                item.add_import(ImportEdit { import, scope: import_scope.clone() });
+                if let Some(docs) = mac.docs(ctx.db) {
+                    item.documentation(docs);
+                }
+                Some(item.build())
+            }),
+    );
+    Some(())
+}
+
 struct DeriveDependencies {
     label: &'static str,
     dependencies: &'static [&'static str],
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 486cbff6859..a5c134714ba 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -125,12 +125,12 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
         }
     };
 
-    let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
+    let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.clone());
 
     let user_input_lowercased = potential_import_name.to_lowercase();
     let import_assets = import_assets(ctx, potential_import_name)?;
     let import_scope = ImportScope::find_insert_use_container_with_macros(
-        position_for_import(ctx, Some(import_assets.import_candidate()))?,
+        &position_for_import(ctx, Some(import_assets.import_candidate()))?,
         &ctx.sema,
     )?;
 
@@ -158,21 +158,19 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
     Some(())
 }
 
-pub(crate) fn position_for_import<'a>(
-    ctx: &'a CompletionContext,
+pub(crate) fn position_for_import(
+    ctx: &CompletionContext,
     import_candidate: Option<&ImportCandidate>,
-) -> Option<&'a SyntaxNode> {
-    Some(match import_candidate {
-        Some(ImportCandidate::Path(_)) => ctx.name_syntax.as_ref()?.syntax(),
-        Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
-        Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
-        None => ctx
-            .name_syntax
-            .as_ref()
-            .map(|name_ref| name_ref.syntax())
-            .or_else(|| ctx.path_qual().map(|path| path.syntax()))
-            .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
-    })
+) -> Option<SyntaxNode> {
+    Some(
+        match import_candidate {
+            Some(ImportCandidate::Path(_)) => ctx.name_syntax.as_ref()?.syntax(),
+            Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
+            Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
+            None => return ctx.original_token.parent(),
+        }
+        .clone(),
+    )
 }
 
 fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
@@ -205,7 +203,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
     }
 }
 
-fn compute_fuzzy_completion_order_key(
+pub(crate) fn compute_fuzzy_completion_order_key(
     proposed_mod_path: &hir::ModPath,
     user_input_lowercased: &str,
 ) -> usize {
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index d555eff878c..463744f22af 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -182,7 +182,7 @@ pub fn resolve_completion_edits(
 ) -> Option<Vec<TextEdit>> {
     let _p = profile::span("resolve_completion_edits");
     let ctx = CompletionContext::new(db, position, config)?;
-    let position_for_import = position_for_import(&ctx, None)?;
+    let position_for_import = &position_for_import(&ctx, None)?;
     let scope = ImportScope::find_insert_use_container_with_macros(position_for_import, &ctx.sema)?;
 
     let current_module = ctx.sema.scope(position_for_import).module()?;
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index 9f86fc50a22..45979d4828c 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -640,6 +640,45 @@ mod derive {
             "#]],
         )
     }
+
+    #[test]
+    fn derive_flyimport() {
+        check_derive(
+            r#"
+//- proc_macros: derive_identity
+#[derive(der$0)] struct Test;
+"#,
+            expect![[r#"
+                at DeriveIdentity (use proc_macros::DeriveIdentity)
+            "#]],
+        );
+        check_derive(
+            r#"
+//- proc_macros: derive_identity
+use proc_macros::DeriveIdentity;
+#[derive(der$0)] struct Test;
+"#,
+            expect![[r#"
+                at DeriveIdentity
+            "#]],
+        );
+    }
+
+    #[test]
+    fn derive_flyimport_edit() {
+        check_edit(
+            "DeriveIdentity",
+            r#"
+//- proc_macros: derive_identity
+#[derive(der$0)] struct Test;
+"#,
+            r#"
+use proc_macros::DeriveIdentity;
+
+#[derive(DeriveIdentity)] struct Test;
+"#,
+        );
+    }
 }
 
 mod lint {