diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 68fbe48ebcb08..306f6b2385ec0 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -696,6 +696,20 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None }; if let Some(kind) = ambiguity_error_kind { + // Prevent infinite recursion when macro_rules! macros shadow builtin attributes. + // Only apply this fix for macro_rules! to avoid breaking legitimate proc-macro ambiguity detection. + if kind == AmbiguityKind::BuiltinAttr { + if is_builtin(innermost_res) + && flags.contains(Flags::MACRO_RULES) + { + return Some(Ok(innermost_binding)); + } else if is_builtin(res) + && innermost_flags.contains(Flags::MACRO_RULES) + { + return Some(Ok(binding)); + } + } + let misc = |f: Flags| { if f.contains(Flags::MISC_SUGGEST_CRATE) { AmbiguityErrorMisc::SuggestCrate diff --git a/tests/ui/resolve/macro-builtin-attr-no-crash.rs b/tests/ui/resolve/macro-builtin-attr-no-crash.rs new file mode 100644 index 0000000000000..6bd3c83498484 --- /dev/null +++ b/tests/ui/resolve/macro-builtin-attr-no-crash.rs @@ -0,0 +1,38 @@ +// Regression test for stack overflow when a `macro_rules!` macro shadows +// a builtin attribute and is used recursively within its own expansion. + +//@ check-pass + +#![allow(unused)] + +#[macro_export] +macro_rules! test { + () => { + #[test] + fn generated_test() { + assert!(true); + } + }; +} + +// Additional case with derive +#[macro_export] +macro_rules! derive { + () => { + #[derive(Debug)] + struct Foo; + }; +} + +#[cfg(test)] +mod tests { + use super::*; + test!(); +} + +mod another_test { + use super::*; + derive!(); +} + +fn main() {}