1use rustc_hir::{self as hir, LangItem};
2use rustc_middle::ty::{self, Ty};
3use rustc_session::lint::fcw;
4use rustc_session::{declare_lint, impl_lint_pass};
5
6use crate::lints::{ShadowedIntoIterDiag, ShadowedIntoIterDiagSub};
7use crate::{LateContext, LateLintPass, LintContext};
8
9#[doc = r" The `array_into_iter` lint detects calling `into_iter` on arrays."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,edition2018"]
#[doc = r" # #![allow(unused)]"]
#[doc = r" [1, 2, 3].into_iter().for_each(|n| { *n; });"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid"]
#[doc =
r" breakage, `array.into_iter()` in Rust 2015 and 2018 code will still"]
#[doc = r" behave as `(&array).into_iter()`, returning an iterator over"]
#[doc = r" references, just like in Rust 1.52 and earlier."]
#[doc =
r" This only applies to the method call syntax `array.into_iter()`, not to"]
#[doc =
r" any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`."]
pub static ARRAY_INTO_ITER: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "ARRAY_INTO_ITER",
default_level: ::rustc_lint_defs::Warn,
desc: "detects calling `into_iter` on arrays in Rust 2015 and 2018",
is_externally_loaded: false,
future_incompatible: Some(::rustc_lint_defs::FutureIncompatibleInfo {
reason: ::rustc_lint_defs::FutureIncompatibilityReason::EditionSemanticsChange(::rustc_lint_defs::EditionFcw {
edition: rustc_span::edition::Edition::Edition2021,
page_slug: "IntoIterator-for-arrays",
}),
..::rustc_lint_defs::FutureIncompatibleInfo::default_fields_for_macro()
}),
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
10 pub ARRAY_INTO_ITER,
30 Warn,
31 "detects calling `into_iter` on arrays in Rust 2015 and 2018",
32 @future_incompatible = FutureIncompatibleInfo {
33 reason: fcw!(EditionSemanticsChange 2021 "IntoIterator-for-arrays"),
34 };
35}
36
37#[doc =
r" The `boxed_slice_into_iter` lint detects calling `into_iter` on boxed slices."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,edition2021"]
#[doc = r" # #![allow(unused)]"]
#[doc =
r" vec![1, 2, 3].into_boxed_slice().into_iter().for_each(|n| { *n; });"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Since Rust 1.80.0, boxed slices implement `IntoIterator`. However, to avoid"]
#[doc =
r" breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still"]
#[doc =
r" behave as `(&boxed_slice).into_iter()`, returning an iterator over"]
#[doc = r" references, just like in Rust 1.79.0 and earlier."]
#[doc =
r" This only applies to the method call syntax `boxed_slice.into_iter()`, not to"]
#[doc =
r" any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`."]
pub static BOXED_SLICE_INTO_ITER: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "BOXED_SLICE_INTO_ITER",
default_level: ::rustc_lint_defs::Warn,
desc: "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
is_externally_loaded: false,
future_incompatible: Some(::rustc_lint_defs::FutureIncompatibleInfo {
reason: ::rustc_lint_defs::FutureIncompatibilityReason::EditionSemanticsChange(::rustc_lint_defs::EditionFcw {
edition: rustc_span::edition::Edition::Edition2024,
page_slug: "intoiterator-box-slice",
}),
..::rustc_lint_defs::FutureIncompatibleInfo::default_fields_for_macro()
}),
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
38 pub BOXED_SLICE_INTO_ITER,
58 Warn,
59 "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
60 @future_incompatible = FutureIncompatibleInfo {
61 reason: fcw!(EditionSemanticsChange 2024 "intoiterator-box-slice"),
62 };
63}
64
65#[derive(#[automatically_derived]
impl ::core::marker::Copy for ShadowedIntoIter { }Copy, #[automatically_derived]
impl ::core::clone::Clone for ShadowedIntoIter {
#[inline]
fn clone(&self) -> ShadowedIntoIter { *self }
}Clone)]
66pub(crate) struct ShadowedIntoIter;
67
68impl ::rustc_lint_defs::LintPass for ShadowedIntoIter {
fn name(&self) -> &'static str { "ShadowedIntoIter" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([ARRAY_INTO_ITER,
BOXED_SLICE_INTO_ITER]))
}
}
impl ShadowedIntoIter {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([ARRAY_INTO_ITER,
BOXED_SLICE_INTO_ITER]))
}
}impl_lint_pass!(ShadowedIntoIter => [ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER]);
69
70impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
71 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
72 let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind else {
73 return;
74 };
75
76 let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else {
79 return;
80 };
81 if !cx.tcx.is_lang_item(method_def_id, LangItem::IntoIterIntoIter) {
82 return;
83 }
84
85 let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
87 let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
88
89 let adjusted_receiver_tys: Vec<_> =
90 [receiver_ty].into_iter().chain(adjustments.iter().map(|adj| adj.target)).collect();
91
92 fn is_ref_to_array(ty: Ty<'_>) -> bool {
93 if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false }
94 }
95 fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool {
96 if let ty::Ref(_, pointee_ty, _) = *ty.kind() {
97 pointee_ty.boxed_ty().is_some_and(Ty::is_slice)
98 } else {
99 false
100 }
101 }
102
103 let (lint, target, edition, can_suggest_ufcs) =
104 if is_ref_to_array(*adjusted_receiver_tys.last().unwrap())
105 && let Some(idx) = adjusted_receiver_tys
106 .iter()
107 .copied()
108 .take_while(|ty| !is_ref_to_array(*ty))
109 .position(|ty| ty.is_array())
110 {
111 (ARRAY_INTO_ITER, "[T; N]", "2021", idx == 0)
112 } else if is_ref_to_boxed_slice(*adjusted_receiver_tys.last().unwrap())
113 && let Some(idx) = adjusted_receiver_tys
114 .iter()
115 .copied()
116 .take_while(|ty| !is_ref_to_boxed_slice(*ty))
117 .position(|ty| ty.boxed_ty().is_some_and(Ty::is_slice))
118 {
119 (BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0)
120 } else {
121 return;
122 };
123
124 let span = receiver_arg.span.find_ancestor_in_same_ctxt(expr.span);
128
129 let sub = if let Some((_, hir::Node::Expr(parent_expr))) =
133 cx.tcx.hir_parent_iter(expr.hir_id).nth(1)
134 && let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) =
135 &parent_expr.kind
136 && let hir::ExprKind::Call(path, [_]) = &arg.kind
137 && let hir::ExprKind::Path(qpath) = path.kind
138 && cx.tcx.qpath_is_lang_item(qpath, LangItem::IntoIterIntoIter)
139 && let Some(span) = span
140 {
141 Some(ShadowedIntoIterDiagSub::RemoveIntoIter {
142 span: span.shrink_to_hi().to(expr.span.shrink_to_hi()),
143 })
144 } else if can_suggest_ufcs && let Some(span) = span {
145 Some(ShadowedIntoIterDiagSub::UseExplicitIntoIter {
146 start_span: expr.span.shrink_to_lo(),
147 end_span: span.shrink_to_hi().to(expr.span.shrink_to_hi()),
148 })
149 } else {
150 None
151 };
152
153 cx.emit_span_lint(
154 lint,
155 call.ident.span,
156 ShadowedIntoIterDiag { target, edition, suggestion: call.ident.span, sub },
157 );
158 }
159}