Skip to content

Update array_into_iter lint for 1.53 and edition changes. #85682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 55 additions & 26 deletions compiler/rustc_lint/src/array_into_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_session::lint::FutureIncompatibilityReason;
use rustc_span::edition::Edition;
use rustc_span::symbol::sym;
use rustc_span::Span;

declare_lint! {
/// The `array_into_iter` lint detects calling `into_iter` on arrays.
Expand All @@ -21,34 +22,44 @@ declare_lint! {
///
/// ### Explanation
///
/// In the future, it is planned to add an `IntoIter` implementation for
/// arrays such that it will iterate over *values* of the array instead of
/// references. Due to how method resolution works, this will change
/// existing code that uses `into_iter` on arrays. The solution to avoid
/// this warning is to use `iter()` instead of `into_iter()`.
///
/// This is a [future-incompatible] lint to transition this to a hard error
/// in the future. See [issue #66145] for more details and a more thorough
/// description of the lint.
///
/// [issue #66145]: https://github.com/rust-lang/rust/issues/66145
/// [future-incompatible]: ../index.md#future-incompatible-lints
/// Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid
/// breakage, `array.into_iter()` in Rust 2015 and 2018 code will still
/// behave as `(&array).into_iter()`, returning an iterator over
/// references, just like in Rust 1.52 and earlier.
/// This only applies to the method call syntax `array.into_iter()`, not to
/// any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`.
pub ARRAY_INTO_ITER,
Warn,
"detects calling `into_iter` on arrays",
"detects calling `into_iter` on arrays in Rust 2015 and 2018",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #66145 <https://github.com/rust-lang/rust/issues/66145>",
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
};
}

declare_lint_pass!(
/// Checks for instances of calling `into_iter` on arrays.
ArrayIntoIter => [ARRAY_INTO_ITER]
);
#[derive(Copy, Clone, Default)]
pub struct ArrayIntoIter {
for_expr_span: Span,
}

impl_lint_pass!(ArrayIntoIter => [ARRAY_INTO_ITER]);

impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
// Save the span of expressions in `for _ in expr` syntax,
// so we can give a better suggestion for those later.
if let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = &expr.kind {
if let hir::ExprKind::Call(path, [arg]) = &arg.kind {
if let hir::ExprKind::Path(hir::QPath::LangItem(
hir::LangItem::IntoIterIntoIter,
_,
)) = &path.kind
{
self.for_expr_span = arg.span;
}
}
}

