1 use crate::{LateContext, LateLintPass, LintContext}
;
2 use rustc_errors
::Applicability
;
5 use rustc_middle
::ty
::adjustment
::{Adjust, Adjustment}
;
6 use rustc_session
::lint
::FutureIncompatibilityReason
;
7 use rustc_span
::edition
::Edition
;
8 use rustc_span
::symbol
::sym
;
12 /// The `array_into_iter` lint detects calling `into_iter` on arrays.
16 /// ```rust,edition2018
17 /// # #![allow(unused)]
18 /// [1, 2, 3].into_iter().for_each(|n| { *n; });
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)`.
33 "detects calling `into_iter` on arrays in Rust 2015 and 2018",
34 @future_incompatible
= FutureIncompatibleInfo
{
35 reference
: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
36 reason
: FutureIncompatibilityReason
::EditionSemanticsChange(Edition
::Edition2021
),
40 #[derive(Copy, Clone, Default)]
41 pub struct ArrayIntoIter
{
45 impl_lint_pass
!(ArrayIntoIter
=> [ARRAY_INTO_ITER
]);
47 impl<'tcx
> LateLintPass
<'tcx
> for ArrayIntoIter
{
48 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
,
58 self.for_expr_span
= arg
.span
;
63 // We only care about method call expressions.
64 if let hir
::ExprKind
::MethodCall(call
, span
, args
, _
) = &expr
.kind
{
65 if call
.ident
.name
!= sym
::into_iter
{
69 // Check if the method call actually calls the libcore
70 // `IntoIterator::into_iter`.
71 let def_id
= cx
.typeck_results().type_dependent_def_id(expr
.hir_id
).unwrap();
72 match cx
.tcx
.trait_of_item(def_id
) {
73 Some(trait_id
) if cx
.tcx
.is_diagnostic_item(sym
::IntoIterator
, trait_id
) => {}
77 // As this is a method call expression, we have at least one argument.
78 let receiver_arg
= &args
[0];
79 let receiver_ty
= cx
.typeck_results().expr_ty(receiver_arg
);
80 let adjustments
= cx
.typeck_results().expr_adjustments(receiver_arg
);
82 let target
= match adjustments
.last() {
83 Some(Adjustment { kind: Adjust::Borrow(_), target }
) => target
,
88 std
::iter
::once(receiver_ty
).chain(adjustments
.iter().map(|adj
| adj
.target
));
90 let mut found_array
= false;
94 // If we run into a &[T; N] or &[T] first, there's nothing to warn about.
95 // It'll resolve to the reference version.
96 ty
::Ref(_
, inner_ty
, _
) if inner_ty
.is_array() => return,
97 ty
::Ref(_
, inner_ty
, _
) if matches
!(inner_ty
.kind(), ty
::Slice(..)) => return,
98 // Found an actual array type without matching a &[T; N] first.
99 // This is the problematic case.
112 // Emit lint diagnostic.
113 let target
= match *target
.kind() {
114 ty
::Ref(_
, inner_ty
, _
) if inner_ty
.is_array() => "[T; N]",
115 ty
::Ref(_
, inner_ty
, _
) if matches
!(inner_ty
.kind(), ty
::Slice(..)) => "[T]",
116 // We know the original first argument type is an array type,
117 // we know that the first adjustment was an autoref coercion
118 // and we know that `IntoIterator` is the trait involved. The
119 // array cannot be coerced to something other than a reference
120 // to an array or to a slice.
121 _
=> bug
!("array type coerced to something other than array or slice"),
123 cx
.struct_span_lint(ARRAY_INTO_ITER
, *span
, |lint
| {
124 let mut diag
= lint
.build(&format
!(
125 "this method call resolves to `<&{} as IntoIterator>::into_iter` \
126 (due to backwards compatibility), \
127 but will resolve to <{} as IntoIterator>::into_iter in Rust 2021",
130 diag
.span_suggestion(
132 "use `.iter()` instead of `.into_iter()` to avoid ambiguity",
134 Applicability
::MachineApplicable
,
136 if self.for_expr_span
== expr
.span
{
137 diag
.span_suggestion(
138 receiver_arg
.span
.shrink_to_hi().to(expr
.span
.shrink_to_hi()),
139 "or remove `.into_iter()` to iterate by value",
141 Applicability
::MaybeIncorrect
,
143 } else if receiver_ty
.is_array() {
144 diag
.multipart_suggestion(
145 "or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value",
147 (expr
.span
.shrink_to_lo(), "IntoIterator::into_iter(".into()),
148 (receiver_arg
.span
.shrink_to_hi().to(expr
.span
.shrink_to_hi()), ")".into()),
150 Applicability
::MaybeIncorrect
,