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