Repository: rust-lang/rust
File: compiler/rustc_expand/src/mbe/macro_rules.rs (Rust)

Code before:
            DummyResult::any(sp, guar)
        }
        Err(CanRetry::Yes) => {
            // Retry and emit a better error.
            let (span, guar) = failed_to_match_macro(
                cx.psess(),
                sp,
                def_span,
                name,
                FailedMacro::Func,
                &arg,
                rules,
            );
            cx.macro_error_and_trace_macros_diag();
            DummyResult::any(span, guar)
        }
    }
}

/// Expands the rules based macro defined by `rules` for a given attribute `args` and `body`.
#[instrument(skip(cx, transparency, args, body, rules))]
fn expand_macro_attr(
    cx: &mut ExtCtxt<'_>,
    sp: Span,
    def_span: Span,
    node_id: NodeId,
    name: Ident,
    transparency: Transparency,
    safety: Safety,
    args: TokenStream,
    body: TokenStream,
    rules: &[MacroRule],
) -> Result<TokenStream, ErrorGuaranteed> {
    let psess = &cx.sess.psess;
    // Macros defined in the current crate have a real node id,
    // whereas macros from an external crate have a dummy id.
    let is_local = node_id != DUMMY_NODE_ID;

    if cx.trace_macros() {
        let msg = format!(
            "expanding `#[{name}({})] {}`",
            pprust::tts_to_string(&args),
            pprust::tts_to_string(&body),
        );
        trace_macros_note(&mut cx.expansions, sp, msg);
    }

    // Track nothing for the best performance.
    match try_match_macro_attr(psess, name, &args, &body, rules, &mut NoopTracker) {
        Ok((i, rule, named_matches)) => {
            let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else {

Review comment (refactor):
Without closely looking at the lifetimes, it might be that we can pass `lhs` and `rules` to `from_tts` and move this logic to the error code path.

Code after:
            };

            if cx.trace_macros() {
                let msg = format!("to `{}`", pprust::tts_to_string(&tts));
                trace_macros_note(&mut cx.expansions, sp, msg);
            }

            let is_local = is_defined_in_current_crate(node_id);
            if is_local {
                cx.resolver.record_macro_rule_usage(node_id, rule_index);
            }

            let mut bindings = vec![];
            for rule in rules {
                let MacroRule::Func { lhs, .. } = rule else { continue };
                for param in lhs {
                    let MatcherLoc::MetaVarDecl { bind, .. } = param else { continue };
                    bindings.push(*bind);
                }
            }

            let mut matched_rule_bindings = vec![];
            for param in lhs {
                let MatcherLoc::MetaVarDecl { bind, .. } = param else { continue };
                matched_rule_bindings.push(*bind);
            }

            // Let the context choose how to interpret the result. Weird, but useful for X-macros.
            Box::new(ParserAnyMacro::from_tts(
                cx,
                tts,
                sp,
                arm_span,
                is_local,
                name,
                bindings,
                matched_rule_bindings,
            ))
        }
        Err(CanRetry::No(guar)) => {
            debug!("Will not retry matching as an error was emitted already");
            DummyResult::any(sp, guar)
        }
        Err(CanRetry::Yes) => {
            // Retry and emit a better error.
            let (span, guar) = failed_to_match_macro(
                cx.psess(),
                sp,
                def_span,
                name,
                FailedMacro::Func,