@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
22use clippy_utils:: higher:: { FormatArgsArg , FormatArgsExpn , FormatExpn } ;
33use clippy_utils:: source:: snippet_opt;
44use clippy_utils:: ty:: implements_trait;
5- use clippy_utils:: { get_trait_def_id , is_diag_trait_item, match_def_path, paths} ;
5+ use clippy_utils:: { is_diag_trait_item, match_def_path, paths} ;
66use if_chain:: if_chain;
77use rustc_errors:: Applicability ;
88use rustc_hir:: { Expr , ExprKind } ;
@@ -64,18 +64,18 @@ declare_clippy_lint! {
6464declare_lint_pass ! ( FormatArgs => [ FORMAT_IN_FORMAT_ARGS , TO_STRING_IN_FORMAT_ARGS ] ) ;
6565
6666const FORMAT_MACROS : & [ & [ & str ] ] = & [
67- & [ "alloc" , "macros" , "format" ] ,
68- & [ "core" , "macros" , "assert_eq" ] ,
69- & [ "core" , "macros" , "assert_ne" ] ,
70- & [ "core" , "macros" , "write" ] ,
71- & [ "core" , "macros" , "writeln" ] ,
72- & [ "core" , "macros" , "builtin" , "assert" ] ,
73- & [ "core" , "macros" , "builtin" , "format_args" ] ,
74- & [ "std" , "macros" , "eprint" ] ,
75- & [ "std" , "macros" , "eprintln" ] ,
76- & [ "std" , "macros" , "panic" ] ,
77- & [ "std" , "macros" , "print" ] ,
78- & [ "std" , "macros" , "println" ] ,
67+ & paths :: FORMAT_MACRO ,
68+ & paths :: FORMAT_ARGS_MACRO ,
69+ & paths :: ASSERT_EQ_MACRO ,
70+ & paths :: ASSERT_MACRO ,
71+ & paths :: ASSERT_NE_MACRO ,
72+ & paths :: EPRINT_MACRO ,
73+ & paths :: EPRINTLN_MACRO ,
74+ & paths :: PANIC_MACRO ,
75+ & paths :: PRINT_MACRO ,
76+ & paths :: PRINTLN_MACRO ,
77+ & paths :: WRITE_MACRO ,
78+ & paths :: WRITELN_MACRO ,
7979] ;
8080
8181impl < ' tcx > LateLintPass < ' tcx > for FormatArgs {
@@ -116,49 +116,50 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
116116}
117117
118118fn check_format_in_format_args ( cx : & LateContext < ' _ > , call_site : Span , name : Symbol , arg : & FormatArgsArg < ' _ > ) {
119- if FormatExpn :: parse ( arg. value ) . is_some ( ) {
120- span_lint_and_then (
121- cx,
122- FORMAT_IN_FORMAT_ARGS ,
123- trim_semicolon ( cx, call_site) ,
124- & format ! ( "`format!` in `{}!` args" , name) ,
125- |diag| {
126- diag. help ( & format ! (
127- "combine the `format!(..)` arguments with the outer `{}!(..)` call" ,
128- name
129- ) ) ;
130- diag. help ( "or consider changing `format!` to `format_args!`" ) ;
131- } ,
132- ) ;
119+ if_chain ! {
120+ if FormatExpn :: parse( arg. value) . is_some( ) ;
121+ if !arg. value. span. ctxt( ) . outer_expn_data( ) . call_site. from_expansion( ) ;
122+ then {
123+ span_lint_and_then(
124+ cx,
125+ FORMAT_IN_FORMAT_ARGS ,
126+ trim_semicolon( cx, call_site) ,
127+ & format!( "`format!` in `{}!` args" , name) ,
128+ |diag| {
129+ diag. help( & format!(
130+ "combine the `format!(..)` arguments with the outer `{}!(..)` call" ,
131+ name
132+ ) ) ;
133+ diag. help( "or consider changing `format!` to `format_args!`" ) ;
134+ } ,
135+ ) ;
136+ }
133137 }
134138}
135139
136140fn check_to_string_in_format_args < ' tcx > ( cx : & LateContext < ' tcx > , name : Symbol , arg : & FormatArgsArg < ' tcx > ) {
137141 let value = arg. value ;
138142 if_chain ! {
143+ if !value. span. from_expansion( ) ;
139144 if let ExprKind :: MethodCall ( _, _, [ receiver] , _) = value. kind;
140145 if let Some ( method_def_id) = cx. typeck_results( ) . type_dependent_def_id( value. hir_id) ;
141146 if is_diag_trait_item( cx, method_def_id, sym:: ToString ) ;
142147 let receiver_ty = cx. typeck_results( ) . expr_ty( receiver) ;
143148 if let Some ( display_trait_id) = cx. tcx. get_diagnostic_item( sym:: Display ) ;
144- if let Some ( value_snippet) = snippet_opt( cx, value. span) ;
145- if let Some ( dot) = value_snippet. rfind( '.' ) ;
146149 if let Some ( receiver_snippet) = snippet_opt( cx, receiver. span) ;
147150 then {
148- let ( n_derefs, target) = count_derefs(
151+ let ( n_overloaded_derefs, target) = count_overloaded_derefs(
152+ 0 ,
149153 0 ,
150154 receiver_ty,
151155 & mut cx. typeck_results( ) . expr_adjustments( receiver) . iter( ) ,
152156 ) ;
153157 if implements_trait( cx, target, display_trait_id, & [ ] ) {
154- if n_derefs == 0 {
155- let span = value. span. with_lo(
156- value. span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) )
157- ) ;
158+ if n_overloaded_derefs == 0 {
158159 span_lint_and_sugg(
159160 cx,
160161 TO_STRING_IN_FORMAT_ARGS ,
161- span,
162+ value . span. with_lo ( receiver . span . hi ( ) ) ,
162163 & format!( "`to_string` applied to a type that implements `Display` in `{}!` args" , name) ,
163164 "remove this" ,
164165 String :: new( ) ,
@@ -171,7 +172,7 @@ fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, ar
171172 value. span,
172173 & format!( "`to_string` applied to a type that implements `Display` in `{}!` args" , name) ,
173174 "use this" ,
174- format!( "{:*>width$}{}" , "" , receiver_snippet, width = n_derefs ) ,
175+ format!( "{:*>width$}{}" , "" , receiver_snippet, width = n_overloaded_derefs ) ,
175176 Applicability :: MachineApplicable ,
176177 ) ;
177178 }
@@ -195,17 +196,31 @@ fn trim_semicolon(cx: &LateContext<'_>, span: Span) -> Span {
195196 } )
196197}
197198
198- fn count_derefs < ' tcx , I > ( n : usize , ty : Ty < ' tcx > , iter : & mut I ) -> ( usize , Ty < ' tcx > )
199+ fn count_overloaded_derefs < ' tcx , I > (
200+ n_total : usize ,
201+ n_overloaded : usize ,
202+ ty : Ty < ' tcx > ,
203+ iter : & mut I ,
204+ ) -> ( usize , Ty < ' tcx > )
199205where
200206 I : Iterator < Item = & ' tcx Adjustment < ' tcx > > ,
201207{
202208 if let Some ( Adjustment {
203- kind : Adjust :: Deref ( Some ( _ ) ) ,
209+ kind : Adjust :: Deref ( overloaded_deref ) ,
204210 target,
205211 } ) = iter. next ( )
206212 {
207- count_derefs ( n + 1 , target, iter)
213+ count_overloaded_derefs (
214+ n_total + 1 ,
215+ if overloaded_deref. is_some ( ) {
216+ n_total + 1
217+ } else {
218+ n_overloaded
219+ } ,
220+ target,
221+ iter,
222+ )
208223 } else {
209- ( n , ty)
224+ ( n_overloaded , ty)
210225 }
211226}
0 commit comments