Change:
() Errors: If either the recursion () or the is removed (), this compiles exactly as expected. Additionally, if I explicitly name my crate, it works as expected, but would be vulnerable to crate renames in user code (can't reproduce in playground because it doesn't build a library).
If you try and build Rustup, you get a similar failure, but without recursion or . Seems that just using another macro and is enough in some circumstances
cc for the hygiene macro in
This code () compiles on stable but not on nightly and beta:
Please, file a separate issue for the regression, I'll prioritize it over pre-existing issues (which the original issue seems to be).
Thanks, I filed it in
Fixed in
Thanks for your work on this!

Discussion:
ImportResolver { r: self }.resolve_imports() } <del> fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: ExpnId, force: bool) -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate> { let parent_scope = self.invocation_parent_scopes[&invoc_id]; </del> <ins> fn resolve_macro_invocation( &mut self, invoc: &Invocation, eager_expansion_root: ExpnId, force: bool ) -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate> { let invoc_id = invoc.expansion_data.id; let parent_scope = match self.invocation_parent_scopes.get(&invoc_id) { Some(parent_scope) => *parent_scope, None => { // If there's no entry in the table, then we are resolving an eagerly expanded // macro, which should inherit its parent scope from its eager expansion root - // the macro that requested this eager expansion. let parent_scope = *self.invocation_parent_scopes.get(&eager_expansion_root) .expect("non-eager expansion without a parent scope"); self.invocation_parent_scopes.insert(invoc_id, parent_scope); parent_scope } }; </ins> let (path, kind, derives, after_derive) = match invoc.kind { InvocationKind::Attr { ref attr, ref derives, after_derive, .. } => (&attr.path, MacroKind::Attr, self.arenas.alloc_ast_paths(derives), after_derive),

match self.resolve_macro_path(path, Some(MacroKind::Derive), &parent_scope, true, force) { Ok((Some(ref ext), _)) if ext.is_derive_copy => { <del> self.add_derives(invoc.expansion_data.id, SpecialDerives::COPY); </del> <ins> self.add_derives(invoc_id, SpecialDerives::COPY); </ins> return Ok(None); } Err(Determinacy::Undetermined) => result = Err(Indeterminate),

let (ext, res) = self.smart_resolve_macro_path(path, kind, parent_scope, force)?; let span = invoc.span(); <del> invoc.expansion_data.id.set_expn_data( ext.expn_data(parent_scope.expansion, span, fast_print_path(path)) ); </del> <ins> invoc_id.set_expn_data(ext.expn_data(parent_scope.expansion, span, fast_print_path(path))); </ins> if let Res::Def(_, def_id) = res { if after_derive { self.session.span_err(span, "macro attributes must be placed before `#[derive]`"); } <del> self.macro_defs.insert(invoc.expansion_data.id, def_id); let normal_module_def_id = self.macro_def_scope(invoc.expansion_data.id).normal_ancestor_id; self.definitions.add_parent_module_of_macro_def(invoc.expansion_data.id, normal_module_def_id); </del> <ins> self.macro_defs.insert(invoc_id, def_id); let normal_module_def_id = self.macro_def_scope(invoc_id).normal_ancestor_id; self.definitions.add_parent_module_of_macro_def(invoc_id, normal_module_def_id); </ins> } Ok(Some(ext))

fn resolve_imports(&mut self); <del> fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: ExpnId, force: bool) -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate>; </del> <ins> fn resolve_macro_invocation( &mut self, invoc: &Invocation, eager_expansion_root: ExpnId, force: bool ) -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate>; </ins> fn check_unused_macros(&self);

/// compilation on error, merely emits a non-fatal error and returns `None`. pub fn expr_to_spanned_string<'a>( cx: &'a mut ExtCtxt<'_>, <del> mut expr: P<ast::Expr>, </del> <ins> expr: P<ast::Expr>, </ins> err_msg: &str, ) -> Result<(Symbol, ast::StrStyle, Span), Option<DiagnosticBuilder<'a>>> { <del> // Update `expr.span`'s ctxt now in case expr is an `include!` macro invocation. expr.span = expr.span.apply_mark(cx.current_expansion.id); </del> // Perform eager expansion on the expression. // We want to be able to handle e.g., `concat!("foo", "bar")`. let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();

continue }; <del> let scope = </del> <ins> let eager_expansion_root = </ins> if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id }; <del> let ext = match self.cx.resolver.resolve_macro_invocation(&invoc, scope, force) { </del> <ins> let ext = match self.cx.resolver.resolve_macro_invocation( &invoc, eager_expansion_root, force ) { </ins> Ok(ext) => ext, Err(Indeterminate) => { undetermined_invocations.push(invoc);

progress = true; let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data; self.cx.current_expansion = invoc.expansion_data.clone(); <del> self.cx.current_expansion.id = scope; </del> // FIXME(jseyfried): Refactor out the following logic let (expanded_fragment, new_invocations) = if let Some(ext) = ext {

<ins> // Regression test for the issue #63460. // check-pass #[macro_export] macro_rules! separator { () => { "/" }; } #[macro_export] macro_rules! concat_separator { ( $e:literal, $($other:literal),+ ) => { concat!($e, $crate::separator!(), $crate::concat_separator!($($other),+)) }; ( $e:literal ) => { $e } } fn main() { println!("{}", concat_separator!(2, 3, 4)) } </ins>

<ins> // Opaque macro can eagerly expand its input without breaking its resolution. // Regression test for issue #63685. // check-pass macro_rules! foo { () => { "foo" }; } macro_rules! bar { () => { foo!() }; } fn main() { format_args!(bar!()); } </ins>

LL | |         "" LL | |     } | |_____^ <ins> ... LL |       format_args!(hang!()); |                    ------- in this macro invocation </ins> help: you might be missing a string literal to format with | LL |     format_args!("{}", hang!());