Skip to content

Commit b492c97

Browse files
Add future incompatibility lint for array.into_iter()
As we might want to add `IntoIterator` impls for arrays in the future, and since that introduces a breaking change, this lint warns and suggests using `iter()` instead (which is shorter and more explicit).
1 parent b3a0350 commit b492c97

File tree

3 files changed

+96
-0
lines changed

3 files changed

+96
-0
lines changed

src/libcore/iter/traits/collect.rs

+1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ pub trait FromIterator<A>: Sized {
205205
/// .collect()
206206
/// }
207207
/// ```
208+
#[rustc_diagnostic_item = "IntoIterator"]
208209
#[stable(feature = "rust1", since = "1.0.0")]
209210
pub trait IntoIterator {
210211
/// The type of the elements being iterated over.

src/librustc_lint/array_into_iter.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use crate::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
2+
use rustc::{
3+
lint::FutureIncompatibleInfo,
4+
hir,
5+
ty::{
6+
self,
7+
adjustment::{Adjust, Adjustment},
8+
},
9+
};
10+
use syntax::{
11+
errors::Applicability,
12+
symbol::sym,
13+
};
14+
15+
16+
declare_lint! {
17+
pub ARRAY_INTO_ITER,
18+
Warn,
19+
"detects calling `into_iter` on arrays",
20+
@future_incompatible = FutureIncompatibleInfo {
21+
reference: "issue #66145 <https://github.com/rust-lang/rust/issues/66145>",
22+
edition: None,
23+
};
24+
}
25+
26+
declare_lint_pass!(
27+
/// Checks for instances of calling `into_iter` on arrays.
28+
ArrayIntoIter => [ARRAY_INTO_ITER]
29+
);
30+
31+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
32+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
33+
// We only care about method call expressions.
34+
if let hir::ExprKind::MethodCall(call, span, args) = &expr.kind {
35+
if call.ident.name != sym::into_iter {
36+
return;
37+
}
38+
39+
// Check if the method call actually calls the libcore
40+
// `IntoIterator::into_iter`.
41+
let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap();
42+
match cx.tcx.trait_of_item(def_id) {
43+
Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {},
44+
_ => return,
45+
};
46+
47+
// As this is a method call expression, we have at least one
48+
// argument.
49+
let receiver_arg = &args[0];
50+
51+
// Test if the original `self` type is an array type.
52+
match cx.tables.expr_ty(receiver_arg).kind {
53+
ty::Array(..) => {}
54+
_ => return,
55+
}
56+
57+
// Make sure that the first adjustment is an autoref coercion.
58+
match cx.tables.expr_adjustments(receiver_arg).get(0) {
59+
Some(Adjustment { kind: Adjust::Borrow(_), .. }) => {}
60+
_ => return,
61+
}
62+
63+
// Emit lint diagnostic.
64+
let target = match cx.tables.expr_ty_adjusted(receiver_arg).kind {
65+
ty::Ref(_, ty::TyS { kind: ty::Array(..), ..}, _) => "[T; N]",
66+
ty::Ref(_, ty::TyS { kind: ty::Slice(..), ..}, _) => "[T]",
67+
68+
// We know the original first argument type is an array type,
69+
// we know that the first adjustment was an autoref coercion
70+
// and we know that `IntoIterator` is the trait involved. The
71+
// array cannot be coerced to something other than a reference
72+
// to an array or to a slice.
73+
_ => bug!("array type coerced to something other than array or slice"),
74+
};
75+
let msg = format!(
76+
"this method call currently resolves to `<&{} as IntoIterator>::into_iter` (due \
77+
to autoref coercions), but that might change in the future when \
78+
`IntoIterator` impls for arrays are added.",
79+
target,
80+
);
81+
cx.struct_span_lint(ARRAY_INTO_ITER, *span, &msg)
82+
.span_suggestion(
83+
call.ident.span,
84+
"use `.iter()` instead of `.into_iter()` to avoid ambiguity",
85+
"iter".into(),
86+
Applicability::MachineApplicable,
87+
)
88+
.emit();
89+
}
90+
}
91+
}

src/librustc_lint/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#[macro_use]
2222
extern crate rustc;
2323

24+
mod array_into_iter;
2425
mod error_codes;
2526
mod nonstandard_style;
2627
mod redundant_semicolon;
@@ -56,6 +57,7 @@ use types::*;
5657
use unused::*;
5758
use non_ascii_idents::*;
5859
use rustc::lint::internal::*;
60+
use array_into_iter::ArrayIntoIter;
5961

6062
/// Useful for other parts of the compiler.
6163
pub use builtin::SoftLints;
@@ -130,6 +132,8 @@ macro_rules! late_lint_passes {
130132
// FIXME: Turn the computation of types which implement Debug into a query
131133
// and change this to a module lint pass
132134
MissingDebugImplementations: MissingDebugImplementations::default(),
135+
136+
ArrayIntoIter: ArrayIntoIter,
133137
]);
134138
)
135139
}

0 commit comments

Comments
 (0)