@@ -4,6 +4,7 @@ use rustc_hir as hir;
4
4
use rustc_middle:: ty;
5
5
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
6
6
use rustc_span:: symbol:: sym;
7
+ use rustc_span:: Span ;
7
8
8
9
declare_lint ! {
9
10
/// The `array_into_iter` lint detects calling `into_iter` on arrays.
@@ -30,13 +31,29 @@ declare_lint! {
30
31
"detects calling `into_iter` on arrays in Rust 2015 and 2018" ,
31
32
}
32
33
33
- declare_lint_pass ! (
34
- /// Checks for instances of calling `into_iter` on arrays.
35
- ArrayIntoIter => [ ARRAY_INTO_ITER ]
36
- ) ;
34
+ #[ derive( Copy , Clone , Default ) ]
35
+ pub struct ArrayIntoIter {
36
+ for_expr_span : Span ,
37
+ }
38
+
39
+ impl_lint_pass ! ( ArrayIntoIter => [ ARRAY_INTO_ITER ] ) ;
37
40
38
41
impl < ' tcx > LateLintPass < ' tcx > for ArrayIntoIter {
39
42
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
43
+ // Save the span of expressions in `for _ in expr` syntax,
44
+ // so we can give a better suggestion for those later.
45
+ if let hir:: ExprKind :: Match ( arg, [ _] , hir:: MatchSource :: ForLoopDesugar ) = & expr. kind {
46
+ if let hir:: ExprKind :: Call ( path, [ arg] ) = & arg. kind {
47
+ if let hir:: ExprKind :: Path ( hir:: QPath :: LangItem (
48
+ hir:: LangItem :: IntoIterIntoIter ,
49
+ _,
50
+ ) ) = & path. kind
51
+ {
52
+ self . for_expr_span = arg. span ;
53
+ }
54
+ }
55
+ }
56
+
40
57
// We only care about method call expressions.
41
58
if let hir:: ExprKind :: MethodCall ( call, span, args, _) = & expr. kind {
42
59
if call. ident . name != sym:: into_iter {
@@ -92,27 +109,37 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
92
109
_ => bug ! ( "array type coerced to something other than array or slice" ) ,
93
110
} ;
94
111
cx. struct_span_lint ( ARRAY_INTO_ITER , * span, |lint| {
95
- lint. build ( & format ! (
112
+ let mut diag = lint. build ( & format ! (
96
113
"this method call resolves to `<&{} as IntoIterator>::into_iter` \
97
114
(due to backwards compatibility), \
98
115
but will resolve to <{} as IntoIterator>::into_iter in Rust 2021.",
99
116
target, target,
100
- ) )
101
- . span_suggestion (
117
+ ) ) ;
118
+ diag . span_suggestion (
102
119
call. ident . span ,
103
120
"use `.iter()` instead of `.into_iter()` to avoid ambiguity" ,
104
121
"iter" . into ( ) ,
105
122
Applicability :: MachineApplicable ,
106
- )
107
- . multipart_suggestion (
108
- "or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value" ,
109
- vec ! [
110
- ( expr. span. shrink_to_lo( ) , "IntoIterator::into_iter(" . into( ) ) ,
111
- ( receiver_arg. span. shrink_to_hi( ) . to( expr. span. shrink_to_hi( ) ) , ")" . into( ) ) ,
112
- ] ,
113
- Applicability :: MaybeIncorrect ,
114
- )
115
- . emit ( ) ;
123
+ ) ;
124
+ if self . for_expr_span == expr. span {
125
+ let expr_span = expr. span . ctxt ( ) . outer_expn_data ( ) . call_site ;
126
+ diag. span_suggestion (
127
+ receiver_arg. span . shrink_to_hi ( ) . to ( expr_span. shrink_to_hi ( ) ) ,
128
+ "or remove `.into_iter()` to iterate by value" ,
129
+ String :: new ( ) ,
130
+ Applicability :: MaybeIncorrect ,
131
+ ) ;
132
+ } else {
133
+ diag. multipart_suggestion (
134
+ "or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value" ,
135
+ vec ! [
136
+ ( expr. span. shrink_to_lo( ) , "IntoIterator::into_iter(" . into( ) ) ,
137
+ ( receiver_arg. span. shrink_to_hi( ) . to( expr. span. shrink_to_hi( ) ) , ")" . into( ) ) ,
138
+ ] ,
139
+ Applicability :: MaybeIncorrect ,
140
+ ) ;
141
+ }
142
+ diag. emit ( ) ;
116
143
} )
117
144
}
118
145
}
0 commit comments