@@ -6,7 +6,6 @@ use crate::utils::{
66use if_chain:: if_chain;
77use rustc:: hir:: * ;
88use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintContext , LintPass } ;
9- use rustc:: ty;
109use rustc:: { declare_lint_pass, declare_tool_lint} ;
1110use rustc_errors:: Applicability ;
1211use syntax:: ast:: LitKind ;
@@ -39,56 +38,16 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
3938
4039impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for UselessFormat {
4140 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
42- if let Some ( span) = is_expn_of ( expr. span , "format" ) {
43- if in_macro_or_desugar ( span) {
44- return ;
45- }
46- match expr. node {
47- // `format!("{}", foo)` expansion
48- ExprKind :: Call ( ref fun, ref args) => {
49- if_chain ! {
50- if let ExprKind :: Path ( ref qpath) = fun. node;
51- if let Some ( fun_def_id) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
52- let new_v1 = match_def_path( cx, fun_def_id, & paths:: FMT_ARGUMENTS_NEWV1 ) ;
53- let new_v1_fmt = match_def_path( cx,
54- fun_def_id,
55- & paths:: FMT_ARGUMENTS_NEWV1FORMATTED
56- ) ;
57- if new_v1 || new_v1_fmt;
58- if check_single_piece( & args[ 0 ] ) ;
59- if let Some ( format_arg) = get_single_string_arg( cx, & args[ 1 ] ) ;
60- if new_v1 || check_unformatted( & args[ 2 ] ) ;
61- if let ExprKind :: AddrOf ( _, ref format_arg) = format_arg. node;
62- then {
63- let ( message, sugg) = if_chain! {
64- if let ExprKind :: MethodCall ( ref path, _, _) = format_arg. node;
65- if path. ident. as_interned_str( ) . as_symbol( ) == sym!( to_string) ;
66- then {
67- ( "`to_string()` is enough" ,
68- snippet( cx, format_arg. span, "<arg>" ) . to_string( ) )
69- } else {
70- ( "consider using .to_string()" ,
71- format!( "{}.to_string()" , snippet( cx, format_arg. span, "<arg>" ) ) )
72- }
73- } ;
41+ let span = match is_expn_of ( expr. span , "format" ) {
42+ Some ( s) if !in_macro_or_desugar ( s) => s,
43+ _ => return ,
44+ } ;
7445
75- span_useless_format( cx, span, message, sugg) ;
76- }
77- }
78- } ,
79- // `format!("foo")` expansion contains `match () { () => [], }`
80- ExprKind :: Match ( ref matchee, _, _) => {
81- if let ExprKind :: Tup ( ref tup) = matchee. node {
82- if tup. is_empty ( ) {
83- let actual_snippet = snippet ( cx, expr. span , "<expr>" ) . to_string ( ) ;
84- let actual_snippet = actual_snippet. replace ( "{{}}" , "{}" ) ;
85- let sugg = format ! ( "{}.to_string()" , actual_snippet) ;
86- span_useless_format ( cx, span, "consider using .to_string()" , sugg) ;
87- }
88- }
89- } ,
90- _ => ( ) ,
91- }
46+ // Operate on the only argument of `alloc::fmt::format`.
47+ if let Some ( sugg) = on_new_v1 ( cx, expr) {
48+ span_useless_format ( cx, span, "consider using .to_string()" , sugg) ;
49+ } else if let Some ( sugg) = on_new_v1_fmt ( cx, expr) {
50+ span_useless_format ( cx, span, "consider using .to_string()" , sugg) ;
9251 }
9352 }
9453}
@@ -112,56 +71,105 @@ fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg:
11271 } ) ;
11372}
11473
115- /// Checks if the expressions matches `&[""]`
116- fn check_single_piece ( expr : & Expr ) -> bool {
74+ fn on_argumentv1_new < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr , arms : & ' a [ Arm ] ) -> Option < String > {
11775 if_chain ! {
118- if let ExprKind :: AddrOf ( _, ref expr) = expr. node; // &[""]
119- if let ExprKind :: Array ( ref exprs) = expr. node; // [""]
120- if exprs. len( ) == 1 ;
121- if let ExprKind :: Lit ( ref lit) = exprs[ 0 ] . node;
122- if let LitKind :: Str ( ref lit, _) = lit. node;
76+ if let ExprKind :: AddrOf ( _, ref format_args) = expr. node;
77+ if let ExprKind :: Array ( ref elems) = arms[ 0 ] . body. node;
78+ if elems. len( ) == 1 ;
79+ if let ExprKind :: Call ( ref fun, ref args) = elems[ 0 ] . node;
80+ if let ExprKind :: Path ( ref qpath) = fun. node;
81+ if let Some ( did) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
82+ if match_def_path( cx, did, & paths:: FMT_ARGUMENTV1_NEW ) ;
83+ // matches `core::fmt::Display::fmt`
84+ if args. len( ) == 2 ;
85+ if let ExprKind :: Path ( ref qpath) = args[ 1 ] . node;
86+ if let Some ( did) = resolve_node( cx, qpath, args[ 1 ] . hir_id) . opt_def_id( ) ;
87+ if match_def_path( cx, did, & paths:: DISPLAY_FMT_METHOD ) ;
88+ if arms[ 0 ] . pats. len( ) == 1 ;
89+ // check `(arg0,)` in match block
90+ if let PatKind :: Tuple ( ref pats, None ) = arms[ 0 ] . pats[ 0 ] . node;
91+ if pats. len( ) == 1 ;
12392 then {
124- return lit. as_str( ) . is_empty( ) ;
93+ if let ExprKind :: Lit ( ref lit) = format_args. node {
94+ if let LitKind :: Str ( ref s, _) = lit. node {
95+ let snip = s. as_str( ) . replace( "{{}}" , "{}" ) ;
96+ let sugg = format!( "\" {}\" .to_string()" , snip) ;
97+ return Some ( sugg) ;
98+ }
99+ return None ;
100+ } else {
101+ let snip = snippet( cx, format_args. span, "<arg>" ) ;
102+ if let ExprKind :: MethodCall ( ref path, _, _) = format_args. node {
103+ if path. ident. name == sym!( to_string) {
104+ return Some ( format!( "{}" , snip) ) ;
105+ }
106+ }
107+ return Some ( format!( "{}.to_string()" , snip) ) ;
108+ }
125109 }
126110 }
127-
128- false
111+ None
129112}
130113
131- /// Checks if the expressions matches
132- /// ```rust,ignore
133- /// &match (&"arg",) {
134- /// (__arg0,) => [::std::fmt::ArgumentV1::new(__arg0,
135- /// ::std::fmt::Display::fmt)],
136- /// }
137- /// ```
138- /// and that the type of `__arg0` is `&str` or `String`,
139- /// then returns the span of first element of the matched tuple.
140- fn get_single_string_arg < ' a > ( cx : & LateContext < ' _ , ' _ > , expr : & ' a Expr ) -> Option < & ' a Expr > {
114+ fn on_new_v1 < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) -> Option < String > {
141115 if_chain ! {
142- if let ExprKind :: AddrOf ( _, ref expr) = expr. node;
143- if let ExprKind :: Match ( ref match_expr, ref arms, _) = expr. node;
144- if arms. len( ) == 1 ;
145- if arms[ 0 ] . pats. len( ) == 1 ;
146- if let PatKind :: Tuple ( ref pat, None ) = arms[ 0 ] . pats[ 0 ] . node;
147- if pat. len( ) == 1 ;
148- if let ExprKind :: Array ( ref exprs) = arms[ 0 ] . body. node;
149- if exprs. len( ) == 1 ;
150- if let ExprKind :: Call ( _, ref args) = exprs[ 0 ] . node;
116+ if let ExprKind :: Call ( ref fun, ref args) = expr. node;
151117 if args. len( ) == 2 ;
152- if let ExprKind :: Path ( ref qpath) = args[ 1 ] . node;
153- if let Some ( fun_def_id) = resolve_node( cx, qpath, args[ 1 ] . hir_id) . opt_def_id( ) ;
154- if match_def_path( cx, fun_def_id, & paths:: DISPLAY_FMT_METHOD ) ;
118+ if let ExprKind :: Path ( ref qpath) = fun. node;
119+ if let Some ( did) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
120+ if match_def_path( cx, did, & paths:: FMT_ARGUMENTS_NEW_V1 ) ;
121+ // Argument 1 in `new_v1()`
122+ if let ExprKind :: AddrOf ( _, ref arr) = args[ 0 ] . node;
123+ if let ExprKind :: Array ( ref pieces) = arr. node;
124+ if pieces. len( ) == 1 ;
125+ if let ExprKind :: Lit ( ref lit) = pieces[ 0 ] . node;
126+ if let LitKind :: Str ( ref s, _) = lit. node;
127+ // Argument 2 in `new_v1()`
128+ if let ExprKind :: AddrOf ( _, ref arg1) = args[ 1 ] . node;
129+ if let ExprKind :: Match ( ref matchee, ref arms, MatchSource :: Normal ) = arg1. node;
130+ if arms. len( ) == 1 ;
131+ if let ExprKind :: Tup ( ref tup) = matchee. node;
155132 then {
156- let ty = walk_ptrs_ty( cx. tables. pat_ty( & pat[ 0 ] ) ) ;
157- if ty. sty == ty:: Str || match_type( cx, ty, & paths:: STRING ) {
158- if let ExprKind :: Tup ( ref values) = match_expr. node {
159- return Some ( & values[ 0 ] ) ;
133+ // `format!("foo")` expansion contains `match () { () => [], }`
134+ if tup. is_empty( ) {
135+ let snip = s. as_str( ) . replace( "{{}}" , "{}" ) ;
136+ let sugg = format!( "\" {}\" .to_string()" , snip) ;
137+ return Some ( sugg) ;
138+ } else {
139+ if s. as_str( ) . is_empty( ) {
140+ return on_argumentv1_new( cx, & tup[ 0 ] , arms) ;
141+ } else {
142+ return None ;
160143 }
161144 }
162145 }
163146 }
147+ None
148+ }
164149
150+ fn on_new_v1_fmt < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) -> Option < String > {
151+ if_chain ! {
152+ if let ExprKind :: Call ( ref fun, ref args) = expr. node;
153+ if args. len( ) == 3 ;
154+ if let ExprKind :: Path ( ref qpath) = fun. node;
155+ if let Some ( did) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
156+ if match_def_path( cx, did, & paths:: FMT_ARGUMENTS_NEW_V1_FORMATTED ) ;
157+ if check_unformatted( & args[ 2 ] ) ;
158+ // Argument 1 in `new_v1_formatted()`
159+ if let ExprKind :: AddrOf ( _, ref arr) = args[ 0 ] . node;
160+ if let ExprKind :: Array ( ref pieces) = arr. node;
161+ if pieces. len( ) == 1 ;
162+ if let ExprKind :: Lit ( ref lit) = pieces[ 0 ] . node;
163+ if let LitKind :: Str ( ..) = lit. node;
164+ // Argument 2 in `new_v1_formatted()`
165+ if let ExprKind :: AddrOf ( _, ref arg1) = args[ 1 ] . node;
166+ if let ExprKind :: Match ( ref matchee, ref arms, MatchSource :: Normal ) = arg1. node;
167+ if arms. len( ) == 1 ;
168+ if let ExprKind :: Tup ( ref tup) = matchee. node;
169+ then {
170+ return on_argumentv1_new( cx, & tup[ 0 ] , arms) ;
171+ }
172+ }
165173 None
166174}
167175
@@ -170,6 +178,7 @@ fn get_single_string_arg<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option
170178/// &[_ {
171179/// format: _ {
172180/// width: _::Implied,
181+ /// precision: _::Implied,
173182/// ...
174183/// },
175184/// ...,
@@ -180,15 +189,17 @@ fn check_unformatted(expr: &Expr) -> bool {
180189 if let ExprKind :: AddrOf ( _, ref expr) = expr. node;
181190 if let ExprKind :: Array ( ref exprs) = expr. node;
182191 if exprs. len( ) == 1 ;
192+ // struct `core::fmt::rt::v1::Argument`
183193 if let ExprKind :: Struct ( _, ref fields, _) = exprs[ 0 ] . node;
184194 if let Some ( format_field) = fields. iter( ) . find( |f| f. ident. name == sym!( format) ) ;
195+ // struct `core::fmt::rt::v1::FormatSpec`
185196 if let ExprKind :: Struct ( _, ref fields, _) = format_field. expr. node;
186- if let Some ( width_field) = fields. iter( ) . find( |f| f. ident. name == sym!( width) ) ;
187- if let ExprKind :: Path ( ref width_qpath) = width_field. expr. node;
188- if last_path_segment( width_qpath) . ident. name == sym!( Implied ) ;
189197 if let Some ( precision_field) = fields. iter( ) . find( |f| f. ident. name == sym!( precision) ) ;
190198 if let ExprKind :: Path ( ref precision_path) = precision_field. expr. node;
191199 if last_path_segment( precision_path) . ident. name == sym!( Implied ) ;
200+ if let Some ( width_field) = fields. iter( ) . find( |f| f. ident. name == sym!( width) ) ;
201+ if let ExprKind :: Path ( ref width_qpath) = width_field. expr. node;
202+ if last_path_segment( width_qpath) . ident. name == sym!( Implied ) ;
192203 then {
193204 return true ;
194205 }
0 commit comments