@@ -4,6 +4,7 @@ use rustc_hir as hir;
44use rustc_middle:: ty;
55use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
66use rustc_span:: symbol:: sym;
7+ use rustc_span:: Span ;
78
89declare_lint ! {
910 /// The `array_into_iter` lint detects calling `into_iter` on arrays.
@@ -30,13 +31,29 @@ declare_lint! {
3031 "detects calling `into_iter` on arrays in Rust 2015 and 2018" ,
3132}
3233
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 ] ) ;
3740
3841impl < ' tcx > LateLintPass < ' tcx > for ArrayIntoIter {
3942 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+
4057 // We only care about method call expressions.
4158 if let hir:: ExprKind :: MethodCall ( call, span, args, _) = & expr. kind {
4259 if call. ident . name != sym:: into_iter {
@@ -92,27 +109,37 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
92109 _ => bug ! ( "array type coerced to something other than array or slice" ) ,
93110 } ;
94111 cx. struct_span_lint ( ARRAY_INTO_ITER , * span, |lint| {
95- lint. build ( & format ! (
112+ let mut diag = lint. build ( & format ! (
96113 "this method call resolves to `<&{} as IntoIterator>::into_iter` \
97114 (due to backwards compatibility), \
98115 but will resolve to <{} as IntoIterator>::into_iter in Rust 2021.",
99116 target, target,
100- ) )
101- . span_suggestion (
117+ ) ) ;
118+ diag . span_suggestion (
102119 call. ident . span ,
103120 "use `.iter()` instead of `.into_iter()` to avoid ambiguity" ,
104121 "iter" . into ( ) ,
105122 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 ( ) ;
116143 } )
117144 }
118145 }
0 commit comments