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 } ;
4
4
use rustc_errors:: Applicability ;
5
- use rustc_hir:: { Expr , ExprKind , QPath } ;
5
+ use rustc_hir:: { Expr , ExprKind , Node , Pat , PatKind , QPath } ;
6
6
use rustc_lint:: LateContext ;
7
- use rustc_span:: sym;
8
7
9
8
use super :: RANGE_ZIP_WITH_LEN ;
10
9
@@ -21,14 +20,93 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
21
20
&& let ExprKind :: Path ( QPath :: Resolved ( _, len_path) ) = len_recv. kind
22
21
&& SpanlessEq :: new ( cx) . eq_path_segments ( iter_path. segments , len_path. segments )
23
22
{
24
- span_lint_and_sugg (
23
+ span_lint_and_then (
25
24
cx,
26
25
RANGE_ZIP_WITH_LEN ,
27
26
expr. span ,
28
27
"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
+ } ,
32
63
) ;
33
64
}
34
65
}
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