1- use  clippy_utils:: diagnostics:: span_lint_and_sugg ; 
2- use  clippy_utils:: source:: snippet ; 
3- use  clippy_utils:: { SpanlessEq ,  higher,  is_integer_const,  is_trait_method} ; 
1+ use  clippy_utils:: diagnostics:: span_lint_and_then ; 
2+ use  clippy_utils:: source:: { SpanRangeExt   as  _ ,  snippet_with_applicability } ; 
3+ use  clippy_utils:: { SpanlessEq ,  get_parent_expr ,   higher,  is_integer_const,  is_trait_method,  sym } ; 
44use  rustc_errors:: Applicability ; 
5- use  rustc_hir:: { Expr ,  ExprKind ,  QPath } ; 
5+ use  rustc_hir:: { Expr ,  ExprKind ,  Node ,   Pat ,   PatKind ,   QPath } ; 
66use  rustc_lint:: LateContext ; 
7- use  rustc_span:: sym; 
87
98use  super :: RANGE_ZIP_WITH_LEN ; 
109
@@ -21,14 +20,93 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
2120        && let  ExprKind :: Path ( QPath :: Resolved ( _,  len_path) )  = len_recv. kind 
2221        && SpanlessEq :: new ( cx) . eq_path_segments ( iter_path. segments ,  len_path. segments ) 
2322    { 
24-         span_lint_and_sugg ( 
23+         span_lint_and_then ( 
2524            cx, 
2625            RANGE_ZIP_WITH_LEN , 
2726            expr. span , 
2827            "using `.zip()` with a range and `.len()`" , 
29-             "try" , 
30-             format ! ( "{}.iter().enumerate()" ,  snippet( cx,  recv. span,  "_" ) ) , 
31-             Applicability :: MachineApplicable , 
28+             |diag| { 
29+                 // If the iterator content is consumed by a pattern with exactly two elements, swap 
30+                 // the order of those elements. Otherwise, the suggestion will be marked as 
31+                 // `Applicability::MaybeIncorrect` (because it will be), and a note will be added 
32+                 // to the diagnostic to underline the swapping of the index and the content. 
33+                 let  pat = methods_pattern ( cx,  expr) . or_else ( || for_loop_pattern ( cx,  expr) ) ; 
34+                 let  invert_bindings = if  let  Some ( pat)  = pat
35+                     && pat. span . eq_ctxt ( expr. span ) 
36+                     && let  PatKind :: Tuple ( [ first,  second] ,  _)  = pat. kind 
37+                 { 
38+                     Some ( ( first. span ,  second. span ) ) 
39+                 }  else  { 
40+                     None 
41+                 } ; 
42+                 let  mut  app = Applicability :: MachineApplicable ; 
43+                 let  mut  suggestions = vec ! [ ( 
44+                     expr. span, 
45+                     format!( 
46+                         "{}.iter().enumerate()" , 
47+                         snippet_with_applicability( cx,  recv. span,  "_" ,  & mut  app) 
48+                     ) , 
49+                 ) ] ; 
50+                 if  let  Some ( ( left,  right) )  = invert_bindings
51+                     && let  Some ( snip_left)  = left. get_source_text ( cx) 
52+                     && let  Some ( snip_right)  = right. get_source_text ( cx) 
53+                 { 
54+                     suggestions. extend ( [ ( left,  snip_right. to_string ( ) ) ,  ( right,  snip_left. to_string ( ) ) ] ) ; 
55+                 }  else  { 
56+                     app = Applicability :: MaybeIncorrect ; 
57+                 } 
58+                 diag. multipart_suggestion ( "use" ,  suggestions,  app) ; 
59+                 if  app != Applicability :: MachineApplicable  { 
60+                     diag. note ( "the order of the element and the index will be swapped" ) ; 
61+                 } 
62+             } , 
3263        ) ; 
3364    } 
3465} 
66+ 
67+ /// If `expr` is the argument of a `for` loop, return the loop pattern. 
68+ fn  for_loop_pattern < ' tcx > ( cx :  & LateContext < ' tcx > ,  expr :  & ' tcx  Expr < ' _ > )  -> Option < & ' tcx  Pat < ' tcx > >  { 
69+     cx. tcx . hir_parent_iter ( expr. hir_id ) . find_map ( |( _,  node) | { 
70+         if  let  Node :: Expr ( ancestor_expr)  = node
71+             && let  Some ( for_loop)  = higher:: ForLoop :: hir ( ancestor_expr) 
72+             && for_loop. arg . hir_id  == expr. hir_id 
73+         { 
74+             Some ( for_loop. pat ) 
75+         }  else  { 
76+             None 
77+         } 
78+     } ) 
79+ } 
80+ 
81+ /// If `expr` is the receiver of an `Iterator` method which consumes the iterator elements and feed 
82+ /// them to a closure, return the pattern of the closure. 
83+ fn  methods_pattern < ' tcx > ( cx :  & LateContext < ' tcx > ,  expr :  & ' tcx  Expr < ' _ > )  -> Option < & ' tcx  Pat < ' tcx > >  { 
84+     if  let  Some ( parent_expr)  = get_parent_expr ( cx,  expr) 
85+         && is_trait_method ( cx,  expr,  sym:: Iterator ) 
86+         && let  ExprKind :: MethodCall ( method,  recv,  [ arg] ,  _)  = parent_expr. kind 
87+         && recv. hir_id  == expr. hir_id 
88+         && matches ! ( 
89+             method. ident. name, 
90+             sym:: all
91+                 | sym:: any
92+                 | sym:: filter_map
93+                 | sym:: find_map
94+                 | sym:: flat_map
95+                 | sym:: for_each
96+                 | sym:: is_partitioned
97+                 | sym:: is_sorted_by_key
98+                 | sym:: map
99+                 | sym:: map_while
100+                 | sym:: position
101+                 | sym:: rposition
102+                 | sym:: try_for_each
103+         ) 
104+         && let  ExprKind :: Closure ( closure)  = arg. kind 
105+         && let  body = cx. tcx . hir_body ( closure. body ) 
106+         && let  [ param]  = body. params 
107+     { 
108+         Some ( param. pat ) 
109+     }  else  { 
110+         None 
111+     } 
112+ } 
0 commit comments