Skip to content

Commit f2571a2

Browse files
committed
Auto merge of rust-lang#85682 - m-ou-se:array-into-iter-2, r=nikomatsakis
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`
2 parents 831ae3c + dbdf7c7 commit f2571a2

8 files changed

+258
-104
lines changed

compiler/rustc_lint/src/array_into_iter.rs

+55-26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment};
66
use rustc_session::lint::FutureIncompatibilityReason;
77
use rustc_span::edition::Edition;
88
use rustc_span::symbol::sym;
9+
use rustc_span::Span;
910

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

45-
declare_lint_pass!(
46-
/// Checks for instances of calling `into_iter` on arrays.
47-
ArrayIntoIter => [ARRAY_INTO_ITER]
48-
);
40+
#[derive(Copy, Clone, Default)]
41+
pub struct ArrayIntoIter {
42+
for_expr_span: Span,
43+
}
44+
45+
impl_lint_pass!(ArrayIntoIter => [ARRAY_INTO_ITER]);
4946

5047
impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
5148
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
49+
// Save the span of expressions in `for _ in expr` syntax,
50+
// so we can give a better suggestion for those later.
51+
if let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = &expr.kind {
52+
if let hir::ExprKind::Call(path, [arg]) = &arg.kind {
53+
if let hir::ExprKind::Path(hir::QPath::LangItem(
54+
hir::LangItem::IntoIterIntoIter,
55+
_,
56+
)) = &path.kind
57+
{
58+
self.for_expr_span = arg.span;
59+
}
60+
}
61+
}
62+
5263
// We only care about method call expressions.
5364
if let hir::ExprKind::MethodCall(call, span, args, _) = &expr.kind {
5465
if call.ident.name != sym::into_iter {
@@ -104,19 +115,37 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
104115
_ => bug!("array type coerced to something other than array or slice"),
105116
};
106117
cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| {
107-
lint.build(&format!(
108-
"this method call currently resolves to `<&{} as IntoIterator>::into_iter` (due \
109-
to autoref coercions), but that might change in the future when \
110-
`IntoIterator` impls for arrays are added.",
111-
target,
112-
))
113-
.span_suggestion(
118+
let mut diag = lint.build(&format!(
119+
"this method call resolves to `<&{} as IntoIterator>::into_iter` \
120+
(due to backwards compatibility), \
121+
but will resolve to <{} as IntoIterator>::into_iter in Rust 2021.",
122+
target, target,
123+
));
124+
diag.span_suggestion(
114125
call.ident.span,
115126
"use `.iter()` instead of `.into_iter()` to avoid ambiguity",
116127
"iter".into(),
117128
Applicability::MachineApplicable,
118-
)
119-
.emit();
129+
);
130+
if self.for_expr_span == expr.span {
131+
let expr_span = expr.span.ctxt().outer_expn_data().call_site;
132+
diag.span_suggestion(
133+
receiver_arg.span.shrink_to_hi().to(expr_span.shrink_to_hi()),
134+
"or remove `.into_iter()` to iterate by value",
135+
String::new(),
136+
Applicability::MaybeIncorrect,
137+
);
138+
} else {
139+
diag.multipart_suggestion(
140+
"or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value",
141+
vec![
142+
(expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
143+
(receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), ")".into()),
144+
],
145+
Applicability::MaybeIncorrect,
146+
);
147+
}
148+
diag.emit();
120149
})
121150
}
122151
}

compiler/rustc_lint/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ macro_rules! late_lint_passes {
163163
// FIXME: Turn the computation of types which implement Debug into a query
164164
// and change this to a module lint pass
165165
MissingDebugImplementations: MissingDebugImplementations::default(),
166-
ArrayIntoIter: ArrayIntoIter,
166+
ArrayIntoIter: ArrayIntoIter::default(),
167167
ClashingExternDeclarations: ClashingExternDeclarations::new(),
168168
DropTraitConstraints: DropTraitConstraints,
169169
TemporaryCStringAsPtr: TemporaryCStringAsPtr,

src/test/ui/iterators/into-iter-on-arrays-2018.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ fn main() {
1212
// Before 2021, the method dispatched to `IntoIterator for &[T; N]`,
1313
// which we continue to support for compatibility.
1414
let _: Iter<'_, i32> = array.into_iter();
15-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
15+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
1616
//~| WARNING this changes meaning
1717

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

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

2626
// But you can always use the trait method explicitly as an array.
2727
let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
28+
29+
for _ in [1, 2, 3].into_iter() {}
30+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
31+
//~| WARNING this changes meaning
2832
}
2933

3034
/// User type that dereferences to an array.
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,54 @@
1-
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.
1+
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.
22
--> $DIR/into-iter-on-arrays-2018.rs:14:34
33
|
44
LL | let _: Iter<'_, i32> = array.into_iter();
5-
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
5+
| ^^^^^^^^^
66
|
77
= note: `#[warn(array_into_iter)]` on by default
88
= warning: this changes meaning in Rust 2021
99
= note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
10+
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
11+
|
12+
LL | let _: Iter<'_, i32> = array.iter();
13+
| ^^^^
14+
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
15+
|
16+
LL | let _: Iter<'_, i32> = IntoIterator::into_iter(array);
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^ ^
1018

