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