@@ -2,73 +2,65 @@ use super::WHILE_LET_ON_ITERATOR;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
4
use clippy_utils:: { get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors:: is_res_used} ;
5
+ use if_chain:: if_chain;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: intravisit:: { walk_expr, ErasedMap , NestedVisitorMap , Visitor } ;
7
8
use rustc_hir:: { def:: Res , Expr , ExprKind , HirId , Local , MatchSource , Node , PatKind , QPath , UnOp } ;
8
9
use rustc_lint:: LateContext ;
9
10
use rustc_span:: { symbol:: sym, Span , Symbol } ;
10
11
11
12
pub ( super ) fn check ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
12
- if let ExprKind :: Match ( scrutinee_expr, [ arm, _] , MatchSource :: WhileLetDesugar ) = expr. kind {
13
- let some_pat = match arm. pat . kind {
14
- PatKind :: TupleStruct ( QPath :: Resolved ( None , path) , sub_pats, _) => match path. res {
15
- Res :: Def ( _, id) if match_def_path ( cx, id, & paths:: OPTION_SOME ) => sub_pats. first ( ) ,
16
- _ => return ,
17
- } ,
18
- _ => return ,
19
- } ;
20
-
21
- let iter_expr = match scrutinee_expr. kind {
22
- ExprKind :: MethodCall ( name, _, [ iter_expr] , _)
23
- if name. ident . name == sym:: next && is_trait_method ( cx, scrutinee_expr, sym:: Iterator ) =>
24
- {
25
- if let Some ( iter_expr) = try_parse_iter_expr ( cx, iter_expr) {
26
- iter_expr
27
- } else {
28
- return ;
29
- }
30
- }
31
- _ => return ,
32
- } ;
33
-
34
- // Needed to find an outer loop, if there are any.
35
- let loop_expr = if let Some ( ( _, Node :: Expr ( e) ) ) = cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) . nth ( 1 ) {
36
- e
13
+ let ( scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain ! {
14
+ if let ExprKind :: Match ( scrutinee_expr, [ arm, _] , MatchSource :: WhileLetDesugar ) = expr. kind;
15
+ // check for `Some(..)` pattern
16
+ if let PatKind :: TupleStruct ( QPath :: Resolved ( None , pat_path) , some_pat, _) = arm. pat. kind;
17
+ if let Res :: Def ( _, pat_did) = pat_path. res;
18
+ if match_def_path( cx, pat_did, & paths:: OPTION_SOME ) ;
19
+ // check for call to `Iterator::next`
20
+ if let ExprKind :: MethodCall ( method_name, _, [ iter_expr] , _) = scrutinee_expr. kind;
21
+ if method_name. ident. name == sym:: next;
22
+ if is_trait_method( cx, scrutinee_expr, sym:: Iterator ) ;
23
+ if let Some ( iter_expr) = try_parse_iter_expr( cx, iter_expr) ;
24
+ // get the loop containing the match expression
25
+ if let Some ( ( _, Node :: Expr ( loop_expr) ) ) = cx. tcx. hir( ) . parent_iter( expr. hir_id) . nth( 1 ) ;
26
+ if !uses_iter( cx, & iter_expr, arm. body) ;
27
+ then {
28
+ ( scrutinee_expr, iter_expr, some_pat, loop_expr)
37
29
} else {
38
30
return ;
39
- } ;
31
+ }
32
+ } ;
40
33
41
- // Refutable patterns don't work with for loops.
42
- // The iterator also can't be accessed withing the loop.
43
- if some_pat. map_or ( true , |p| is_refutable ( cx, p) ) || uses_iter ( cx, & iter_expr, arm. body ) {
34
+ let mut applicability = Applicability :: MachineApplicable ;
35
+ let loop_var = if let Some ( some_pat) = some_pat. first ( ) {
36
+ if is_refutable ( cx, some_pat) {
37
+ // Refutable patterns don't work with for loops.
44
38
return ;
45
39
}
40
+ snippet_with_applicability ( cx, some_pat. span , ".." , & mut applicability)
41
+ } else {
42
+ "_" . into ( )
43
+ } ;
46
44
47
- // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
48
- // borrowed mutably.
49
- // TODO: If the struct can be partially moved from and the struct isn't used afterwards a mutable
50
- // borrow of a field isn't necessary.
51
- let ref_mut = if !iter_expr. fields . is_empty ( ) || needs_mutable_borrow ( cx, & iter_expr, loop_expr) {
52
- "&mut "
53
- } else {
54
- ""
55
- } ;
56
- let mut applicability = Applicability :: MachineApplicable ;
57
- let iterator = snippet_with_applicability ( cx, iter_expr. span , "_" , & mut applicability) ;
58
- let loop_var = some_pat. map_or_else (
59
- || "_" . into ( ) ,
60
- |pat| snippet_with_applicability ( cx, pat. span , "_" , & mut applicability) . into_owned ( ) ,
61
- ) ;
62
- span_lint_and_sugg (
63
- cx,
64
- WHILE_LET_ON_ITERATOR ,
65
- expr. span . with_hi ( scrutinee_expr. span . hi ( ) ) ,
66
- "this loop could be written as a `for` loop" ,
67
- "try" ,
68
- format ! ( "for {} in {}{}" , loop_var, ref_mut, iterator) ,
69
- applicability,
70
- ) ;
71
- }
45
+ // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
46
+ // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
47
+ // afterwards a mutable borrow of a field isn't necessary.
48
+ let ref_mut = if !iter_expr. fields . is_empty ( ) || needs_mutable_borrow ( cx, & iter_expr, loop_expr) {
49
+ "&mut "
50
+ } else {
51
+ ""
52
+ } ;
53
+
54
+ let iterator = snippet_with_applicability ( cx, iter_expr. span , "_" , & mut applicability) ;
55
+ span_lint_and_sugg (
56
+ cx,
57
+ WHILE_LET_ON_ITERATOR ,
58
+ expr. span . with_hi ( scrutinee_expr. span . hi ( ) ) ,
59
+ "this loop could be written as a `for` loop" ,
60
+ "try" ,
61
+ format ! ( "for {} in {}{}" , loop_var, ref_mut, iterator) ,
62
+ applicability,
63
+ ) ;
72
64
}
73
65
74
66
#[ derive( Debug ) ]
@@ -135,8 +127,10 @@ fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symb
135
127
}
136
128
}
137
129
138
- /// Checks if the given expression is the same field as, is a child of, of the parent of the given
139
- /// field. Used to check if the expression can be used while the given field is borrowed.
130
+ /// Checks if the given expression is the same field as, is a child of, or is the parent of the
131
+ /// given field. Used to check if the expression can be used while the given field is borrowed
132
+ /// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but
133
+ /// `x.z`, and `y` will return false.
140
134
fn is_expr_same_child_or_parent_field ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , fields : & [ Symbol ] , path_res : Res ) -> bool {
141
135
match expr. kind {
142
136
ExprKind :: Field ( base, name) => {
@@ -166,7 +160,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
166
160
}
167
161
}
168
162
} ,
169
- // If the path matches, this is either an exact match, of the expression is a parent of the field.
163
+ // If the path matches, this is either an exact match, or the expression is a parent of the field.
170
164
ExprKind :: Path ( ref path) => cx. qpath_res ( path, expr. hir_id ) == path_res,
171
165
ExprKind :: DropTemps ( base) | ExprKind :: Type ( base, _) | ExprKind :: AddrOf ( _, _, base) => {
172
166
is_expr_same_child_or_parent_field ( cx, base, fields, path_res)
@@ -175,8 +169,8 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
175
169
}
176
170
}
177
171
178
- /// Strips off all field and path expressions. Used to skip them after failing to check for
179
- /// equality.
172
+ /// Strips off all field and path expressions. This will return true if a field or path has been
173
+ /// skipped. Used to skip them after failing to check for equality.
180
174
fn skip_fields_and_path ( expr : & ' tcx Expr < ' _ > ) -> ( Option < & ' tcx Expr < ' tcx > > , bool ) {
181
175
let mut e = expr;
182
176
let e = loop {
@@ -257,7 +251,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
257
251
self . visit_expr ( e) ;
258
252
}
259
253
} else if let ExprKind :: Closure ( _, _, id, _, _) = e. kind {
260
- self . used_iter | = is_res_used ( self . cx , self . iter_expr . path , id) ;
254
+ self . used_iter = is_res_used ( self . cx , self . iter_expr . path , id) ;
261
255
} else {
262
256
walk_expr ( self , e) ;
263
257
}
@@ -309,7 +303,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
309
303
self . visit_expr ( e) ;
310
304
}
311
305
} else if let ExprKind :: Closure ( _, _, id, _, _) = e. kind {
312
- self . used_after | = is_res_used ( self . cx , self . iter_expr . path , id) ;
306
+ self . used_after = is_res_used ( self . cx , self . iter_expr . path , id) ;
313
307
} else {
314
308
walk_expr ( self , e) ;
315
309
}
0 commit comments