// We only care about method call expressions.
if let hir::ExprKind::MethodCall(call, span, args, _) = &expr.kind {
if call.ident.name != sym::into_iter {
Expand Down Expand Up @@ -104,19 +115,37 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
_ => bug!("array type coerced to something other than array or slice"),
};
cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| {
lint.build(&format!(
"this method call currently resolves to `<&{} as IntoIterator>::into_iter` (due \
to autoref coercions), but that might change in the future when \
`IntoIterator` impls for arrays are added.",
target,
))
.span_suggestion(
let mut diag = lint.build(&format!(
"this method call resolves to `<&{} as IntoIterator>::into_iter` \
(due to backwards compatibility), \
but will resolve to <{} as IntoIterator>::into_iter in Rust 2021.",
target, target,
));
diag.span_suggestion(
call.ident.span,
"use `.iter()` instead of `.into_iter()` to avoid ambiguity",
"iter".into(),
Applicability::MachineApplicable,
)
.emit();
);
if self.for_expr_span == expr.span {
let expr_span = expr.span.ctxt().outer_expn_data().call_site;
diag.span_suggestion(
receiver_arg.span.shrink_to_hi().to(expr_span.shrink_to_hi()),
"or remove `.into_iter()` to iterate by value",
String::new(),
Applicability::MaybeIncorrect,
);
} else {
diag.multipart_suggestion(
"or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value",
vec![
(expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
(receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), ")".into()),
],
Applicability::MaybeIncorrect,
);
}
diag.emit();
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ macro_rules! late_lint_passes {
// FIXME: Turn the computation of types which implement Debug into a query
// and change this to a module lint pass
MissingDebugImplementations: MissingDebugImplementations::default(),
ArrayIntoIter: ArrayIntoIter,
ArrayIntoIter: ArrayIntoIter::default(),
ClashingExternDeclarations: ClashingExternDeclarations::new(),
DropTraitConstraints: DropTraitConstraints,
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
Expand Down
8 changes: 6 additions & 2 deletions src/test/ui/iterators/into-iter-on-arrays-2018.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ fn main() {
// Before 2021, the method dispatched to `IntoIterator for &[T; N]`,
// which we continue to support for compatibility.
let _: Iter<'_, i32> = array.into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

let _: Iter<'_, i32> = Box::new(array).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

// The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
Expand All @@ -25,6 +25,10 @@ fn main() {

// But you can always use the trait method explicitly as an array.
let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);

for _ in [1, 2, 3].into_iter() {}
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
}

/// User type that dereferences to an array.
Expand Down
43 changes: 38 additions & 5 deletions src/test/ui/iterators/into-iter-on-arrays-2018.stderr
Original file line number Diff line number Diff line change
@@ -1,21 +1,54 @@
warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021.
--> $DIR/into-iter-on-arrays-2018.rs:14:34
|
LL | let _: Iter<'_, i32> = array.into_iter();
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
| ^^^^^^^^^
|
= note: `#[warn(array_into_iter)]` on by default
= warning: this changes meaning in Rust 2021
= note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | let _: Iter<'_, i32> = array.iter();
| ^^^^
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | let _: Iter<'_, i32> = IntoIterator::into_iter(array);
| ^^^^^^^^^^^^^^^^^^^^^^^^ ^

warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021.
--> $DIR/into-iter-on-arrays-2018.rs:18:44
|
LL | let _: Iter<'_, i32> = Box::new(array).into_iter();
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
| ^^^^^^^^^
|
= warning: this changes meaning in Rust 2021
= note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | let _: Iter<'_, i32> = Box::new(array).iter();
| ^^^^
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | let _: Iter<'_, i32> = IntoIterator::into_iter(Box::new(array));
| ^^^^^^^^^^^^^^^^^^^^^^^^ ^

warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021.
--> $DIR/into-iter-on-arrays-2018.rs:29:24
|
LL | for _ in [1, 2, 3].into_iter() {}
| ^^^^^^^^^
|
= warning: this changes meaning in Rust 2021
= note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | for _ in [1, 2, 3].iter() {}
| ^^^^
help: or remove `.into_iter()` to iterate by value
|
LL | for _ in [1, 2, 3] {}
| --

warning: 2 warnings emitted
warning: 3 warnings emitted

25 changes: 13 additions & 12 deletions src/test/ui/iterators/into-iter-on-arrays-lint.fixed
Original file line number Diff line number Diff line change
@@ -1,48 +1,49 @@
// run-pass
// run-rustfix
// rustfix-only-machine-applicable

fn main() {
let small = [1, 2];
let big = [0u8; 33];

// Expressions that should trigger the lint
small.iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
[1, 2].iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
big.iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
[0u8; 33].iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

Box::new(small).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new([1, 2]).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(big).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new([0u8; 33]).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

Box::new(Box::new(small)).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(Box::new([1, 2])).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(Box::new(big)).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(Box::new([0u8; 33])).iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

// Expressions that should not
Expand Down
25 changes: 13 additions & 12 deletions src/test/ui/iterators/into-iter-on-arrays-lint.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,49 @@
// run-pass
// run-rustfix
// rustfix-only-machine-applicable

fn main() {
let small = [1, 2];
let big = [0u8; 33];

// Expressions that should trigger the lint
small.into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
[1, 2].into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
big.into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
[0u8; 33].into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

Box::new(small).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new([1, 2]).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(big).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new([0u8; 33]).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

Box::new(Box::new(small)).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(Box::new([1, 2])).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(Box::new(big)).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
Box::new(Box::new([0u8; 33])).into_iter();
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

// Expressions that should not
Expand Down
Loading