11-
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.
19+
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.
1220
--> $DIR/into-iter-on-arrays-2018.rs:18:44
1321
|
1422
LL | let _: Iter<'_, i32> = Box::new(array).into_iter();
15-
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
23+
| ^^^^^^^^^
1624
|
1725
= warning: this changes meaning in Rust 2021
1826
= note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
27+
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
28+
|
29+
LL | let _: Iter<'_, i32> = Box::new(array).iter();
30+
| ^^^^
31+
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
32+
|
33+
LL | let _: Iter<'_, i32> = IntoIterator::into_iter(Box::new(array));
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^ ^
35+
36+
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.
37+
--> $DIR/into-iter-on-arrays-2018.rs:29:24
38+
|
39+
LL | for _ in [1, 2, 3].into_iter() {}
40+
| ^^^^^^^^^
41+
|
42+
= warning: this changes meaning in Rust 2021
43+
= note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
44+
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
45+
|
46+
LL | for _ in [1, 2, 3].iter() {}
47+
| ^^^^
48+
help: or remove `.into_iter()` to iterate by value
49+
|
50+
LL | for _ in [1, 2, 3] {}
51+
| --
1952

20-
warning: 2 warnings emitted
53+
warning: 3 warnings emitted
2154

src/test/ui/iterators/into-iter-on-arrays-lint.fixed

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,49 @@
11
// run-pass
22
// run-rustfix
3+
// rustfix-only-machine-applicable
34

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

89
// Expressions that should trigger the lint
910
small.iter();
10-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
11+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
1112
//~| WARNING this changes meaning
1213
[1, 2].iter();
13-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
14+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
1415
//~| WARNING this changes meaning
1516
big.iter();
16-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
17+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
1718
//~| WARNING this changes meaning
1819
[0u8; 33].iter();
19-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
20+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
2021
//~| WARNING this changes meaning
2122

2223
Box::new(small).iter();
23-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
24+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
2425
//~| WARNING this changes meaning
2526
Box::new([1, 2]).iter();
26-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
27+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
2728
//~| WARNING this changes meaning
2829
Box::new(big).iter();
29-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
30+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
3031
//~| WARNING this changes meaning
3132
Box::new([0u8; 33]).iter();
32-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
33+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
3334
//~| WARNING this changes meaning
3435

3536
Box::new(Box::new(small)).iter();
36-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
37+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
3738
//~| WARNING this changes meaning
3839
Box::new(Box::new([1, 2])).iter();
39-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
40+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
4041
//~| WARNING this changes meaning
4142
Box::new(Box::new(big)).iter();
42-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
43+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
4344
//~| WARNING this changes meaning
4445
Box::new(Box::new([0u8; 33])).iter();
45-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
46+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
4647
//~| WARNING this changes meaning
4748

4849
// Expressions that should not

src/test/ui/iterators/into-iter-on-arrays-lint.rs

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,49 @@
11
// run-pass
22
// run-rustfix
3+
// rustfix-only-machine-applicable
34

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

89
// Expressions that should trigger the lint
910
small.into_iter();
10-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
11+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
1112
//~| WARNING this changes meaning
1213
[1, 2].into_iter();
13-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
14+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
1415
//~| WARNING this changes meaning
1516
big.into_iter();
16-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
17+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
1718
//~| WARNING this changes meaning
1819
[0u8; 33].into_iter();
19-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
20+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
2021
//~| WARNING this changes meaning
2122

2223
Box::new(small).into_iter();
23-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
24+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
2425
//~| WARNING this changes meaning
2526
Box::new([1, 2]).into_iter();
26-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
27+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
2728
//~| WARNING this changes meaning
2829
Box::new(big).into_iter();
29-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
30+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
3031
//~| WARNING this changes meaning
3132
Box::new([0u8; 33]).into_iter();
32-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
33+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
3334
//~| WARNING this changes meaning
3435

3536
Box::new(Box::new(small)).into_iter();
36-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
37+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
3738
//~| WARNING this changes meaning
3839
Box::new(Box::new([1, 2])).into_iter();
39-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
40+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
4041
//~| WARNING this changes meaning
4142
Box::new(Box::new(big)).into_iter();
42-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
43+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
4344
//~| WARNING this changes meaning
4445
Box::new(Box::new([0u8; 33])).into_iter();
45-
//~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
46+
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
4647
//~| WARNING this changes meaning
4748

4849
// Expressions that should not

0 commit comments

Comments
 (0)