fix(useless_format): fire on wrapped in a block-producing macro#17060
Merged
samueltardieu merged 2 commits intoMay 23, 2026
Conversation
Collaborator
|
r? @Jarcho rustbot has assigned @Jarcho. Use Why was this reviewer chosen?The reviewer was selected based on:
|
Contributor
Author
samueltardieu
requested changes
May 23, 2026
Collaborator
|
Reminder, once the PR becomes ready for a review, use |
f08f793 to
c31d970
Compare
Contributor
Author
|
@rustbot ready |
samueltardieu
approved these changes
May 23, 2026
Contributor
Author
|
@samueltardieu Thanks for the patient review 🙏 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a false negative in
useless_format: the lint silently failed to fire onformat!("{}", s)when the call is the tail expression of a block produced by another macro. This affects rustc's entiredefine_helper!-generated family (with_forced_trimmed_paths!,with_no_trimmed_paths!,with_no_queries!, etc).Spotted during review of #17058:
Reproducer
Before this PR, cases (3) and (5) are silently missed. After this PR all five fire as expected. Note that the discriminator is block-wrapping, not
macro_rulesvsdecl_macro.Root cause
clippy_lints/src/format.rspreviously usedroot_macro_call_first_node, which requiresfirst_node_in_macro == Some(ExpnId::root()). Forblock_dm!(format!(...)), theformat!HIR parent is theBlockemitted byblock_dm, whose span lives inblock_dm's expansion (sibling toformat!'s).first_node_in_macroreturnsSome(block_dm.expn)rather thanSome(root), and the lint bails.Fix
Replace the gate with:
matching_root_macro_callpreserves hygiene. The outermost macro onexpr.span's backtrace must beformat!, so aformat!written inside another macro's body (where the rewrite would target the macro definition) is still ignored.first_node_in_macro(..).is_some_and(|p| p != macro_call.expn)preserves single-firing.exprmust be the outermost node offormat!'s expansion. Withoutp != macro_call.expn, deeper nodes whose parent is also informat!'s expansion (including internalformat_args!invocations) would slip through and the lint would fire multiple times per call.Test changes
tests/ui/format.{rs,fixed,stderr}: added#![feature(decl_macro)]and ablock_wrapmodule covering all four pass-through and block-wrap combinations acrossmacro_rules!anddecl_macro, as regression coverage.tests/ui/unused_format_specs.{rs,1.fixed,2.fixed}: addedclippy::useless_formatto the allow-list. The relaxed gate also starts firing onprintln!("{:.3}", format!("abcde"))-style cases, which appear in this test's.fixedoutputs (afterunused_format_specssuggestsformat_args!toformat!). This is a latent true positive previously masked by the strict gate. The test is scoped tounused_format_specsand should not be entangled with another lint's coverage. The same pattern is already used bytests/ui/format.rsitself, which allows other unrelated lints.Verification
cargo test --test compile-test: 1764 UI tests + 188 fixed checks + 46 ui-cargo tests pass. 0 duplicate diagnostics.cargo test --test dogfood: catches the same false negative in clippy's ownunnecessary_literal_unwrap.rs(the line thatuseless_formatmissesformat!inside block-wrapping macros #17059 is fixing manually), demonstrating the fix on real-world code.Related
format!("{}", ..)arounddef_path_strinunnecessary_literal_unwrap#17058.useless_formatmissesformat!inside block-wrapping macros #17059.tests/ui/format.rs, see the newmod block_wrapat the bottom.changelog: [
useless_format] no longer missesformat!calls that are the tail expression of a block produced by another macro (e.g. rustc'swith_forced_trimmed_paths!).