rust-lang / rust-lang/rust-analyzer

`macro_rules!` emitted by a path-qualified cross-crate call is not added to textual scope

Open
#23,268 4 comments 0 reactions 1 assignee View on GitHub

@Aditya-PS-05 is already working on this.

Since Sep 3, 2026.

A-nameres C-bug
Dominant language
Rust
Stars
16.9k
Forks
2.2k
Avg merge
1d 12h
Merged PRs (30d)
72

Description

rust-analyzer version: 1.100.0-nightly (9085017 2026-08-30) (also 1.98.0, 1.99.0-beta.3)

rustc version: 1.98.0 (88d9e12ae 2026-08-18)

editor or extension: Cursor v3.18.9, Extension version 0.4.3032 (Pre-release, same with release)

relevant settings: None

The issue

Crate generator exports a macro that expands to a macro_rules! foo definition. When a consumer invokes that generator by qualified path (generator::gen_foo!(...)), rust-analyzer expands the generator but never registers the generated foo in the module's textual scope, so every later foo!(...) is unresolved macro. rustc resolves it and the code runs.

Invoking the same generator unqualified via #[macro_use] extern crate generator; works in rust-analyzer. So this is not nested macro_rules! in general, and not a rustc/rust-analyzer hygiene disagreement, it is specific to the qualified call form.

Reproducing code

Workspace with three crates. consumer_qualified and consumer_macro_use differ only in how they invoke gen_foo!.

Workspace Cargo.toml
[workspace]
resolver = "3"
members = ["generator", "consumer_qualified", "consumer_macro_use"]
generator/Cargo.toml
[package]
name = "generator"
version = "0.0.0"
edition = "2024"
generator/src/lib.rs
#[macro_export]
macro_rules! gen_foo {
    () => {
        macro_rules! foo {
            () => {
                42
            };
        }
    };
}
consumer_qualified/Cargo.toml
[package]
name = "consumer_qualified"
version = "0.0.0"
edition = "2024"
[dependencies]
generator = { path = "../generator" }
consumer_qualified/src/inner.rs
generator::gen_foo!();
consumer_qualified/src/lib.rs
#[macro_use]
mod inner;
pub fn call() -> u32 {
    foo!()
}

#[cfg(test)]
mod tests {
    #[test]
    fn generated_macro_runs() {
        assert_eq!(super::call(), 42);
    }
}
consumer_macro_use/Cargo.toml
[package]
name = "consumer_macro_use"
version = "0.0.0"
edition = "2024"
[dependencies]
generator = { path = "../generator" }
consumer_macro_use/src/inner.rs
gen_foo!();
consumer_macro_use/src/lib.rs
#[macro_use]
extern crate generator;
#[macro_use]
mod inner;
pub fn call() -> u32 {
    foo!()
}

#[cfg(test)]
mod tests {
    #[test]
    fn generated_macro_runs() {
        assert_eq!(super::call(), 42);
    }
}
Expected behavior

Both consumers behave the same since both compile and pass the tests

Actual

In the IDE, consumer_qualified call() is marked as error expected u32, found () (which is the consequence of the not expanded macro).
When running rust-analyzer diagnostics . it produces diagnostics (filtered):

consumer_qualified/src/lib.rs: unresolved-macro-call: unresolved macro `foo!`
consumer_qualified/src/lib.rs: E0308: expected u32, found ()

while silent about consumer_macro_use.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.