Skip to content

Commit

Permalink
Auto merge of rust-lang#85682 - m-ou-se:array-into-iter-2, r=nikomats…
Browse files Browse the repository at this point in the history
…akis

Update array_into_iter lint for 1.53 and edition changes.

This updates the array_into_iter lint for Rust 1.53 and the edition changes.

See rust-lang#84513

r? `@estebank`
  • Loading branch information
bors committed Jun 26, 2021
2 parents 831ae3c + dbdf7c7 commit f2571a2
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 104 deletions.
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

0 comments on commit f2571a2

Please sign in to comment.