Skip to content

Commit ca978aa

Browse files
committed
Return a value from find_format_args instead of using a callback
1 parent b788add commit ca978aa

File tree

9 files changed

+121
-116
lines changed

9 files changed

+121
-116
lines changed

clippy_lints/src/explicit_write.rs

+44-46
Original file line numberDiff line numberDiff line change
@@ -57,54 +57,52 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
5757
} else {
5858
None
5959
}
60+
&& let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root())
6061
{
61-
find_format_args(cx, write_arg, ExpnId::root(), |format_args| {
62-
let calling_macro =
63-
// ordering is important here, since `writeln!` uses `write!` internally
64-
if is_expn_of(write_call.span, "writeln").is_some() {
65-
Some("writeln")
66-
} else if is_expn_of(write_call.span, "write").is_some() {
67-
Some("write")
68-
} else {
69-
None
70-
};
71-
let prefix = if dest_name == "stderr" {
72-
"e"
73-
} else {
74-
""
75-
};
62+
// ordering is important here, since `writeln!` uses `write!` internally
63+
let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() {
64+
Some("writeln")
65+
} else if is_expn_of(write_call.span, "write").is_some() {
66+
Some("write")
67+
} else {
68+
None
69+
};
70+
let prefix = if dest_name == "stderr" {
71+
"e"
72+
} else {
73+
""
74+
};
7675

77-
// We need to remove the last trailing newline from the string because the
78-
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
79-
// used.
80-
let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
81-
(
82-
format!("{macro_name}!({dest_name}(), ...)"),
83-
macro_name.replace("write", "print"),
84-
)
85-
} else {
86-
(
87-
format!("{dest_name}().write_fmt(...)"),
88-
"print".into(),
89-
)
90-
};
91-
let mut applicability = Applicability::MachineApplicable;
92-
let inputs_snippet = snippet_with_applicability(
93-
cx,
94-
format_args_inputs_span(format_args),
95-
"..",
96-
&mut applicability,
97-
);
98-
span_lint_and_sugg(
99-
cx,
100-
EXPLICIT_WRITE,
101-
expr.span,
102-
&format!("use of `{used}.unwrap()`"),
103-
"try",
104-
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
105-
applicability,
106-
);
107-
});
76+
// We need to remove the last trailing newline from the string because the
77+
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
78+
// used.
79+
let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
80+
(
81+
format!("{macro_name}!({dest_name}(), ...)"),
82+
macro_name.replace("write", "print"),
83+
)
84+
} else {
85+
(
86+
format!("{dest_name}().write_fmt(...)"),
87+
"print".into(),
88+
)
89+
};
90+
let mut applicability = Applicability::MachineApplicable;
91+
let inputs_snippet = snippet_with_applicability(
92+
cx,
93+
format_args_inputs_span(format_args),
94+
"..",
95+
&mut applicability,
96+
);
97+
span_lint_and_sugg(
98+
cx,
99+
EXPLICIT_WRITE,
100+
expr.span,
101+
&format!("use of `{used}.unwrap()`"),
102+
"try",
103+
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
104+
applicability,
105+
);
108106
}
109107
}
110108
}

clippy_lints/src/format.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,10 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
4343

4444
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
4545
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
46-
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
47-
return;
48-
};
49-
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
50-
return;
51-
}
52-
53-
find_format_args(cx, expr, macro_call.expn, |format_args| {
46+
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
47+
&& cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
48+
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
49+
{
5450
let mut applicability = Applicability::MachineApplicable;
5551
let call_site = macro_call.span;
5652

@@ -91,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
9187
},
9288
_ => {},
9389
}
94-
});
90+
}
9591
}
9692
}
9793

clippy_lints/src/format_args.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,10 @@ impl FormatArgs {
186186

187187
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
188188
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
189-
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
190-
return;
191-
};
192-
if !is_format_macro(cx, macro_call.def_id) {
193-
return;
194-
}
195-
let name = cx.tcx.item_name(macro_call.def_id);
196-
197-
find_format_args(cx, expr, macro_call.expn, |format_args| {
189+
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
190+
&& is_format_macro(cx, macro_call.def_id)
191+
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
192+
{
198193
for piece in &format_args.template {
199194
if let FormatArgsPiece::Placeholder(placeholder) = piece
200195
&& let Ok(index) = placeholder.argument.index
@@ -212,6 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
212207
}
213208

214209
if let Ok(arg_hir_expr) = arg_expr {
210+
let name = cx.tcx.item_name(macro_call.def_id);
215211
check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
216212
check_to_string_in_format_args(cx, name, arg_hir_expr);
217213
}
@@ -221,7 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
221217
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
222218
check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
223219
}
224-
});
220+
}
225221
}
226222

