Unnamed repository; edit this file 'description' to name the repository.
Fix case where completion inside macro that expands to `#[test]` was unavailable
We ignore `#[test]` in the def map, so that's why it failed.
Chayim Refael Friedman 2025-01-07
parent 3f2bbe9 · commit b24142b
-rw-r--r--crates/hir-def/src/nameres/collector.rs1
-rw-r--r--crates/hir/src/semantics.rs37
-rw-r--r--crates/ide-completion/src/tests/item.rs72
3 files changed, 105 insertions, 5 deletions
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index de76257587..f5c7338e46 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -1316,6 +1316,7 @@ impl DefCollector<'_> {
// being cfg'ed out).
// Ideally we will just expand them to nothing here. But we are only collecting macro calls,
// not expanding them, so we have no way to do that.
+ // If you add an ignored attribute here, also add it to `Semantics::might_be_inside_macro_call()`.
if matches!(
def.kind,
MacroDefKind::BuiltInAttr(_, expander)
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 7f44f396bf..f6bf53dec0 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -30,7 +30,7 @@ use hir_expand::{
name::AsName,
ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
};
-use intern::Symbol;
+use intern::{sym, Symbol};
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
@@ -811,10 +811,37 @@ impl<'db> SemanticsImpl<'db> {
item.attrs().any(|attr| {
let Some(meta) = attr.meta() else { return false };
let Some(path) = meta.path() else { return false };
- let Some(attr_name) = path.as_single_name_ref() else { return true };
- let attr_name = attr_name.text();
- let attr_name = attr_name.as_str();
- attr_name == "derive" || find_builtin_attr_idx(&Symbol::intern(attr_name)).is_none()
+ if let Some(attr_name) = path.as_single_name_ref() {
+ let attr_name = attr_name.text();
+ let attr_name = Symbol::intern(attr_name.as_str());
+ if attr_name == sym::derive {
+ return true;
+ }
+ // We ignore `#[test]` and friends in the def map, so we cannot expand them.
+ // FIXME: We match by text. This is both hacky and incorrect (people can, and do, create
+ // other macros named `test`). We cannot fix that unfortunately because we use this method
+ // for speculative expansion in completion, which we cannot analyze. Fortunately, most macros
+ // named `test` are test-like, meaning their expansion is not terribly important for IDE.
+ if attr_name == sym::test
+ || attr_name == sym::bench
+ || attr_name == sym::test_case
+ || find_builtin_attr_idx(&attr_name).is_some()
+ {
+ return false;
+ }
+ }
+ let mut segments = path.segments();
+ let mut next_segment_text = || segments.next().and_then(|it| it.name_ref());
+ // `#[core::prelude::rust_2024::test]` or `#[std::prelude::rust_2024::test]`.
+ if next_segment_text().is_some_and(|it| matches!(&*it.text(), "core" | "std"))
+ && next_segment_text().is_some_and(|it| it.text() == "prelude")
+ && next_segment_text().is_some()
+ && next_segment_text()
+ .is_some_and(|it| matches!(&*it.text(), "test" | "bench" | "test_case"))
+ {
+ return false;
+ }
+ true
})
})
}
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index f34f3d0fc2..79561a0419 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -241,3 +241,75 @@ impl Copy for S where $0
"#,
);
}
+
+#[test]
+fn test_is_not_considered_macro() {
+ check(
+ r#"
+#[rustc_builtin]
+pub macro test($item:item) {
+ /* compiler built-in */
+}
+
+macro_rules! expand_to_test {
+ ( $i:ident ) => {
+ #[test]
+ fn foo() { $i; }
+ };
+}
+
+fn bar() {
+ let value = 5;
+ expand_to_test!(v$0);
+}
+ "#,
+ expect![[r#"
+ ct CONST Unit
+ en Enum Enum
+ fn bar() fn()
+ fn foo() fn()
+ fn function() fn()
+ ma expand_to_test!(…) macro_rules! expand_to_test
+ ma makro!(…) macro_rules! makro
+ ma test!(…) macro test
+ md module
+ sc STATIC Unit
+ st Record Record
+ st Tuple Tuple
+ st Unit Unit
+ un Union Union
+ ev TupleV(…) TupleV(u32)
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}