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