about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs72
3 files changed, 105 insertions, 5 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index d5e13f5d45f..8beeda82bca 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index f53798b39e9..34d169cd761 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs
index f34f3d0fc2f..79561a0419f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs
+++ b/src/tools/rust-analyzer/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
+        "#]],
+    );
+}