11use super :: WHILE_LET_ON_ITERATOR ;
2+ use if_chain:: if_chain;
23use clippy_utils:: diagnostics:: span_lint_and_sugg;
34use clippy_utils:: source:: snippet_with_applicability;
45use clippy_utils:: { get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors:: is_res_used} ;
@@ -9,66 +10,56 @@ use rustc_lint::LateContext;
910use rustc_span:: { symbol:: sym, Span , Symbol } ;
1011
1112pub ( 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)
3729 } else {
3830 return ;
39- } ;
31+ }
32+ } ;
4033
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.
4438 return ;
4539 }
40+ snippet_with_applicability ( cx, some_pat. span , ".." , & mut applicability)
41+ } else {
42+ "_" . into ( )
43+ } ;
4644
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 borrowed mutably.
46+ // TODO: If the struct can be partially moved from and the struct isn't used afterwards a mutable borrow of a field isn't necessary.
47+ let ref_mut = if !iter_expr. fields . is_empty ( ) || needs_mutable_borrow ( cx, & iter_expr, loop_expr) {
48+ "&mut "
49+ } else {
50+ ""
51+ } ;
52+
53+ let iterator = snippet_with_applicability ( cx, iter_expr. span , "_" , & mut applicability) ;
54+ span_lint_and_sugg (
55+ cx,
56+ WHILE_LET_ON_ITERATOR ,
57+ expr. span . with_hi ( scrutinee_expr. span . hi ( ) ) ,
58+ "this loop could be written as a `for` loop" ,
59+ "try" ,
60+ format ! ( "for {} in {}{}" , loop_var, ref_mut, iterator) ,
61+ applicability,
62+ ) ;
7263}
7364
7465#[ derive( Debug ) ]
@@ -135,8 +126,9 @@ fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symb
135126 }
136127}
137128
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.
129+ /// Checks if the given expression is the same field as, is a child of, or is the parent of the given
130+ /// field. Used to check if the expression can be used while the given field is borrowed mutably.
131+ /// e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but `x.z`, and `y` will return false.
140132fn is_expr_same_child_or_parent_field ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , fields : & [ Symbol ] , path_res : Res ) -> bool {
141133 match expr. kind {
142134 ExprKind :: Field ( base, name) => {
@@ -166,7 +158,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
166158 }
167159 }
168160 } ,
169- // If the path matches, this is either an exact match, of the expression is a parent of the field.
161+ // If the path matches, this is either an exact match, or the expression is a parent of the field.
170162 ExprKind :: Path ( ref path) => cx. qpath_res ( path, expr. hir_id ) == path_res,
171163 ExprKind :: DropTemps ( base) | ExprKind :: Type ( base, _) | ExprKind :: AddrOf ( _, _, base) => {
172164 is_expr_same_child_or_parent_field ( cx, base, fields, path_res)
@@ -175,8 +167,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
175167 }
176168}
177169
178- /// Strips off all field and path expressions. Used to skip them after failing to check for
179- /// equality.
170+ /// Strips off all field and path expressions. This will return true if a field or path has been skipped. Used to skip them after failing to check for equality.
180171fn skip_fields_and_path ( expr : & ' tcx Expr < ' _ > ) -> ( Option < & ' tcx Expr < ' tcx > > , bool ) {
181172 let mut e = expr;
182173 let e = loop {
@@ -257,7 +248,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
257248 self . visit_expr ( e) ;
258249 }
259250 } else if let ExprKind :: Closure ( _, _, id, _, _) = e. kind {
260- self . used_iter | = is_res_used ( self . cx , self . iter_expr . path , id) ;
251+ self . used_iter = is_res_used ( self . cx , self . iter_expr . path , id) ;
261252 } else {
262253 walk_expr ( self , e) ;
263254 }
@@ -309,7 +300,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
309300 self . visit_expr ( e) ;
310301 }
311302 } else if let ExprKind :: Closure ( _, _, id, _, _) = e. kind {
312- self . used_after | = is_res_used ( self . cx , self . iter_expr . path , id) ;
303+ self . used_after = is_res_used ( self . cx , self . iter_expr . path , id) ;
313304 } else {
314305 walk_expr ( self , e) ;
315306 }
0 commit comments