1
- use clippy_utils:: diagnostics:: span_lint_and_then;
2
- use clippy_utils:: paths;
3
- use clippy_utils:: source:: { snippet, snippet_opt} ;
1
+ use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
+ use clippy_utils:: higher:: FormatExpn ;
3
+ use clippy_utils:: last_path_segment;
4
+ use clippy_utils:: source:: { snippet_opt, snippet_with_applicability} ;
4
5
use clippy_utils:: sugg:: Sugg ;
5
- use clippy_utils:: ty:: is_type_diagnostic_item;
6
- use clippy_utils:: { is_expn_of, last_path_segment, match_def_path, match_function_call} ;
7
6
use if_chain:: if_chain;
8
- use rustc_ast:: ast:: LitKind ;
9
7
use rustc_errors:: Applicability ;
10
- use rustc_hir:: { Arm , BorrowKind , Expr , ExprKind , MatchSource , PatKind } ;
11
- use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
8
+ use rustc_hir:: { BorrowKind , Expr , ExprKind , QPath } ;
9
+ use rustc_lint:: { LateContext , LateLintPass } ;
10
+ use rustc_middle:: ty;
12
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
- use rustc_span:: source_map :: Span ;
14
- use rustc_span:: sym;
12
+ use rustc_span:: symbol :: kw ;
13
+ use rustc_span:: { sym, Span } ;
15
14
16
15
declare_clippy_lint ! {
17
16
/// **What it does:** Checks for the use of `format!("string literal with no
@@ -44,130 +43,78 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
44
43
45
44
impl < ' tcx > LateLintPass < ' tcx > for UselessFormat {
46
45
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
47
- let span = match is_expn_of ( expr. span , "format" ) {
48
- Some ( s ) if !s . from_expansion ( ) => s ,
46
+ let FormatExpn { call_site , format_args } = match FormatExpn :: parse ( expr) {
47
+ Some ( e ) if !e . call_site . from_expansion ( ) => e ,
49
48
_ => return ,
50
49
} ;
51
50
52
- // Operate on the only argument of `alloc::fmt::format`.
53
- if let Some ( sugg) = on_new_v1 ( cx, expr) {
54
- span_useless_format ( cx, span, "consider using `.to_string()`" , sugg) ;
55
- } else if let Some ( sugg) = on_new_v1_fmt ( cx, expr) {
56
- span_useless_format ( cx, span, "consider using `.to_string()`" , sugg) ;
57
- }
58
- }
59
- }
60
-
61
- fn span_useless_format < T : LintContext > ( cx : & T , span : Span , help : & str , mut sugg : String ) {
62
- let to_replace = span. source_callsite ( ) ;
63
-
64
- // The callsite span contains the statement semicolon for some reason.
65
- let snippet = snippet ( cx, to_replace, ".." ) ;
66
- if snippet. ends_with ( ';' ) {
67
- sugg. push ( ';' ) ;
68
- }
69
-
70
- span_lint_and_then ( cx, USELESS_FORMAT , span, "useless use of `format!`" , |diag| {
71
- diag. span_suggestion (
72
- to_replace,
73
- help,
74
- sugg,
75
- Applicability :: MachineApplicable , // snippet
76
- ) ;
77
- } ) ;
78
- }
79
-
80
- fn on_argumentv1_new < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , arms : & ' tcx [ Arm < ' _ > ] ) -> Option < String > {
81
- if_chain ! {
82
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, format_args) = expr. kind;
83
- if let ExprKind :: Array ( elems) = arms[ 0 ] . body. kind;
84
- if elems. len( ) == 1 ;
85
- if let Some ( args) = match_function_call( cx, & elems[ 0 ] , & paths:: FMT_ARGUMENTV1_NEW ) ;
86
- // matches `core::fmt::Display::fmt`
87
- if args. len( ) == 2 ;
88
- if let ExprKind :: Path ( ref qpath) = args[ 1 ] . kind;
89
- if let Some ( did) = cx. qpath_res( qpath, args[ 1 ] . hir_id) . opt_def_id( ) ;
90
- if match_def_path( cx, did, & paths:: DISPLAY_FMT_METHOD ) ;
91
- // check `(arg0,)` in match block
92
- if let PatKind :: Tuple ( pats, None ) = arms[ 0 ] . pat. kind;
93
- if pats. len( ) == 1 ;
94
- then {
95
- let ty = cx. typeck_results( ) . pat_ty( pats[ 0 ] ) . peel_refs( ) ;
96
- if * ty. kind( ) != rustc_middle:: ty:: Str && !is_type_diagnostic_item( cx, ty, sym:: string_type) {
97
- return None ;
98
- }
99
- if let ExprKind :: Lit ( ref lit) = format_args. kind {
100
- if let LitKind :: Str ( ref s, _) = lit. node {
101
- return Some ( format!( "{:?}.to_string()" , s. as_str( ) ) ) ;
51
+ let mut applicability = Applicability :: MachineApplicable ;
52
+ if format_args. value_args . is_empty ( ) {
53
+ if_chain ! {
54
+ if let [ e] = & * format_args. format_string_parts;
55
+ if let ExprKind :: Lit ( lit) = & e. kind;
56
+ if let Some ( s_src) = snippet_opt( cx, lit. span) ;
57
+ then {
58
+ // Simulate macro expansion, converting {{ and }} to { and }.
59
+ let s_expand = s_src. replace( "{{" , "{" ) . replace( "}}" , "}" ) ;
60
+ let sugg = format!( "{}.to_string()" , s_expand) ;
61
+ span_useless_format( cx, call_site, sugg, applicability) ;
102
62
}
103
- } else {
104
- let sugg = Sugg :: hir( cx, format_args, "<arg>" ) ;
105
- if let ExprKind :: MethodCall ( path, _, _, _) = format_args. kind {
106
- if path. ident. name == sym!( to_string) {
107
- return Some ( format!( "{}" , sugg) ) ;
108
- }
109
- } else if let ExprKind :: Binary ( ..) = format_args. kind {
110
- return Some ( format!( "{}" , sugg) ) ;
63
+ }
64
+ } else if let [ value] = * format_args. value_args {
65
+ if_chain ! {
66
+ if format_args. format_string_symbols == [ kw:: Empty ] ;
67
+ if match cx. typeck_results( ) . expr_ty( value) . peel_refs( ) . kind( ) {
68
+ ty:: Adt ( adt, _) => cx. tcx. is_diagnostic_item( sym:: string_type, adt. did) ,
69
+ ty:: Str => true ,
70
+ _ => false ,
71
+ } ;
72
+ if format_args. args. iter( ) . all( |e| is_display_arg( e) ) ;
73
+ if format_args. fmt_expr. map_or( true , |e| check_unformatted( e) ) ;
74
+ then {
75
+ let is_new_string = match value. kind {
76
+ ExprKind :: Binary ( ..) => true ,
77
+ ExprKind :: MethodCall ( path, ..) => path. ident. name. as_str( ) == "to_string" ,
78
+ _ => false ,
79
+ } ;
80
+ let sugg = if is_new_string {
81
+ snippet_with_applicability( cx, value. span, ".." , & mut applicability) . into_owned( )
82
+ } else {
83
+ let sugg = Sugg :: hir_with_applicability( cx, value, "<arg>" , & mut applicability) ;
84
+ format!( "{}.to_string()" , sugg. maybe_par( ) )
85
+ } ;
86
+ span_useless_format( cx, call_site, sugg, applicability) ;
111
87
}
112
- return Some ( format!( "{}.to_string()" , sugg. maybe_par( ) ) ) ;
113
88
}
114
- }
89
+ } ;
115
90
}
116
- None
117
91
}
118
92
119
- fn on_new_v1 < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < String > {
120
- if_chain ! {
121
- if let Some ( args) = match_function_call( cx, expr, & paths:: FMT_ARGUMENTS_NEW_V1 ) ;
122
- if args. len( ) == 2 ;
123
- // Argument 1 in `new_v1()`
124
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, arr) = args[ 0 ] . kind;
125
- if let ExprKind :: Array ( pieces) = arr. kind;
126
- if pieces. len( ) == 1 ;
127
- if let ExprKind :: Lit ( ref lit) = pieces[ 0 ] . kind;
128
- if let LitKind :: Str ( ref s, _) = lit. node;
129
- // Argument 2 in `new_v1()`
130
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, arg1) = args[ 1 ] . kind;
131
- if let ExprKind :: Match ( matchee, arms, MatchSource :: Normal ) = arg1. kind;
132
- if arms. len( ) == 1 ;
133
- if let ExprKind :: Tup ( tup) = matchee. kind;
134
- then {
135
- // `format!("foo")` expansion contains `match () { () => [], }`
136
- if tup. is_empty( ) {
137
- if let Some ( s_src) = snippet_opt( cx, lit. span) {
138
- // Simulate macro expansion, converting {{ and }} to { and }.
139
- let s_expand = s_src. replace( "{{" , "{" ) . replace( "}}" , "}" ) ;
140
- return Some ( format!( "{}.to_string()" , s_expand) ) ;
141
- }
142
- } else if s. as_str( ) . is_empty( ) {
143
- return on_argumentv1_new( cx, & tup[ 0 ] , arms) ;
144
- }
145
- }
93
+ fn span_useless_format ( cx : & LateContext < ' _ > , span : Span , mut sugg : String , mut applicability : Applicability ) {
94
+ // The callsite span contains the statement semicolon for some reason.
95
+ if snippet_with_applicability ( cx, span, ".." , & mut applicability) . ends_with ( ';' ) {
96
+ sugg. push ( ';' ) ;
146
97
}
147
- None
98
+
99
+ span_lint_and_sugg (
100
+ cx,
101
+ USELESS_FORMAT ,
102
+ span,
103
+ "useless use of `format!`" ,
104
+ "consider using `.to_string()`" ,
105
+ sugg,
106
+ applicability,
107
+ ) ;
148
108
}
149
109
150
- fn on_new_v1_fmt < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < String > {
110
+ fn is_display_arg ( expr : & Expr < ' _ > ) -> bool {
151
111
if_chain ! {
152
- if let Some ( args) = match_function_call( cx, expr, & paths:: FMT_ARGUMENTS_NEW_V1_FORMATTED ) ;
153
- if args. len( ) == 3 ;
154
- if check_unformatted( & args[ 2 ] ) ;
155
- // Argument 1 in `new_v1_formatted()`
156
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, arr) = args[ 0 ] . kind;
157
- if let ExprKind :: Array ( pieces) = arr. kind;
158
- if pieces. len( ) == 1 ;
159
- if let ExprKind :: Lit ( ref lit) = pieces[ 0 ] . kind;
160
- if let LitKind :: Str ( ..) = lit. node;
161
- // Argument 2 in `new_v1_formatted()`
162
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, arg1) = args[ 1 ] . kind;
163
- if let ExprKind :: Match ( matchee, arms, MatchSource :: Normal ) = arg1. kind;
164
- if arms. len( ) == 1 ;
165
- if let ExprKind :: Tup ( tup) = matchee. kind;
166
- then {
167
- return on_argumentv1_new( cx, & tup[ 0 ] , arms) ;
168
- }
112
+ if let ExprKind :: Call ( _, [ _, fmt] ) = expr. kind;
113
+ if let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = fmt. kind;
114
+ if let [ .., t, _] = path. segments;
115
+ if t. ident. name. as_str( ) == "Display" ;
116
+ then { true } else { false }
169
117
}
170
- None
171
118
}
172
119
173
120
/// Checks if the expression matches
@@ -184,10 +131,9 @@ fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<S
184
131
fn check_unformatted ( expr : & Expr < ' _ > ) -> bool {
185
132
if_chain ! {
186
133
if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, expr) = expr. kind;
187
- if let ExprKind :: Array ( exprs) = expr. kind;
188
- if exprs. len( ) == 1 ;
134
+ if let ExprKind :: Array ( [ expr] ) = expr. kind;
189
135
// struct `core::fmt::rt::v1::Argument`
190
- if let ExprKind :: Struct ( _, fields, _) = exprs [ 0 ] . kind;
136
+ if let ExprKind :: Struct ( _, fields, _) = expr . kind;
191
137
if let Some ( format_field) = fields. iter( ) . find( |f| f. ident. name == sym:: format) ;
192
138
// struct `core::fmt::rt::v1::FormatSpec`
193
139
if let ExprKind :: Struct ( _, fields, _) = format_field. expr. kind;
0 commit comments