@@ -2,19 +2,21 @@ use clippy_config::Conf;
2
2
use clippy_utils:: consts:: { ConstEvalCtxt , Constant } ;
3
3
use clippy_utils:: diagnostics:: span_lint_and_then;
4
4
use clippy_utils:: msrvs:: { self , Msrv } ;
5
- use clippy_utils:: source:: snippet ;
5
+ use clippy_utils:: source:: snippet_with_applicability ;
6
6
use clippy_utils:: usage:: mutated_variables;
7
7
use clippy_utils:: { eq_expr_value, higher} ;
8
+ use rustc_ast:: BindingMode ;
8
9
use rustc_ast:: ast:: LitKind ;
10
+ use rustc_data_structures:: fx:: FxHashMap ;
9
11
use rustc_errors:: Applicability ;
10
12
use rustc_hir:: def:: Res ;
11
- use rustc_hir:: intravisit:: { Visitor , walk_expr} ;
12
- use rustc_hir:: { BinOpKind , BorrowKind , Expr , ExprKind } ;
13
- use rustc_lint:: { LateContext , LateLintPass } ;
13
+ use rustc_hir:: intravisit:: { Visitor , walk_expr, walk_pat } ;
14
+ use rustc_hir:: { BinOpKind , BorrowKind , Expr , ExprKind , Node , PatKind } ;
15
+ use rustc_lint:: { LateContext , LateLintPass , LintContext as _ } ;
14
16
use rustc_middle:: ty;
15
17
use rustc_session:: impl_lint_pass;
16
18
use rustc_span:: source_map:: Spanned ;
17
- use rustc_span:: { Span , sym} ;
19
+ use rustc_span:: { Symbol , sym} ;
18
20
use std:: iter;
19
21
20
22
declare_clippy_lint ! {
@@ -95,18 +97,37 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
95
97
return ;
96
98
}
97
99
98
- let strippings = find_stripping ( cx, strip_kind, target_res, pattern, then) ;
100
+ let ( strippings, bindings ) = find_stripping ( cx, strip_kind, target_res, pattern, then) ;
99
101
if !strippings. is_empty ( ) {
100
102
let kind_word = match strip_kind {
101
103
StripKind :: Prefix => "prefix" ,
102
104
StripKind :: Suffix => "suffix" ,
103
105
} ;
104
106
105
107
let test_span = expr. span . until ( then. span ) ;
108
+
109
+ // If the first use is a simple `let` statement, reuse its identifier in the `if let Some(…)` and
110
+ // remove the `let` statement as long as the identifier is never bound again within the lexical
111
+ // scope of interest.
112
+ let ( ident_name, let_stmt_span, skip, mut app) = if let Node :: LetStmt ( let_stmt) =
113
+ cx. tcx . parent_hir_node ( strippings[ 0 ] . hir_id )
114
+ && let PatKind :: Binding ( BindingMode :: NONE , _, ident, None ) = & let_stmt. pat . kind
115
+ && bindings. get ( & ident. name ) == Some ( & 1 )
116
+ {
117
+ (
118
+ ident. name . as_str ( ) ,
119
+ Some ( cx. sess ( ) . source_map ( ) . span_extend_while_whitespace ( let_stmt. span ) ) ,
120
+ 1 ,
121
+ Applicability :: MachineApplicable ,
122
+ )
123
+ } else {
124
+ ( "<stripped>" , None , 0 , Applicability :: HasPlaceholders )
125
+ } ;
126
+
106
127
span_lint_and_then (
107
128
cx,
108
129
MANUAL_STRIP ,
109
- strippings[ 0 ] ,
130
+ strippings[ 0 ] . span ,
110
131
format ! ( "stripping a {kind_word} manually" ) ,
111
132
|diag| {
112
133
diag. span_note ( test_span, format ! ( "the {kind_word} was tested here" ) ) ;
@@ -115,14 +136,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
115
136
iter:: once ( (
116
137
test_span,
117
138
format ! (
118
- "if let Some(<stripped> ) = {}.strip_{kind_word}({}) " ,
119
- snippet ( cx, target_arg. span, ".." ) ,
120
- snippet ( cx, pattern. span, ".." )
139
+ "if let Some({ident_name} ) = {}.strip_{kind_word}({}) " ,
140
+ snippet_with_applicability ( cx, target_arg. span, "_" , & mut app ) ,
141
+ snippet_with_applicability ( cx, pattern. span, "_" , & mut app )
121
142
) ,
122
143
) )
123
- . chain ( strippings. into_iter ( ) . map ( |span| ( span, "<stripped>" . into ( ) ) ) )
144
+ . chain ( let_stmt_span. map ( |span| ( span, String :: new ( ) ) ) )
145
+ . chain (
146
+ strippings
147
+ . into_iter ( )
148
+ . skip ( skip)
149
+ . map ( |expr| ( expr. span , ident_name. into ( ) ) ) ,
150
+ )
124
151
. collect ( ) ,
125
- Applicability :: HasPlaceholders ,
152
+ app ,
126
153
) ;
127
154
} ,
128
155
) ;
@@ -188,19 +215,21 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
188
215
/// Find expressions where `target` is stripped using the length of `pattern`.
189
216
/// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
190
217
/// method.
218
+ /// Also, all bindings found during the visit are counted and returned.
191
219
fn find_stripping < ' tcx > (
192
220
cx : & LateContext < ' tcx > ,
193
221
strip_kind : StripKind ,
194
222
target : Res ,
195
223
pattern : & ' tcx Expr < ' _ > ,
196
- expr : & ' tcx Expr < ' _ > ,
197
- ) -> Vec < Span > {
224
+ expr : & ' tcx Expr < ' tcx > ,
225
+ ) -> ( Vec < & ' tcx Expr < ' tcx > > , FxHashMap < Symbol , usize > ) {
198
226
struct StrippingFinder < ' a , ' tcx > {
199
227
cx : & ' a LateContext < ' tcx > ,
200
228
strip_kind : StripKind ,
201
229
target : Res ,
202
230
pattern : & ' tcx Expr < ' tcx > ,
203
- results : Vec < Span > ,
231
+ results : Vec < & ' tcx Expr < ' tcx > > ,
232
+ bindings : FxHashMap < Symbol , usize > ,
204
233
}
205
234
206
235
impl < ' tcx > Visitor < ' tcx > for StrippingFinder < ' _ , ' tcx > {
@@ -215,7 +244,7 @@ fn find_stripping<'tcx>(
215
244
match ( self . strip_kind , start, end) {
216
245
( StripKind :: Prefix , Some ( start) , None ) => {
217
246
if eq_pattern_length ( self . cx , self . pattern , start) {
218
- self . results . push ( ex. span ) ;
247
+ self . results . push ( ex) ;
219
248
return ;
220
249
}
221
250
} ,
@@ -232,7 +261,7 @@ fn find_stripping<'tcx>(
232
261
&& self . cx . qpath_res ( left_path, left_arg. hir_id ) == self . target
233
262
&& eq_pattern_length ( self . cx , self . pattern , right)
234
263
{
235
- self . results . push ( ex. span ) ;
264
+ self . results . push ( ex) ;
236
265
return ;
237
266
}
238
267
} ,
@@ -242,6 +271,13 @@ fn find_stripping<'tcx>(
242
271
243
272
walk_expr ( self , ex) ;
244
273
}
274
+
275
+ fn visit_pat ( & mut self , pat : & ' tcx rustc_hir:: Pat < ' tcx > ) -> Self :: Result {
276
+ if let PatKind :: Binding ( _, _, ident, _) = pat. kind {
277
+ * self . bindings . entry ( ident. name ) . or_default ( ) += 1 ;
278
+ }
279
+ walk_pat ( self , pat) ;
280
+ }
245
281
}
246
282
247
283
let mut finder = StrippingFinder {
@@ -250,7 +286,8 @@ fn find_stripping<'tcx>(
250
286
target,
251
287
pattern,
252
288
results : vec ! [ ] ,
289
+ bindings : FxHashMap :: default ( ) ,
253
290
} ;
254
291
walk_expr ( & mut finder, expr) ;
255
- finder. results
292
+ ( finder. results , finder . bindings )
256
293
}
0 commit comments