Skip to content
New issue

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

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

Already on GitHub? # to your account

Upgrade array_into_iter lint to include Deref-to-array types. #88512

Merged
merged 3 commits into from
Sep 2, 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
54 changes: 30 additions & 24 deletions compiler/rustc_lint/src/array_into_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,39 +74,45 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
_ => return,
};

// As this is a method call expression, we have at least one
// argument.
// As this is a method call expression, we have at least one argument.
let receiver_arg = &args[0];
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);

// Peel all `Box<_>` layers. We have to special case `Box` here as
// `Box` is the only thing that values can be moved out of via
// method call. `Box::new([1]).into_iter()` should trigger this
// lint.
let mut recv_ty = cx.typeck_results().expr_ty(receiver_arg);
let mut num_box_derefs = 0;
while recv_ty.is_box() {
num_box_derefs += 1;
recv_ty = recv_ty.boxed_ty();
}
let target = match adjustments.last() {
Some(Adjustment { kind: Adjust::Borrow(_), target }) => target,
_ => return,
};

// Make sure we found an array after peeling the boxes.
if !matches!(recv_ty.kind(), ty::Array(..)) {
return;
let types =
std::iter::once(receiver_ty).chain(adjustments.iter().map(|adj| adj.target));

let mut found_array = false;

for ty in types {
match ty.kind() {
// If we run into a &[T; N] or &[T] first, there's nothing to warn about.
// It'll resolve to the reference version.
ty::Ref(_, inner_ty, _) if inner_ty.is_array() => return,
ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => return,
// Found an actual array type without matching a &[T; N] first.
// This is the problematic case.
ty::Array(..) => {
found_array = true;
break;
}
_ => {}
}
}

// Make sure that there is an autoref coercion at the expected
// position. The first `num_box_derefs` adjustments are the derefs
// of the box.
match cx.typeck_results().expr_adjustments(receiver_arg).get(num_box_derefs) {
Some(Adjustment { kind: Adjust::Borrow(_), .. }) => {}
_ => return,
if !found_array {
return;
}

// Emit lint diagnostic.
let target = match *cx.typeck_results().expr_ty_adjusted(receiver_arg).kind() {
let target = match *target.kind() {
ty::Ref(_, inner_ty, _) if inner_ty.is_array() => "[T; N]",
ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => "[T]",

// We know the original first argument type is an array type,
// we know that the first adjustment was an autoref coercion
// and we know that `IntoIterator` is the trait involved. The
Expand Down Expand Up @@ -135,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
String::new(),
Applicability::MaybeIncorrect,
);
} else {
} else if receiver_ty.is_array() {
diag.multipart_suggestion(
"or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value",
vec![
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/iterators/into-iter-on-arrays-2018.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ fn main() {
//~^ 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.
let _: Iter<'_, i32> = Rc::new(array).into_iter();
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
let _: Iter<'_, i32> = Array(array).into_iter();
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning

// But you can always use the trait method explicitly as an array.
let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
Expand Down
28 changes: 19 additions & 9 deletions src/test/ui/iterators/into-iter-on-arrays-2018.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,31 @@ warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (du
--> $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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity

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:22:43
|
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> = Rc::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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>

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:25:41
|
LL | let _: Iter<'_, i32> = IntoIterator::into_iter(Box::new(array));
| ++++++++++++++++++++++++ ~
LL | let _: Iter<'_, i32> = Array(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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>

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
--> $DIR/into-iter-on-arrays-2018.rs:32:24
|
LL | for _ in [1, 2, 3].into_iter() {}
| ^^^^^^^^^
Expand All @@ -51,5 +61,5 @@ LL - for _ in [1, 2, 3].into_iter() {}
LL + for _ in [1, 2, 3] {}
|

warning: 3 warnings emitted
warning: 5 warnings emitted

80 changes: 8 additions & 72 deletions src/test/ui/iterators/into-iter-on-arrays-lint.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -71,137 +71,73 @@ warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (du
--> $DIR/into-iter-on-arrays-lint.rs:23:21
|
LL | Box::new(small).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new(small).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new(small));
| ++++++++++++++++++++++++ ~
Comment on lines -82 to -85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still emit this suggestion in any case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for [1, 2, 3].into_iter(). See line 65 in this file.


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-lint.rs:26:22
|
LL | Box::new([1, 2]).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new([1, 2]).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new([1, 2]));
| ++++++++++++++++++++++++ ~

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-lint.rs:29:19
|
LL | Box::new(big).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new(big).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new(big));
| ++++++++++++++++++++++++ ~

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-lint.rs:32:25
|
LL | Box::new([0u8; 33]).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new([0u8; 33]).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new([0u8; 33]));
| ++++++++++++++++++++++++ ~

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-lint.rs:36:31
|
LL | Box::new(Box::new(small)).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new(Box::new(small)).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new(Box::new(small)));
| ++++++++++++++++++++++++ ~

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-lint.rs:39:32
|
LL | Box::new(Box::new([1, 2])).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new(Box::new([1, 2])).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new(Box::new([1, 2])));
| ++++++++++++++++++++++++ ~

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-lint.rs:42:29
|
LL | Box::new(Box::new(big)).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new(Box::new(big)).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new(Box::new(big)));
| ++++++++++++++++++++++++ ~

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-lint.rs:45:35
|
LL | Box::new(Box::new([0u8; 33])).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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
LL | Box::new(Box::new([0u8; 33])).iter();
| ~~~~
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
LL | IntoIterator::into_iter(Box::new(Box::new([0u8; 33])));
| ++++++++++++++++++++++++ ~

warning: 12 warnings emitted