about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-05-13 18:47:42 +0000
committerbors <bors@rust-lang.org>2023-05-13 18:47:42 +0000
commitdaa03b0b0be6bd51dd43f4556dd4e27649bc6f57 (patch)
tree8e273656d17347bcc25e38f674dc3ea246bfca0c
parentf38264fa567144adba2d846347d66e00b9d08f35 (diff)
parente9ddb62c658b35e3026c470217f38a465f2a3dc7 (diff)
downloadrust-daa03b0b0be6bd51dd43f4556dd4e27649bc6f57.tar.gz
rust-daa03b0b0be6bd51dd43f4556dd4e27649bc6f57.zip
Auto merge of #14800 - lowr:patch/macro-subns-and-prelude, r=Veykril
Expand more single ident macro calls upon item collection

Addresses https://github.com/rust-lang/rust-analyzer/pull/14781#issuecomment-1546201022

I believe this (almost) brings the number of unresolved names back to pre-#14781:

|r-a version|`analysis-stats compiler/rustc` (rust-lang/rust@69fef92ab2f287f072b66fb7b4f62c8bb4acba43) |
|---|---|
|pre-#14781 (b069eb720bec6ce40ab224f57d271687b19b5a07) | exprs: 2747778, ??ty: 122236 (4%), ?ty: 107826 (3%), !ty: 728 |
| #14781 (a7944a93a1520b96f079bbbcd841d6aec9e4ba5d) | exprs: 2713080, ??ty: 139651 (5%), ?ty: 114444 (4%), !ty: 730 |
| with this fix | exprs: 2747871, ??ty: 122237 (4%), ?ty: 108171 (3%), !ty: 676 |

(I haven't investigated on the increase in some numbers but hopefully not too much of a problem)

This is only a temporary solution. The core problem is that we haven't fully implemented the textual scope of legacy macros. For example, we *have been* failing to resolve `foo` in the following snippet, even before #14781 or after this patch. As noted in a FIXME, we need a way to resolve names in textual scope without eager expansion during item collection.

```rust
//- /main.rs crate:main deps:lib
lib::mk_foo!();
const A: i32 = foo!();
             //^^^^^^ unresolved-macro-call

//- /lib.rs crate:lib
#[macro_export]
macro_rules! mk_foo {
    () => {
        macro_rules! foo { () => { 42 } }
    }
}
```
-rw-r--r--crates/hir-def/src/nameres/collector.rs37
-rw-r--r--crates/hir-def/src/nameres/path_resolution.rs6
-rw-r--r--crates/hir-def/src/nameres/tests/macros.rs33
3 files changed, 61 insertions, 15 deletions
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 177edcbb7f2..d1288b7b59b 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -44,7 +44,8 @@ use crate::{
         mod_resolution::ModDir,
         path_resolution::ReachedFixedPoint,
         proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
-        BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin, ResolveMode,
+        sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
+        ResolveMode,
     },
     path::{ImportAlias, ModPath, PathKind},
     per_ns::PerNs,
@@ -2141,26 +2142,34 @@ impl ModCollector<'_, '_> {
 
     fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
         let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
+        let db = self.def_collector.db;
 
-        // Case 1: try to resolve in legacy scope and expand macro_rules
+        // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define
+        // new legacy macros that create textual scopes. We need a way to resolve names in textual
+        // scopes without eager expansion.
+
+        // Case 1: try to resolve macro calls with single-segment name and expand macro_rules
         if let Ok(res) = macro_call_as_call_id(
-            self.def_collector.db.upcast(),
+            db.upcast(),
             &ast_id,
             mac.expand_to,
             self.def_collector.def_map.krate,
             |path| {
                 path.as_ident().and_then(|name| {
-                    self.def_collector.def_map.with_ancestor_maps(
-                        self.def_collector.db,
-                        self.module_id,
-                        &mut |map, module| {
-                            map[module]
-                                .scope
-                                .get_legacy_macro(name)
-                                .and_then(|it| it.last())
-                                .map(|&it| macro_id_to_def_id(self.def_collector.db, it))
-                        },
-                    )
+                    let def_map = &self.def_collector.def_map;
+                    def_map
+                        .with_ancestor_maps(db, self.module_id, &mut |map, module| {
+                            map[module].scope.get_legacy_macro(name)?.last().copied()
+                        })
+                        .or_else(|| def_map[self.module_id].scope.get(name).take_macros())
+                        .or_else(|| def_map.macro_use_prelude.get(name).copied())
+                        .filter(|&id| {
+                            sub_namespace_match(
+                                Some(MacroSubNs::from_id(db, id)),
+                                Some(MacroSubNs::Bang),
+                            )
+                        })
+                        .map(|it| macro_id_to_def_id(self.def_collector.db, it))
                 })
             },
         ) {
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 4740fd7f449..981171013a1 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -59,7 +59,11 @@ impl ResolvePathResult {
 }
 
 impl PerNs {
-    fn filter_macro(mut self, db: &dyn DefDatabase, expected: Option<MacroSubNs>) -> Self {
+    pub(super) fn filter_macro(
+        mut self,
+        db: &dyn DefDatabase,
+        expected: Option<MacroSubNs>,
+    ) -> Self {
         self.macros = self.macros.filter(|&(id, _)| {
             let this = MacroSubNs::from_id(db, id);
             sub_namespace_match(Some(this), expected)
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index e795b7b9b7e..7eb64beb1d7 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -1273,6 +1273,39 @@ pub mod prelude {
 }
 
 #[test]
+fn macro_use_prelude_is_eagerly_expanded() {
+    // See FIXME in `ModCollector::collect_macro_call()`.
+    check(
+        r#"
+//- /main.rs crate:main deps:lib
+#[macro_use]
+extern crate lib;
+mk_foo!();
+mod a {
+    foo!();
+}
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! mk_foo {
+    () => {
+        macro_rules! foo {
+            () => { struct Ok; }
+        }
+    }
+}
+    "#,
+        expect![[r#"
+        crate
+        a: t
+        lib: t
+
+        crate::a
+        Ok: t v
+    "#]],
+    );
+}
+
+#[test]
 fn macro_sub_namespace() {
     let map = compute_crate_def_map(
         r#"