227223
extract_msrv_attr!(LateContext);

clippy_lints/src/format_impl.rs

+20-21
Original file line numberDiff line numberDiff line change
@@ -170,30 +170,29 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
170170
if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
171171
&& let macro_def_id = outer_macro.def_id
172172
&& is_format_macro(cx, macro_def_id)
173+
&& let Some(format_args) = find_format_args(cx, expr, outer_macro.expn)
173174
{
174-
find_format_args(cx, expr, outer_macro.expn, |format_args| {
175-
for piece in &format_args.template {
176-
if let FormatArgsPiece::Placeholder(placeholder) = piece
177-
&& let trait_name = match placeholder.format_trait {
178-
FormatTrait::Display => sym::Display,
179-
FormatTrait::Debug => sym::Debug,
180-
FormatTrait::LowerExp => sym!(LowerExp),
181-
FormatTrait::UpperExp => sym!(UpperExp),
182-
FormatTrait::Octal => sym!(Octal),
183-
FormatTrait::Pointer => sym::Pointer,
184-
FormatTrait::Binary => sym!(Binary),
185-
FormatTrait::LowerHex => sym!(LowerHex),
186-
FormatTrait::UpperHex => sym!(UpperHex),
187-
}
188-
&& trait_name == impl_trait.name
189-
&& let Ok(index) = placeholder.argument.index
190-
&& let Some(arg) = format_args.arguments.all_args().get(index)
191-
&& let Ok(arg_expr) = find_format_arg_expr(expr, arg)
192-
{
193-
check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
175+
for piece in &format_args.template {
176+
if let FormatArgsPiece::Placeholder(placeholder) = piece
177+
&& let trait_name = match placeholder.format_trait {
178+
FormatTrait::Display => sym::Display,
179+
FormatTrait::Debug => sym::Debug,
180+
FormatTrait::LowerExp => sym!(LowerExp),
181+
FormatTrait::UpperExp => sym!(UpperExp),
182+
FormatTrait::Octal => sym!(Octal),
183+
FormatTrait::Pointer => sym::Pointer,
184+
FormatTrait::Binary => sym!(Binary),
185+
FormatTrait::LowerHex => sym!(LowerHex),
186+
FormatTrait::UpperHex => sym!(UpperHex),
194187
}
188+
&& trait_name == impl_trait.name
189+
&& let Ok(index) = placeholder.argument.index
190+
&& let Some(arg) = format_args.arguments.all_args().get(index)
191+
&& let Ok(arg_expr) = find_format_arg_expr(expr, arg)
192+
{
193+
check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
195194
}
196-
});
195+
}
197196
}
198197
}
199198

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
609609
.collect(),
610610
))
611611
});
612-
store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
612+
store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default());
613613
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
614614
store.register_late_pass(|_| Box::new(utils::author::Author));
615615
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();

clippy_lints/src/methods/expect_fun_call.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,11 @@ pub(super) fn check<'tcx>(
131131

132132
let mut applicability = Applicability::MachineApplicable;
133133

134-
//Special handling for `format!` as arg_root
134+
// Special handling for `format!` as arg_root
135135
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
136-
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
137-
return;
138-
}
139-
find_format_args(cx, arg_root, macro_call.expn, |format_args| {
136+
if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
137+
&& let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn)
138+
{
140139
let span = format_args_inputs_span(format_args);
141140
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
142141
span_lint_and_sugg(
@@ -148,7 +147,7 @@ pub(super) fn check<'tcx>(
148147
format!("unwrap_or_else({closure_args} panic!({sugg}))"),
149148
applicability,
150149
);
151-
});
150+
}
152151
return;
153152
}
154153

clippy_lints/src/utils/format_args_collector.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use clippy_utils::macros::collect_ast_format_args;
22
use clippy_utils::source::snippet_opt;
33
use itertools::Itertools;
4-
use rustc_ast::{Expr, ExprKind, FormatArgs};
4+
use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
5+
use rustc_data_structures::fx::FxHashMap;
56
use rustc_lexer::{tokenize, TokenKind};
67
use rustc_lint::{EarlyContext, EarlyLintPass};
7-
use rustc_session::{declare_lint_pass, declare_tool_lint};
8-
use rustc_span::hygiene;
8+
use rustc_session::{declare_tool_lint, impl_lint_pass};
9+
use rustc_span::{hygiene, Span};
910
use std::iter::once;
11+
use std::mem;
1012

