11use 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 } ;
33use clippy_utils:: { get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators} ;
44use if_chain:: if_chain;
5+ use rustc_ast:: { FormatArgsPiece , FormatTrait } ;
56use rustc_errors:: Applicability ;
67use rustc_hir:: { Expr , ExprKind , Impl , ImplItem , ImplItemKind , QPath } ;
78use rustc_lint:: { LateContext , LateLintPass } ;
89use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
10+ use rustc_span:: Span ;
911use rustc_span:: { sym, symbol:: kw, Symbol } ;
1012
1113declare_clippy_lint ! {
@@ -89,7 +91,7 @@ declare_clippy_lint! {
8991}
9092
9193#[ derive( Clone , Copy ) ]
92- struct FormatTrait {
94+ struct FormatTraitNames {
9395 /// e.g. `sym::Display`
9496 name : Symbol ,
9597 /// `f` in `fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}`
@@ -99,7 +101,7 @@ struct FormatTrait {
99101#[ derive( Default ) ]
100102pub struct FormatImpl {
101103 // Whether we are inside Display or Debug trait impl - None for neither
102- format_trait_impl : Option < FormatTrait > ,
104+ format_trait_impl : Option < FormatTraitNames > ,
103105}
104106
105107impl FormatImpl {
@@ -161,43 +163,57 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
161163 }
162164}
163165
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 ) {
165167 // 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) ;
175192 }
176- check_format_arg_self( cx, expr, & arg, impl_trait) ;
177193 }
178- }
194+ } ) ;
179195 }
180196}
181197
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 ) {
183199 // Handle multiple dereferencing of references e.g. &&self
184200 // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
185201 // 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) ;
187203 let map = cx. tcx . hir ( ) ;
188204 // Is the reference self?
189205 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;
191207 span_lint (
192208 cx,
193209 RECURSIVE_FORMAT_IMPL ,
194- expr . span ,
210+ span,
195211 & format ! ( "using `self` as `{name}` in `impl {name}` will cause infinite recursion" ) ,
196212 ) ;
197213 }
198214}
199215
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 ) {
201217 if_chain ! {
202218 if let Some ( macro_call) = root_macro_call_first_node( cx, expr) ;
203219 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:
227243 }
228244}
229245
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 > {
231247 if_chain ! {
232248 if impl_item. ident. name == sym:: fmt;
233249 if let ImplItemKind :: Fn ( _, body_id) = impl_item. kind;
@@ -241,7 +257,7 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
241257 . and_then( |param| param. pat. simple_ident( ) )
242258 . map( |ident| ident. name) ;
243259
244- Some ( FormatTrait {
260+ Some ( FormatTraitNames {
245261 name,
246262 formatter_name,
247263 } )
0 commit comments