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