diff --git a/Cargo.lock b/Cargo.lock index 6dab239d38..eff2545268 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 20a4b111e3..5ba8255ddd 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 44d3b7ca5b..84e295e014 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, 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 17b901d5f8..9c6531da20 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 { 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 ef8dc3e3b0..81f58fa2ce 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 37c57ea63d..d0b8248a34 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, edition: Edition, @@ -279,7 +279,9 @@ impl DefMap { pub fn modules(&self) -> impl Iterator + '_ { self.modules.iter() } - + pub fn exported_proc_macros(&self) -> impl Iterator + '_ { + 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 5022642b0c..0225ab425f 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 7f050f25c9..5c08d12ef2 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 486cbff685..a5c134714b 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 { + 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 { @@ -205,7 +203,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option usize { diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index d555eff878..463744f22a 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> { 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 9f86fc50a2..45979d4828 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 {