@@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
8
use rustc_session:: impl_lint_pass;
9
9
use rustc_span:: symbol:: kw;
10
- use rustc_span:: { sym, Span , Symbol } ;
10
+ use rustc_span:: { sym, Symbol } ;
11
11
12
12
declare_clippy_lint ! {
13
13
/// ### What it does
@@ -119,123 +119,132 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
119
119
}
120
120
121
121
fn check_impl_item_post ( & mut self , cx : & LateContext < ' _ > , impl_item : & ImplItem < ' _ > ) {
122
- // Assume no nested Impl of Debug and Display within eachother
122
+ // Assume no nested Impl of Debug and Display within each other
123
123
if is_format_trait_impl ( cx, impl_item) . is_some ( ) {
124
124
self . format_trait_impl = None ;
125
125
}
126
126
}
127
127
128
128
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
129
- let Some ( format_trait_impl) = self . format_trait_impl else {
130
- return ;
131
- } ;
132
-
133
- if format_trait_impl. name == sym:: Display {
134
- check_to_string_in_display ( cx, expr) ;
129
+ if let Some ( format_trait_impl) = self . format_trait_impl {
130
+ let linter = FormatImplExpr {
131
+ cx,
132
+ expr,
133
+ format_trait_impl,
134
+ } ;
135
+ linter. check_to_string_in_display ( ) ;
136
+ linter. check_self_in_format_args ( ) ;
137
+ linter. check_print_in_format_impl ( ) ;
135
138
}
136
-
137
- check_self_in_format_args ( cx, expr, format_trait_impl) ;
138
- check_print_in_format_impl ( cx, expr, format_trait_impl) ;
139
139
}
140
140
}
141
141
142
- fn check_to_string_in_display ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
143
- if let ExprKind :: MethodCall ( path, self_arg, ..) = expr. kind
144
- // Get the hir_id of the object we are calling the method on
145
- // Is the method to_string() ?
146
- && path. ident . name == sym:: to_string
147
- // Is the method a part of the ToString trait? (i.e. not to_string() implemented
148
- // separately)
149
- && let Some ( expr_def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
150
- && is_diag_trait_item ( cx, expr_def_id, sym:: ToString )
151
- // Is the method is called on self
152
- && let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = self_arg. kind
153
- && let [ segment] = path. segments
154
- && segment. ident . name == kw:: SelfLower
155
- {
156
- span_lint (
157
- cx,
158
- RECURSIVE_FORMAT_IMPL ,
159
- expr. span ,
160
- "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion" ,
161
- ) ;
162
- }
142
+ struct FormatImplExpr < ' a , ' tcx > {
143
+ cx : & ' a LateContext < ' tcx > ,
144
+ expr : & ' tcx Expr < ' tcx > ,
145
+ format_trait_impl : FormatTraitNames ,
163
146
}
164
147
165
- fn check_self_in_format_args < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , impl_trait : FormatTraitNames ) {
166
- // Check each arg in format calls - do we ever use Display on self (directly or via deref)?
167
- if let Some ( outer_macro) = root_macro_call_first_node ( cx, expr)
168
- && let macro_def_id = outer_macro. def_id
169
- && is_format_macro ( cx, macro_def_id)
170
- && let Some ( format_args) = find_format_args ( cx, expr, outer_macro. expn )
171
- {
172
- for piece in & format_args. template {
173
- if let FormatArgsPiece :: Placeholder ( placeholder) = piece
174
- && let trait_name = match placeholder. format_trait {
175
- FormatTrait :: Display => sym:: Display ,
176
- FormatTrait :: Debug => sym:: Debug ,
177
- FormatTrait :: LowerExp => sym ! ( LowerExp ) ,
178
- FormatTrait :: UpperExp => sym ! ( UpperExp ) ,
179
- FormatTrait :: Octal => sym ! ( Octal ) ,
180
- FormatTrait :: Pointer => sym:: Pointer ,
181
- FormatTrait :: Binary => sym ! ( Binary ) ,
182
- FormatTrait :: LowerHex => sym ! ( LowerHex ) ,
183
- FormatTrait :: UpperHex => sym ! ( UpperHex ) ,
148
+ impl < ' a , ' tcx > FormatImplExpr < ' a , ' tcx > {
149
+ fn check_to_string_in_display ( & self ) {
150
+ if self . format_trait_impl . name == sym:: Display
151
+ && let ExprKind :: MethodCall ( path, self_arg, ..) = self . expr . kind
152
+ // Get the hir_id of the object we are calling the method on
153
+ // Is the method to_string() ?
154
+ && path. ident . name == sym:: to_string
155
+ // Is the method a part of the ToString trait? (i.e. not to_string() implemented
156
+ // separately)
157
+ && let Some ( expr_def_id) = self . cx . typeck_results ( ) . type_dependent_def_id ( self . expr . hir_id )
158
+ && is_diag_trait_item ( self . cx , expr_def_id, sym:: ToString )
159
+ // Is the method is called on self
160
+ && let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = self_arg. kind
161
+ && let [ segment] = path. segments
162
+ && segment. ident . name == kw:: SelfLower
163
+ {
164
+ span_lint (
165
+ self . cx ,
166
+ RECURSIVE_FORMAT_IMPL ,
167
+ self . expr . span ,
168
+ "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion" ,
169
+ ) ;
170
+ }
171
+ }
172
+
173
+ fn check_self_in_format_args ( & self ) {
174
+ // Check each arg in format calls - do we ever use Display on self (directly or via deref)?
175
+ if let Some ( outer_macro) = root_macro_call_first_node ( self . cx , self . expr )
176
+ && let macro_def_id = outer_macro. def_id
177
+ && is_format_macro ( self . cx , macro_def_id)
178
+ && let Some ( format_args) = find_format_args ( self . cx , self . expr , outer_macro. expn )
179
+ {
180
+ for piece in & format_args. template {
181
+ if let FormatArgsPiece :: Placeholder ( placeholder) = piece
182
+ && let trait_name = match placeholder. format_trait {
183
+ FormatTrait :: Display => sym:: Display ,
184
+ FormatTrait :: Debug => sym:: Debug ,
185
+ FormatTrait :: LowerExp => sym ! ( LowerExp ) ,
186
+ FormatTrait :: UpperExp => sym ! ( UpperExp ) ,
187
+ FormatTrait :: Octal => sym ! ( Octal ) ,
188
+ FormatTrait :: Pointer => sym:: Pointer ,
189
+ FormatTrait :: Binary => sym ! ( Binary ) ,
190
+ FormatTrait :: LowerHex => sym ! ( LowerHex ) ,
191
+ FormatTrait :: UpperHex => sym ! ( UpperHex ) ,
192
+ }
193
+ && trait_name == self . format_trait_impl . name
194
+ && let Ok ( index) = placeholder. argument . index
195
+ && let Some ( arg) = format_args. arguments . all_args ( ) . get ( index)
196
+ && let Ok ( arg_expr) = find_format_arg_expr ( self . expr , arg)
197
+ {
198
+ self . check_format_arg_self ( arg_expr) ;
184
199
}
185
- && trait_name == impl_trait. name
186
- && let Ok ( index) = placeholder. argument . index
187
- && let Some ( arg) = format_args. arguments . all_args ( ) . get ( index)
188
- && let Ok ( arg_expr) = find_format_arg_expr ( expr, arg)
189
- {
190
- check_format_arg_self ( cx, expr. span , arg_expr, impl_trait) ;
191
200
}
192
201
}
193
202
}
194
- }
195
203
196
- fn check_format_arg_self ( cx : & LateContext < ' _ > , span : Span , arg : & Expr < ' _ > , impl_trait : FormatTraitNames ) {
197
- // Handle multiple dereferencing of references e.g. &&self
198
- // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
199
- // Since the argument to fmt is itself a reference: &self
200
- let reference = peel_ref_operators ( cx, arg) ;
201
- let map = cx. tcx . hir ( ) ;
202
- // Is the reference self?
203
- if path_to_local ( reference) . map ( |x| map. name ( x) ) == Some ( kw:: SelfLower ) {
204
- let FormatTraitNames { name, .. } = impl_trait;
205
- span_lint (
206
- cx,
207
- RECURSIVE_FORMAT_IMPL ,
208
- span,
209
- & format ! ( "using `self` as `{name}` in `impl {name}` will cause infinite recursion" ) ,
210
- ) ;
204
+ fn check_format_arg_self ( & self , arg : & Expr < ' _ > ) {
205
+ // Handle multiple dereferencing of references e.g. &&self
206
+ // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
207
+ // Since the argument to fmt is itself a reference: &self
208
+ let reference = peel_ref_operators ( self . cx , arg) ;
209
+ let map = self . cx . tcx . hir ( ) ;
210
+ // Is the reference self?
211
+ if path_to_local ( reference) . map ( |x| map. name ( x) ) == Some ( kw:: SelfLower ) {
212
+ let FormatTraitNames { name, .. } = self . format_trait_impl ;
213
+ span_lint (
214
+ self . cx ,
215
+ RECURSIVE_FORMAT_IMPL ,
216
+ self . expr . span ,
217
+ & format ! ( "using `self` as `{name}` in `impl {name}` will cause infinite recursion" ) ,
218
+ ) ;
219
+ }
211
220
}
212
- }
213
221
214
- fn check_print_in_format_impl ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , impl_trait : FormatTraitNames ) {
215
- if let Some ( macro_call) = root_macro_call_first_node ( cx, expr)
216
- && let Some ( name) = cx. tcx . get_diagnostic_name ( macro_call. def_id )
217
- {
218
- let replacement = match name {
219
- sym:: print_macro | sym:: eprint_macro => "write" ,
220
- sym:: println_macro | sym:: eprintln_macro => "writeln" ,
221
- _ => return ,
222
- } ;
222
+ fn check_print_in_format_impl ( & self ) {
223
+ if let Some ( macro_call) = root_macro_call_first_node ( self . cx , self . expr )
224
+ && let Some ( name) = self . cx . tcx . get_diagnostic_name ( macro_call. def_id )
225
+ {
226
+ let replacement = match name {
227
+ sym:: print_macro | sym:: eprint_macro => "write" ,
228
+ sym:: println_macro | sym:: eprintln_macro => "writeln" ,
229
+ _ => return ,
230
+ } ;
223
231
224
- let name = name. as_str ( ) . strip_suffix ( "_macro" ) . unwrap ( ) ;
232
+ let name = name. as_str ( ) . strip_suffix ( "_macro" ) . unwrap ( ) ;
225
233
226
- span_lint_and_sugg (
227
- cx,
228
- PRINT_IN_FORMAT_IMPL ,
229
- macro_call. span ,
230
- & format ! ( "use of `{name}!` in `{}` impl" , impl_trait. name) ,
231
- "replace with" ,
232
- if let Some ( formatter_name) = impl_trait. formatter_name {
233
- format ! ( "{replacement}!({formatter_name}, ..)" )
234
- } else {
235
- format ! ( "{replacement}!(..)" )
236
- } ,
237
- Applicability :: HasPlaceholders ,
238
- ) ;
234
+ span_lint_and_sugg (
235
+ self . cx ,
236
+ PRINT_IN_FORMAT_IMPL ,
237
+ macro_call. span ,
238
+ & format ! ( "use of `{name}!` in `{}` impl" , self . format_trait_impl. name) ,
239
+ "replace with" ,
240
+ if let Some ( formatter_name) = self . format_trait_impl . formatter_name {
241
+ format ! ( "{replacement}!({formatter_name}, ..)" )
242
+ } else {
243
+ format ! ( "{replacement}!(..)" )
244
+ } ,
245
+ Applicability :: HasPlaceholders ,
246
+ ) ;
247
+ }
239
248
}
240
249
}
241
250
0 commit comments