1
1
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
2
- use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn };
2
+ use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node };
3
3
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
4
4
use if_chain::if_chain;
5
+ use rustc_ast::{FormatArgsPiece, FormatTrait};
5
6
use rustc_errors::Applicability;
6
7
use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
7
8
use rustc_lint::{LateContext, LateLintPass};
8
9
use rustc_session::{declare_tool_lint, impl_lint_pass};
10
+ use rustc_span::Span;
9
11
use rustc_span::{sym, symbol::kw, Symbol};
10
12
11
13
declare_clippy_lint! {
@@ -89,7 +91,7 @@ declare_clippy_lint! {
89
91
}
90
92
91
93
#[derive(Clone, Copy)]
92
- struct FormatTrait {
94
+ struct FormatTraitNames {
93
95
/// e.g. `sym::Display`
94
96
name: Symbol,
95
97
/// `f` in `fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}`
@@ -99,7 +101,7 @@ struct FormatTrait {
99
101
#[derive(Default)]
100
102
pub struct FormatImpl {
101
103
// Whether we are inside Display or Debug trait impl - None for neither
102
- format_trait_impl: Option<FormatTrait >,
104
+ format_trait_impl: Option<FormatTraitNames >,
103
105
}
104
106
105
107
impl FormatImpl {
@@ -161,43 +163,57 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
161
163
}
162
164
}
163
165
164
- fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTrait ) {
166
+ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames ) {
165
167
// Check each arg in format calls - do we ever use Display on self (directly or via deref)?
166
- if_chain! {
167
- if let Some(outer_macro) = root_macro_call_first_node(cx, expr);
168
- if let macro_def_id = outer_macro.def_id;
169
- if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
170
- if is_format_macro(cx, macro_def_id);
171
- then {
172
- for arg in format_args.args {
173
- if arg.format.r#trait != impl_trait.name {
174
- continue;
168
+ if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
169
+ && let macro_def_id = outer_macro.def_id
170
+ && is_format_macro(cx, macro_def_id)
171
+ {
172
+ find_format_args(cx, expr, outer_macro.expn, |format_args| {
173
+ for piece in &format_args.template {
174
+ if let FormatArgsPiece::Placeholder(placeholder) = piece
175
+ && let trait_name = match placeholder.format_trait {
176
+ FormatTrait::Display => sym::Display,
177
+ FormatTrait::Debug => sym::Debug,
178
+ FormatTrait::LowerExp => sym!(LowerExp),
179
+ FormatTrait::UpperExp => sym!(UpperExp),
180
+ FormatTrait::Octal => sym!(Octal),
181
+ FormatTrait::Pointer => sym::Pointer,
182
+ FormatTrait::Binary => sym!(Binary),
183
+ FormatTrait::LowerHex => sym!(LowerHex),
184
+ FormatTrait::UpperHex => sym!(UpperHex),
185
+ }
186
+ && trait_name == impl_trait.name
187
+ && let Ok(index) = placeholder.argument.index
188
+ && let Some(arg) = format_args.arguments.all_args().get(index)
189
+ && let Ok(arg_expr) = find_format_arg_expr(expr, arg)
190
+ {
191
+ check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
175
192
}
176
- check_format_arg_self(cx, expr, &arg, impl_trait);
177
193
}
178
- }
194
+ });
179
195
}
180
196
}
181
197
182
- fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_> , arg: &FormatArg <'_>, impl_trait: FormatTrait ) {
198
+ fn check_format_arg_self(cx: &LateContext<'_>, span: Span , arg: &Expr <'_>, impl_trait: FormatTraitNames ) {
183
199
// Handle multiple dereferencing of references e.g. &&self
184
200
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
185
201
// Since the argument to fmt is itself a reference: &self
186
- let reference = peel_ref_operators(cx, arg.param.value );
202
+ let reference = peel_ref_operators(cx, arg);
187
203
let map = cx.tcx.hir();
188
204
// Is the reference self?
189
205
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
190
- let FormatTrait { name, .. } = impl_trait;
206
+ let FormatTraitNames { name, .. } = impl_trait;
191
207
span_lint(
192
208
cx,
193
209
RECURSIVE_FORMAT_IMPL,
194
- expr. span,
210
+ span,
195
211
&format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
196
212
);
197
213
}
198
214
}
199
215
200
- fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTrait ) {
216
+ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames ) {
201
217
if_chain! {
202
218
if let Some(macro_call) = root_macro_call_first_node(cx, expr);
203
219
if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id);
@@ -227,7 +243,7 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
227
243
}
228
244
}
229
245
230
- fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTrait > {
246
+ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames > {
231
247
if_chain! {
232
248
if impl_item.ident.name == sym::fmt;
233
249
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
@@ -241,7 +257,7 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
241
257
.and_then(|param| param.pat.simple_ident())
242
258
.map(|ident| ident.name);
243
259
244
- Some(FormatTrait {
260
+ Some(FormatTraitNames {
245
261
name,
246
262
formatter_name,
247
263
})
0 commit comments