@@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
77use rustc_lint:: { LateContext , LateLintPass } ;
88use rustc_session:: impl_lint_pass;
99use rustc_span:: symbol:: kw;
10- use rustc_span:: { sym, Span , Symbol } ;
10+ use rustc_span:: { sym, Symbol } ;
1111
1212declare_clippy_lint ! {
1313 /// ### What it does
@@ -119,123 +119,132 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
119119 }
120120
121121 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
123123 if is_format_trait_impl ( cx, impl_item) . is_some ( ) {
124124 self . format_trait_impl = None ;
125125 }
126126 }
127127
128128 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 ( ) ;
135138 }
136-
137- check_self_in_format_args ( cx, expr, format_trait_impl) ;
138- check_print_in_format_impl ( cx, expr, format_trait_impl) ;
139139 }
140140}
141141
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 ,
163146}
164147
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) ;
184199 }
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) ;
191200 }
192201 }
193202 }
194- }
195203
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+ }
211220 }
212- }
213221
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+ } ;
223231
224- let name = name. as_str ( ) . strip_suffix ( "_macro" ) . unwrap ( ) ;
232+ let name = name. as_str ( ) . strip_suffix ( "_macro" ) . unwrap ( ) ;
225233
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+ }
239248 }
240249}
241250
0 commit comments