Skip to content

Commit 1072d6e

Browse files
committed
teach clippy::author format args
1 parent 9283497 commit 1072d6e

File tree

2 files changed

+60
-14
lines changed

2 files changed

+60
-14
lines changed

clippy_lints/src/utils/author.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! A group of attributes that can be attached to Rust code in order
22
//! to generate a clippy lint detecting said code automatically.
33
4+
use clippy_utils::macros::{collect_format_args, is_format_macro, root_macro_call_first_node};
45
use clippy_utils::{get_attr, higher};
56
use rustc_ast::ast::{LitFloatType, LitKind};
67
use rustc_ast::LitIntType;
@@ -239,14 +240,14 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
239240
}
240241
}
241242

242-
fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) {
243+
fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(Binding<&T>)) {
243244
if slice.value.is_empty() {
244245
chain!(self, "{slice}.is_empty()");
245246
} else {
246247
chain!(self, "{slice}.len() == {}", slice.value.len());
247248
for (i, value) in slice.value.iter().enumerate() {
248249
let name = format!("{slice}[{i}]");
249-
f(&Binding { name, value });
250+
f(Binding { name, value });
250251
}
251252
}
252253
}
@@ -333,6 +334,24 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
333334

334335
#[allow(clippy::too_many_lines)]
335336
fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
337+
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr.value)
338+
&& is_format_macro(self.cx, macro_call.def_id)
339+
{
340+
let diag = self.cx.tcx.get_diagnostic_name(macro_call.def_id).unwrap();
341+
bind!(self, macro_call);
342+
chain!(self, "let Some({macro_call}) = macros::root_macro_call_first_node(cx, {expr})");
343+
chain!(self, "cx.tcx.is_diagnostic_item(sym::{diag}, {macro_call}.def_id)");
344+
let exprs = collect_format_args(self.cx, expr.value, macro_call.value.expn);
345+
let args = exprs.as_slice();
346+
bind!(self, args);
347+
chain!(self, "let {args} = macros::collect_format_args(cx, {expr}, {macro_call}.expn)");
348+
self.slice(args, |e| {
349+
let expr = Binding { name: e.name, value: *e.value };
350+
self.expr(&expr);
351+
});
352+
return;
353+
}
354+
336355
if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
337356
bind!(self, condition, body);
338357
chain!(
@@ -398,25 +417,25 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
398417
ExprKind::Array(elements) => {
399418
bind!(self, elements);
400419
kind!("Array({elements})");
401-
self.slice(elements, |e| self.expr(e));
420+
self.slice(elements, |e| self.expr(&e));
402421
},
403422
ExprKind::Call(func, args) => {
404423
bind!(self, func, args);
405424
kind!("Call({func}, {args})");
406425
self.expr(func);
407-
self.slice(args, |e| self.expr(e));
426+
self.slice(args, |e| self.expr(&e));
408427
},
409428
ExprKind::MethodCall(method_name, receiver, args, _) => {
410429
bind!(self, method_name, receiver, args);
411430
kind!("MethodCall({method_name}, {receiver}, {args}, _)");
412431
self.ident(field!(method_name.ident));
413432
self.expr(receiver);
414-
self.slice(args, |e| self.expr(e));
433+
self.slice(args, |e| self.expr(&e));
415434
},
416435
ExprKind::Tup(elements) => {
417436
bind!(self, elements);
418437
kind!("Tup({elements})");
419-
self.slice(elements, |e| self.expr(e));
438+
self.slice(elements, |e| self.expr(&e));
420439
},
421440
ExprKind::Binary(op, left, right) => {
422441
bind!(self, op, left, right);
@@ -469,7 +488,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
469488
bind!(self, scrutinee, arms);
470489
kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})");
471490
self.expr(scrutinee);
472-
self.slice(arms, |arm| self.arm(arm));
491+
self.slice(arms, |arm| self.arm(&arm));
473492
},
474493
ExprKind::Closure(&Closure {
475494
capture_clause,
@@ -593,7 +612,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
593612
}
594613

595614
fn block(&self, block: &Binding<&hir::Block<'_>>) {
596-
self.slice(field!(block.stmts), |stmt| self.stmt(stmt));
615+
self.slice(field!(block.stmts), |stmt| self.stmt(&stmt));
597616
self.option(field!(block.expr), "trailing_expr", |expr| {
598617
self.expr(expr);
599618
});
@@ -639,13 +658,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
639658
PatKind::Or(fields) => {
640659
bind!(self, fields);
641660
kind!("Or({fields})");
642-
self.slice(fields, |pat| self.pat(pat));
661+
self.slice(fields, |pat| self.pat(&pat));
643662
},
644663
PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
645664
bind!(self, qpath, fields);
646665
kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
647666
self.qpath(qpath);
648-
self.slice(fields, |pat| self.pat(pat));
667+
self.slice(fields, |pat| self.pat(&pat));
649668
},
650669
PatKind::Path(ref qpath) => {
651670
bind!(self, qpath);
@@ -655,7 +674,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
655674
PatKind::Tuple(fields, skip_pos) => {
656675
bind!(self, fields);
657676
kind!("Tuple({fields}, {skip_pos:?})");
658-
self.slice(fields, |field| self.pat(field));
677+
self.slice(fields, |field| self.pat(&field));
659678
},
660679
PatKind::Box(pat) => {
661680
bind!(self, pat);
@@ -683,8 +702,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
683702
opt_bind!(self, middle);
684703
kind!("Slice({start}, {middle}, {end})");
685704
middle.if_some(|p| self.pat(p));
686-
self.slice(start, |pat| self.pat(pat));
687-
self.slice(end, |pat| self.pat(pat));
705+
self.slice(start, |pat| self.pat(&pat));
706+
self.slice(end, |pat| self.pat(&pat));
688707
},
689708
}
690709
}

clippy_utils/src/macros.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::visitors::{for_each_expr, Descend};
44

55
use arrayvec::ArrayVec;
6-
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
6+
use rustc_ast::{FormatArgs, FormatArgsPiece, FormatArgument, FormatPlaceholder};
77
use rustc_data_structures::fx::FxHashMap;
88
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
99
use rustc_lint::LateContext;
@@ -425,6 +425,33 @@ pub fn find_format_arg_expr<'hir, 'ast>(
425425
.ok_or(&target.expr)
426426
}
427427

428+
/// Finds and extracts all format arguments from a format-like macro call.
429+
///
430+
/// ```ignore
431+
/// // vvvvv any format-like macro
432+
/// println!("Hello, {}!", "ferris")
433+
/// // ^^^^^^^^ returns these expressions
434+
/// ```
435+
pub fn collect_format_args<'hir>(
436+
cx: &LateContext<'_>,
437+
start: &'hir Expr<'hir>,
438+
expn_id: ExpnId,
439+
) -> Vec<&'hir Expr<'hir>> {
440+
let mut args = vec![];
441+
find_format_args(cx, start, expn_id, |format_args| {
442+
for piece in &format_args.template {
443+
if let FormatArgsPiece::Placeholder(placeholder) = piece
444+
&& let Ok(index) = placeholder.argument.index
445+
&& let Some(arg) = format_args.arguments.all_args().get(index)
446+
&& let Ok(arg_expr) = find_format_arg_expr(start, arg)
447+
{
448+
args.push(arg_expr);
449+
}
450+
}
451+
});
452+
args
453+
}
454+
428455
/// Span of the `:` and format specifiers
429456
///
430457
/// ```ignore

0 commit comments

Comments
 (0)