Unnamed repository; edit this file 'description' to name the repository.
Complete exported macros in `#[macro_use($0)]`
unexge 2023-12-16
parent 21b06c1 · commit b986d8a
-rw-r--r--crates/ide-completion/src/completions/attribute.rs5
-rw-r--r--crates/ide-completion/src/completions/attribute/macro_use.rs34
-rw-r--r--crates/ide-completion/src/context.rs1
-rw-r--r--crates/ide-completion/src/context/analysis.rs2
-rw-r--r--crates/ide-completion/src/lib.rs2
-rw-r--r--crates/ide-completion/src/tests/attribute.rs79
6 files changed, 123 insertions, 0 deletions
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index 466f0b1fb7..9155caa2e0 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 0000000000..806c9f64c0
--- /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 0da7ba6d00..280a3ffebd 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<ast::Attr>,
+ extern_crate: Option<ast::ExternCrate>,
},
}
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 1e6b2f319a..65060ea99f 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 37a2828e8d..ff324e7a56 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 d8c134c533..351abe9850 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
+ "#]],
+ )
+ }
+}