Skip to content

Rust: reconstruct format-macro expansions on pre-1.94 toolchains - #22350

Draft
redsun82 wants to merge 1 commit into
redsun82-rust-analyzer-updatefrom
redsun82-format-macro-flow-recovery
Draft

Rust: reconstruct format-macro expansions on pre-1.94 toolchains#22350
redsun82 wants to merge 1 commit into
redsun82-rust-analyzer-updatefrom
redsun82-format-macro-flow-recovery

Conversation

@redsun82

Copy link
Copy Markdown
Contributor

Stacked on #22346 (RA 0.0.347 upgrade). Review/merge that first; this PR targets its branch.

Problem

rust-analyzer 0.0.347 only expands the builtin format_args! machinery against a std that carries the new lowering (roughly Rust >= 1.94). On older toolchains the format-family macros (format!, println!, write!, panic!, ...) fail to expand, so expand_macro_call returns None and we get a bare unexpanded MacroCall. That drops:

  • data flow through these macros (flow is modeled on the FormatArgsExpr node), and
  • security-query sinks keyed on the wrapping callee, e.g. log injection on println!/eprintln!/panic! (std::io::stdio::_print / _eprint, core::panicking::panic_fmt).

Fix

The syntactic lowering of these macros is a pure, sysroot-independent transform, so the extractor rebuilds the same token tree the real (>=1.94) expansion has, parses it, and registers the result as the macro expansion.

The reconstruction is faithful per macro rather than a blanket bare FormatArgsExpr, so the callee that carries flow and the sink models is preserved:

macro reconstruction
format_args!, const_format_args!, format_args_nl! format_args!(..)
format! ::std::fmt::format(format_args!(..))
print!, println! ::std::io::_print(format_args!(..))
eprint!, eprintln! ::std::io::_eprint(format_args!(..))
panic! ::core::panicking::panic_fmt(format_args!(..))
write!, writeln! <dst>.write_fmt(format_args!(..))

format_args_nl!'s trailing newline is dropped: it is irrelevant to flow and to the sinks keyed on the callee.

The synthesized-token construction lives in a new translate/format_args.rs module; base.rs keeps only the orchestration (tokenize, reconstruct, parse, emit).

Testing

New library-tests/format-macros-legacy/ test, pinned to a pre-1.94 (1.93) toolchain so it actually hits the reconstruction path the default 1.95 test toolchain never reaches:

  • taint flow through format! (InlineFlowTest),
  • log-injection alerts for println!/eprintln!/print!/eprint! (LogInjection.qlref), confirming the sinks survive on <1.94,
  • FormatArgsExpr node presence for the rest of the family.

setup.sh pre-installs the 1.93 toolchain so the parallel QL tests do not race on rustup auto-install.

Notes

  • Flow into a write!/writeln! writer buffer is not recovered, but that matches native >=1.94 behavior (there is no Write::write_fmt content-to-self taint model). It is a pre-existing model gap, not a regression from this change, and is left for a follow-up.

@github-actions github-actions Bot added documentation Rust Pull requests that update Rust code labels Aug 14, 2026
@redsun82
redsun82 force-pushed the redsun82-format-macro-flow-recovery branch from 962c659 to da97573 Compare August 14, 2026 17:35
`rust-analyzer` 0.0.347 no longer expands the format-family macros
(`format!`, `println!`, `write!`, `panic!`, ...) against a pre-1.94 std,
so flow through them and the security-query sinks keyed on their callees
were lost. Rebuild each macro's real expansion (a `FormatArgsExpr` wrapped
in its callee) from the argument tokens so both keep working.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7492ff50-9c8e-47ef-a70d-f2623b702c8f
@redsun82
redsun82 force-pushed the redsun82-format-macro-flow-recovery branch from da97573 to 7faf81f Compare August 14, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant