Repository: rust-analyzer/rust-analyzer
Pull request #11354 (2022-01-27 15:59:03)

fix: More correct `$crate` handling in eager macros
Fixes a few of the additional bugs in https://github.com/rust-analyzer/rust-analyzer/issues/10300, but not yet that issue itself.

bors r+


Patch:
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index faeb6bebab..66f7d8e3bc 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -104,6 +104,7 @@ pub fn expand_eager_macro(
     resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
     diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
 ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+    let hygiene = Hygiene::new(db, macro_call.file_id);
     let parsed_args = macro_call
         .value
         .token_tree()
@@ -131,6 +132,7 @@ pub fn expand_eager_macro(
     let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
     let result = match eager_macro_recur(
         db,
+        &hygiene,
         InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
         krate,
         resolver,
@@ -193,12 +195,12 @@ fn lazy_expand(
 
 fn eager_macro_recur(
     db: &dyn AstDatabase,
+    hygiene: &Hygiene,
     curr: InFile<SyntaxNode>,
     krate: CrateId,
     macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
     mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
 ) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
-    let hygiene = Hygiene::new(db, curr.file_id);
     let original = curr.value.clone_for_update();
 
     let children = original.descendants().filter_map(ast::MacroCall::cast);
@@ -243,7 +245,8 @@ fn eager_macro_recur(
                 };
 
                 // replace macro inside
-                match eager_macro_recur(db, val, krate, macro_resolver, diagnostic_sink) {
+                let hygiene = Hygiene::new(db, val.file_id);
+                match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) {
                     Ok(Ok(it)) => it,
                     Ok(Err(err)) => return Ok(Err(err)),
                     Err(err) => return Err(err),
diff --git a/crates/ide_diagnostics/src/handlers/macro_error.rs b/crates/ide_diagnostics/src/handlers/macro_error.rs
index 43b525133f..a5bfb302d5 100644
--- a/crates/ide_diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide_diagnostics/src/handlers/macro_error.rs
@@ -38,6 +38,45 @@ macro_rules! compile_error { () => {} }
         );
     }
 
+    #[test]
+    fn eager_macro_concat() {
+        // FIXME: this is incorrectly handling `$crate`, resulting in a wrong diagnostic.
+        // See: https://github.com/rust-analyzer/rust-analyzer/issues/10300
+
+        check_diagnostics(
+            r#"
+//- /lib.rs crate:lib deps:core
+use core::{panic, concat};
+
+mod private {
+    pub use core::concat;
+}
+
+macro_rules! m {
+    () => {
+        panic!(concat!($crate::private::concat!("")));
+    };
+}
+
+fn f() {
+    m!();
+  //^^^^ error: unresolved macro `$crate::private::concat!`
+}
+
+//- /core.rs crate:core
+#[macro_export]
+#[rustc_builtin_macro]
+macro_rules! concat { () => {} }
+
+pub macro panic {
+    ($msg:expr) => (
+        $crate::panicking::panic_str($msg)
+    ),
+}
+            "#,
+        );
+    }
+
     #[test]
     fn include_macro_should_allow_empty_content() {
         let mut config = DiagnosticsConfig::default();


Test patch:
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 5d770ecab0..813c16c208 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -1086,6 +1086,7 @@ struct B;
 
 #[test]
 fn eager_macro_correctly_resolves_dollar_crate() {
+    // MBE -> eager -> $crate::mbe
     check(
         r#"
 //- /lib.rs
@@ -1108,9 +1109,37 @@ struct A;
 "#,
         expect![[r#"
             crate
+            A: t v
             inner: m
         "#]],
     );
+    // eager -> MBE -> $crate::mbe
+    check(
+        r#"
+//- /lib.rs
+#[rustc_builtin_macro]
+macro_rules! include { () => {} }
+
+#[macro_export]
+macro_rules! inner {
+    () => { "inc.rs" };
+}
 
-    // FIXME: This currently fails. The result should contain `A: t v`.
+macro_rules! n {
+    () => {
+        $crate::inner!()
+    };
+}
+
+include!(n!());
+
+//- /inc.rs
+struct A;
+"#,
+        expect![[r#"
+            crate
+            A: t v
+            inner: m
+        "#]],
+    );
 }