1113
declare_clippy_lint! {
1214
/// ### What it does
@@ -17,7 +19,12 @@ declare_clippy_lint! {
1719
"collects `format_args` AST nodes for use in later lints"
1820
}
1921

20-
declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
22+
#[derive(Default)]
23+
pub struct FormatArgsCollector {
24+
format_args: FxHashMap<Span, FormatArgs>,
25+
}
26+
27+
impl_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
2128

2229
impl EarlyLintPass for FormatArgsCollector {
2330
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
@@ -26,9 +33,13 @@ impl EarlyLintPass for FormatArgsCollector {
2633
return;
2734
}
2835

29-
collect_ast_format_args(expr.span, args);
36+
self.format_args.insert(expr.span.with_parent(None), (**args).clone());
3037
}
3138
}
39+
40+
fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
41+
collect_ast_format_args(mem::take(&mut self.format_args));
42+
}
3243
}
3344

3445
/// Detects if the format string or an argument has its span set by a proc macro to something inside

clippy_lints/src/write.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
304304
_ => return,
305305
}
306306

307-
find_format_args(cx, expr, macro_call.expn, |format_args| {
307+
if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) {
308308
// ignore `writeln!(w)` and `write!(v, some_macro!())`
309309
if format_args.span.from_expansion() {
310310
return;
@@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
334334
}
335335
}
336336
}
337-
});
337+
}
338338
}
339339
}
340340

clippy_utils/src/macros.rs

+22-16
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_lint::LateContext;
1010
use rustc_span::def_id::DefId;
1111
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
1212
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
13-
use std::cell::RefCell;
13+
use std::cell::OnceCell;
1414
use std::ops::ControlFlow;
1515
use std::sync::atomic::{AtomicBool, Ordering};
1616

@@ -374,30 +374,31 @@ thread_local! {
374374
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
375375
/// assumption that the early pass that populates the map and the later late passes will all be
376376
/// running on the same thread.
377-
static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
377+
static AST_FORMAT_ARGS: OnceCell<FxHashMap<Span, FormatArgs>> = {
378378
static CALLED: AtomicBool = AtomicBool::new(false);
379379
debug_assert!(
380380
!CALLED.swap(true, Ordering::SeqCst),
381381
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
382382
);
383383

384-
RefCell::default()
384+
OnceCell::new()
385385
};
386386
}
387387

388388
/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
389389
/// `FormatArgsCollector`
390-
pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
390+
#[allow(clippy::implicit_hasher)]
391+
#[doc(hidden)]
392+
pub fn collect_ast_format_args(format_args: FxHashMap<Span, FormatArgs>) {
391393
AST_FORMAT_ARGS.with(|ast_format_args| {
392-
ast_format_args
393-
.borrow_mut()
394-
.insert(span.with_parent(None), format_args.clone());
394+
let result = ast_format_args.set(format_args);
395+
debug_assert!(result.is_ok(), "`collect_ast_format_args` should only be called once");
395396
});
396397
}
397398

398-
/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
399-
/// descendant of `expn_id`
400-
pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
399+
/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
400+
/// `expn_id`
401+
pub fn find_format_args<'a>(cx: &'a LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&'a FormatArgs> {
401402
let format_args_expr = for_each_expr(start, |expr| {
402403
let ctxt = expr.span.ctxt();
403404
if ctxt.outer_expn().is_descendant_of(expn_id) {
@@ -412,13 +413,18 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId,
412413
} else {
413414
ControlFlow::Continue(Descend::No)
414415
}
415-
});
416+
})?;
416417

417-
if let Some(expr) = format_args_expr {
418-
AST_FORMAT_ARGS.with(|ast_format_args| {
419-
ast_format_args.borrow().get(&expr.span.with_parent(None)).map(callback);
420-
});
421-
}
418+
AST_FORMAT_ARGS.with(|ast_format_args| {
419+
let found = ast_format_args.get()?.get(&format_args_expr.span.with_parent(None))?;
420+
421+
// SAFETY: the lifetime is 'thread which isn't nameable, so we instead use the lifetime of the
422+
// reference to the `LateContext`
423+
//
424+
// It's possible for a `LateContext` that outlives the thread to exist e.g. with `Box::leak`, but
425+
// only `rustc_lint` can construct a `LateContext` and it does not do that
426+
unsafe { Some(std::mem::transmute::<&FormatArgs, &'a FormatArgs>(found)) }
427+
})
422428
}
423429

424430
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if

0 commit comments

Comments
 (0)