diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 466f0b1fb7f9..9155caa2e0b8 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -26,6 +26,7 @@ mod cfg; mod derive; mod lint; mod repr; +mod macro_use; pub(crate) use self::derive::complete_derive_path; @@ -35,6 +36,7 @@ pub(crate) fn complete_known_attribute_input( ctx: &CompletionContext<'_>, &colon_prefix: &bool, fake_attribute_under_caret: &ast::Attr, + extern_crate: Option<&ast::ExternCrate>, ) -> Option<()> { let attribute = fake_attribute_under_caret; let name_ref = match attribute.path() { @@ -66,6 +68,9 @@ pub(crate) fn complete_known_attribute_input( lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints); } "cfg" => cfg::complete_cfg(acc, ctx), + "macro_use" => { + macro_use::complete_macro_use(acc, ctx, extern_crate, &parse_tt_as_comma_sep_paths(tt)?) + } _ => (), } Some(()) diff --git a/crates/ide-completion/src/completions/attribute/macro_use.rs b/crates/ide-completion/src/completions/attribute/macro_use.rs new file mode 100644 index 000000000000..806c9f64c0da --- /dev/null +++ b/crates/ide-completion/src/completions/attribute/macro_use.rs @@ -0,0 +1,34 @@ +use hir::ModuleDef; +use ide_db::SymbolKind; +use syntax::ast; + +use crate::{context::CompletionContext, item::CompletionItem, Completions}; + +pub(super) fn complete_macro_use( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + extern_crate: Option<&ast::ExternCrate>, + existing_imports: &[ast::Path], +) { + let Some(extern_crate) = extern_crate else { return }; + let Some(extern_crate) = ctx.sema.to_def(extern_crate) else { return }; + let Some(krate) = extern_crate.resolved_crate(ctx.db) else { return }; + + for mod_def in krate.root_module().declarations(ctx.db) { + if let ModuleDef::Macro(mac) = mod_def { + let mac_name = mac.name(ctx.db); + let Some(mac_name) = mac_name.as_str() else { continue }; + + let existing_import = existing_imports + .iter() + .filter_map(|p| p.as_single_name_ref()) + .find(|n| n.text() == mac_name); + if existing_import.is_some() { + continue; + } + + let item = CompletionItem::new(SymbolKind::Macro, ctx.source_range(), mac_name); + item.add_to(acc, ctx.db); + } + } +} diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 0da7ba6d0001..280a3ffebd70 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -371,6 +371,7 @@ pub(super) enum CompletionAnalysis { UnexpandedAttrTT { colon_prefix: bool, fake_attribute_under_caret: Option, + extern_crate: Option, }, } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 1e6b2f319aad..65060ea99f9e 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -254,11 +254,13 @@ fn analyze( { let colon_prefix = previous_non_trivia_token(self_token.clone()) .map_or(false, |it| T![:] == it.kind()); + CompletionAnalysis::UnexpandedAttrTT { fake_attribute_under_caret: fake_ident_token .parent_ancestors() .find_map(ast::Attr::cast), colon_prefix, + extern_crate: p.ancestors().find_map(ast::ExternCrate::cast), } } else { return None; diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 37a2828e8dc8..ff324e7a56d6 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -211,12 +211,14 @@ pub fn completions( CompletionAnalysis::UnexpandedAttrTT { colon_prefix, fake_attribute_under_caret: Some(attr), + extern_crate, } => { completions::attribute::complete_known_attribute_input( acc, ctx, colon_prefix, attr, + extern_crate.as_ref(), ); } CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index d8c134c533b3..351abe9850b9 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/crates/ide-completion/src/tests/attribute.rs @@ -1067,3 +1067,82 @@ mod repr { ); } } + +mod macro_use { + use super::*; + + #[test] + fn completes_macros() { + check( + r#" +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => {}; +} + +#[macro_export] +macro_rules! bar { + () => {}; +} + +//- /main.rs crate:main deps:dep +#[macro_use($0)] +extern crate dep; +"#, + expect![[r#" + ma bar + ma foo + "#]], + ) + } + + #[test] + fn only_completes_exported_macros() { + check( + r#" +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => {}; +} + +macro_rules! bar { + () => {}; +} + +//- /main.rs crate:main deps:dep +#[macro_use($0)] +extern crate dep; +"#, + expect![[r#" + ma foo + "#]], + ) + } + + #[test] + fn does_not_completes_already_imported_macros() { + check( + r#" +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => {}; +} + +#[macro_export] +macro_rules! bar { + () => {}; +} + +//- /main.rs crate:main deps:dep +#[macro_use(foo, $0)] +extern crate dep; +"#, + expect![[r#" + ma bar + "#]], + ) + } +}