Skip to content

fix: uninlined_format_args for #[clippy::format_args] macros that adapt arguments Fixes uninlined_format_args not firing for #[clippy::format_args] macros that wrap their arguments before forwarding to format_args! (e.g. wrapping x in Adapter(&x)).#16877

Open
Souradip121 wants to merge 3 commits intorust-lang:masterfrom
Souradip121:fix-format-args-adapted-args

Conversation

@Souradip121
Copy link
Copy Markdown
Contributor

@Souradip121 Souradip121 commented Apr 17, 2026

Closes #16833

What was broken

#[clippy::format_args]
macro_rules! adapted_format_args {
    ($fmt:literal, $arg:expr) => {
        format_args!($fmt, Adapter(&($arg)))  // adapts the argument
    };
}

let _ = adapted_format_args!("{}", x);  // no warning — should warn

The lint silently did nothing. Two separate bugs caused this:

Bug 1 — has_span_from_proc_macro false positive (format_args_collector.rs)

FormatArgsCollector uses has_span_from_proc_macro to decide whether to store a
FormatArgs node. It checks the "between" spans — gaps between the format string and
each argument — to detect proc-macro-set spans.

For adapted macros, walk_chain(Adapter(&(x)).span, call_site_ctxt) resolves to the
entire outer macro call span, which starts before the format string literal. This
makes start.between(end) produce an inverted span. The original code passed this
inverted span to check_source_text, which returned false, causing
has_span_from_proc_macro to return true — wrongly treating the FormatArgs as
proc-macro-generated and refusing to store it. Without storage, the late lint pass never
sees it and never fires.

Fix: Skip the check_source_text call when the between-span is inverted
(sp.lo() < start.hi()), treating it as valid. The guard is scoped to
!args.span.from_expansion() so that macros like concat! (whose format string IS
from expansion) are unaffected.

Bug 2 — check_one_arg only handled bare path arguments (format_args.rs)

The existing code required the format argument to be a bare ExprKind::Path (e.g. x).
For adapted macros, format_args! receives Adapter(&(x)) — an ExprKind::Call — so
the pattern match failed and no suggestion was produced.

Fix: Added find_callsite_simple_path, a helper that recurses through macro-body
wrappers (Call, AddrOf, Paren, Unary, Cast) to find the single call-site
variable that $arg was substituted with. Returns None if the call-site expression
is not a simple path (e.g. x + 1), preventing false positives. The argument removal
span is computed using the call-site variable span, anchored to the end of the previous
item.

After the fix

let _ = adapted_format_args!("{}", x);
//~^ warning: variables can be used directly in the `format!` string
// suggested fix: adapted_format_args!("{x}")

let _ = forwarding_adapted_format_args!("{}", x);
//~^ warning: variables can be used directly in the `format!` string
// suggested fix: forwarding_adapted_format_args!("{x}")

// correctly does NOT lint (not a simple variable):
let _ = adapted_format_args!("{}", x + 1);

Non-adapted macros are unaffected

Existing #[clippy::format_args] tests (usr_println! etc.) continue to pass
unchanged.

changelog: [uninlined_format_args]: Now correctly fires for #[clippy::format_args] macros that adapt their arguments (e.g. wrap them in a newtype) before forwarding to format_args!.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Apr 17, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 17, 2026

r? @samueltardieu

rustbot has assigned @samueltardieu.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: 7 candidates
  • 7 candidates expanded to 7 candidates
  • Random selection from Jarcho, dswij, llogiq, samueltardieu

@samueltardieu
Copy link
Copy Markdown
Member

Out of rotation
r? clippy

@rustbot rustbot assigned dswij and unassigned samueltardieu Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties

Projects

None yet

Development

Successfully merging this pull request may close these issues.

#[clippy::format_args] does not support macros that adapt arguments

4 participants