@@ -6,7 +6,6 @@ use crate::utils::{
6
6
use if_chain:: if_chain;
7
7
use rustc:: hir:: * ;
8
8
use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintContext , LintPass } ;
9
- use rustc:: ty;
10
9
use rustc:: { declare_lint_pass, declare_tool_lint} ;
11
10
use rustc_errors:: Applicability ;
12
11
use syntax:: ast:: LitKind ;
@@ -39,56 +38,16 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
39
38
40
39
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for UselessFormat {
41
40
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
+ } ;
74
45
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) ;
92
51
}
93
52
}
94
53
}
@@ -112,56 +71,105 @@ fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg:
112
71
} ) ;
113
72
}
114
73
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 > {
117
75
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 ;
123
92
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
+ }
125
109
}
126
110
}
127
-
128
- false
111
+ None
129
112
}
130
113
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 > {
141
115
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;
151
117
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;
155
132
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 ;
160
143
}
161
144
}
162
145
}
163
146
}
147
+ None
148
+ }
164
149
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
+ }
165
173
None
166
174
}
167
175
@@ -170,6 +178,7 @@ fn get_single_string_arg<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option
170
178
/// &[_ {
171
179
/// format: _ {
172
180
/// width: _::Implied,
181
+ /// precision: _::Implied,
173
182
/// ...
174
183
/// },
175
184
/// ...,
@@ -180,15 +189,17 @@ fn check_unformatted(expr: &Expr) -> bool {
180
189
if let ExprKind :: AddrOf ( _, ref expr) = expr. node;
181
190
if let ExprKind :: Array ( ref exprs) = expr. node;
182
191
if exprs. len( ) == 1 ;
192
+ // struct `core::fmt::rt::v1::Argument`
183
193
if let ExprKind :: Struct ( _, ref fields, _) = exprs[ 0 ] . node;
184
194
if let Some ( format_field) = fields. iter( ) . find( |f| f. ident. name == sym!( format) ) ;
195
+ // struct `core::fmt::rt::v1::FormatSpec`
185
196
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 ) ;
189
197
if let Some ( precision_field) = fields. iter( ) . find( |f| f. ident. name == sym!( precision) ) ;
190
198
if let ExprKind :: Path ( ref precision_path) = precision_field. expr. node;
191
199
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 ) ;
192
203
then {
193
204
return true ;
194
205
}
0 commit comments