@@ -2,19 +2,21 @@ use clippy_config::Conf;
22use clippy_utils:: consts:: { ConstEvalCtxt , Constant } ;
33use clippy_utils:: diagnostics:: span_lint_and_then;
44use clippy_utils:: msrvs:: { self , Msrv } ;
5- use clippy_utils:: source:: snippet ;
5+ use clippy_utils:: source:: snippet_with_applicability ;
66use clippy_utils:: usage:: mutated_variables;
77use clippy_utils:: { eq_expr_value, higher} ;
8+ use rustc_ast:: BindingMode ;
89use rustc_ast:: ast:: LitKind ;
10+ use rustc_data_structures:: fx:: FxHashMap ;
911use rustc_errors:: Applicability ;
1012use 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 _ } ;
1416use rustc_middle:: ty;
1517use rustc_session:: impl_lint_pass;
1618use rustc_span:: source_map:: Spanned ;
17- use rustc_span:: { Span , sym} ;
19+ use rustc_span:: { Symbol , sym} ;
1820use std:: iter;
1921
2022declare_clippy_lint ! {
@@ -95,18 +97,37 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
9597 return ;
9698 }
9799
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) ;
99101 if !strippings. is_empty ( ) {
100102 let kind_word = match strip_kind {
101103 StripKind :: Prefix => "prefix" ,
102104 StripKind :: Suffix => "suffix" ,
103105 } ;
104106
105107 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+
106127 span_lint_and_then (
107128 cx,
108129 MANUAL_STRIP ,
109- strippings[ 0 ] ,
130+ strippings[ 0 ] . span ,
110131 format ! ( "stripping a {kind_word} manually" ) ,
111132 |diag| {
112133 diag. span_note ( test_span, format ! ( "the {kind_word} was tested here" ) ) ;
@@ -115,14 +136,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
115136 iter:: once ( (
116137 test_span,
117138 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 )
121142 ) ,
122143 ) )
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+ )
124151 . collect ( ) ,
125- Applicability :: HasPlaceholders ,
152+ app ,
126153 ) ;
127154 } ,
128155 ) ;
@@ -188,19 +215,21 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
188215/// Find expressions where `target` is stripped using the length of `pattern`.
189216/// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
190217/// method.
218+ /// Also, all bindings found during the visit are counted and returned.
191219fn find_stripping < ' tcx > (
192220 cx : & LateContext < ' tcx > ,
193221 strip_kind : StripKind ,
194222 target : Res ,
195223 pattern : & ' tcx Expr < ' _ > ,
196- expr : & ' tcx Expr < ' _ > ,
197- ) -> Vec < Span > {
224+ expr : & ' tcx Expr < ' tcx > ,
225+ ) -> ( Vec < & ' tcx Expr < ' tcx > > , FxHashMap < Symbol , usize > ) {
198226 struct StrippingFinder < ' a , ' tcx > {
199227 cx : & ' a LateContext < ' tcx > ,
200228 strip_kind : StripKind ,
201229 target : Res ,
202230 pattern : & ' tcx Expr < ' tcx > ,
203- results : Vec < Span > ,
231+ results : Vec < & ' tcx Expr < ' tcx > > ,
232+ bindings : FxHashMap < Symbol , usize > ,
204233 }
205234
206235 impl < ' tcx > Visitor < ' tcx > for StrippingFinder < ' _ , ' tcx > {
@@ -215,7 +244,7 @@ fn find_stripping<'tcx>(
215244 match ( self . strip_kind , start, end) {
216245 ( StripKind :: Prefix , Some ( start) , None ) => {
217246 if eq_pattern_length ( self . cx , self . pattern , start) {
218- self . results . push ( ex. span ) ;
247+ self . results . push ( ex) ;
219248 return ;
220249 }
221250 } ,
@@ -232,7 +261,7 @@ fn find_stripping<'tcx>(
232261 && self . cx . qpath_res ( left_path, left_arg. hir_id ) == self . target
233262 && eq_pattern_length ( self . cx , self . pattern , right)
234263 {
235- self . results . push ( ex. span ) ;
264+ self . results . push ( ex) ;
236265 return ;
237266 }
238267 } ,
@@ -242,6 +271,13 @@ fn find_stripping<'tcx>(
242271
243272 walk_expr ( self , ex) ;
244273 }
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+ }
245281 }
246282
247283 let mut finder = StrippingFinder {
@@ -250,7 +286,8 @@ fn find_stripping<'tcx>(
250286 target,
251287 pattern,
252288 results : vec ! [ ] ,
289+ bindings : FxHashMap :: default ( ) ,
253290 } ;
254291 walk_expr ( & mut finder, expr) ;
255- finder. results
292+ ( finder. results , finder . bindings )
256293}
0 commit comments