Skip to content

refactor(dfir_lang): Define internal codegen identifiers once with explicit Span::call_site() #2781

@MingweiSamuel

Description

@MingweiSamuel

Problem

The inline codegen in meta_graph.rs uses several "magic" identifiers that are internal to the generated code: __dfir_work_done, __dfir_metrics, and potentially others. These are referenced across multiple quote! and quote_spanned! blocks throughout the codegen.

When these identifiers appear inside quote_spanned! {some_user_span=> ...}, they inherit the user-provided span. This causes macro hygiene failures when dfir_syntax! is invoked indirectly through a declarative macro (e.g. dfir_expect_warnings!), because the user tokens have a different hygiene context and the generated identifiers can't resolve to the variables defined in the outer quote! scope.

We hit this in #2779dfir_expect_warnings! passes user code as a tt to dfir_syntax_noemit!, which changes the hygiene context. The fix was to create __dfir_work_done and __dfir_metrics with explicit Span::call_site() at the one call site that used quote_spanned!.

Proposed improvement

Define all internal codegen identifiers once at the top of the codegen function using Ident::new("...", Span::call_site()), then substitute them as #ident everywhere:

let work_done_ident = Ident::new("__dfir_work_done", Span::call_site());
let metrics_ident = Ident::new("__dfir_metrics", Span::call_site());
let wake_state_ident = Ident::new("__dfir_wake_state", Span::call_site());
// ... etc

Then use #work_done_ident and #metrics_ident in all quote!/quote_spanned! blocks instead of bare __dfir_work_done/__dfir_metrics.

This ensures:

  1. Hygiene correctness by construction — every reference to an internal identifier gets Span::call_site(), so it always resolves in the proc macro's output context regardless of how the input tokens arrive.
  2. Single source of truth — if an identifier name needs to change, it's updated in one place.
  3. No future regressions — new quote_spanned! blocks can't accidentally introduce bare identifiers that inherit user spans.

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions