mirror of https://github.com/rust-lang/rust.git
Implement BOXED_SLICE_INTO_ITER
This commit is contained in:
parent
1a81092531
commit
a502e7ac1d
|
@ -184,7 +184,7 @@ impl<'a, T> IntoIterator for &'a P<[T]> {
|
||||||
type Item = &'a T;
|
type Item = &'a T;
|
||||||
type IntoIter = slice::Iter<'a, T>;
|
type IntoIter = slice::Iter<'a, T>;
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.ptr.into_iter()
|
self.ptr.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -899,10 +899,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
"the `#[rustc_main]` attribute is used internally to specify test entry point function",
|
"the `#[rustc_main]` attribute is used internally to specify test entry point function",
|
||||||
),
|
),
|
||||||
rustc_attr!(
|
rustc_attr!(
|
||||||
rustc_skip_array_during_method_dispatch, Normal, template!(Word),
|
rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), WarnFollowing,
|
||||||
WarnFollowing, EncodeCrossCrate::No,
|
EncodeCrossCrate::No,
|
||||||
"the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
|
"the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \
|
||||||
from method dispatch when the receiver is an array, for compatibility in editions < 2021."
|
from method dispatch when the receiver is of the following type, for compatibility in \
|
||||||
|
editions < 2021 (array) or editions < 2024 (boxed_slice)."
|
||||||
),
|
),
|
||||||
rustc_attr!(
|
rustc_attr!(
|
||||||
rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."),
|
rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."),
|
||||||
|
|
|
@ -1117,8 +1117,24 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
||||||
|
|
||||||
let is_marker = tcx.has_attr(def_id, sym::marker);
|
let is_marker = tcx.has_attr(def_id, sym::marker);
|
||||||
let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
|
let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
|
||||||
let skip_array_during_method_dispatch =
|
|
||||||
tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
|
// FIXME: We could probably do way better attribute validation here.
|
||||||
|
let mut skip_array_during_method_dispatch = false;
|
||||||
|
let mut skip_boxed_slice_during_method_dispatch = false;
|
||||||
|
for attr in tcx.get_attrs(def_id, sym::rustc_skip_during_method_dispatch) {
|
||||||
|
if let Some(lst) = attr.meta_item_list() {
|
||||||
|
for item in lst {
|
||||||
|
if let Some(ident) = item.ident() {
|
||||||
|
match ident.as_str() {
|
||||||
|
"array" => skip_array_during_method_dispatch = true,
|
||||||
|
"boxed_slice" => skip_boxed_slice_during_method_dispatch = true,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
|
let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
|
||||||
ty::trait_def::TraitSpecializationKind::Marker
|
ty::trait_def::TraitSpecializationKind::Marker
|
||||||
} else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
|
} else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
|
||||||
|
@ -1253,6 +1269,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
||||||
is_marker,
|
is_marker,
|
||||||
is_coinductive: rustc_coinductive || is_auto,
|
is_coinductive: rustc_coinductive || is_auto,
|
||||||
skip_array_during_method_dispatch,
|
skip_array_during_method_dispatch,
|
||||||
|
skip_boxed_slice_during_method_dispatch,
|
||||||
specialization_kind,
|
specialization_kind,
|
||||||
must_implement_one_of,
|
must_implement_one_of,
|
||||||
implement_via_object,
|
implement_via_object,
|
||||||
|
|
|
@ -1444,6 +1444,18 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
return ProbeResult::NoMatch;
|
return ProbeResult::NoMatch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some trait methods are excluded for boxed slices before 2024.
|
||||||
|
// (`boxed_slice.into_iter()` wants a slice iterator for compatibility.)
|
||||||
|
if self_ty.is_box()
|
||||||
|
&& self_ty.boxed_ty().is_slice()
|
||||||
|
&& !method_name.span.at_least_rust_2024()
|
||||||
|
{
|
||||||
|
let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
|
||||||
|
if trait_def.skip_boxed_slice_during_method_dispatch {
|
||||||
|
return ProbeResult::NoMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let trait_ref = self.instantiate_binder_with_fresh_vars(
|
let trait_ref = self.instantiate_binder_with_fresh_vars(
|
||||||
|
|
|
@ -2,13 +2,6 @@ lint_ambiguous_wide_pointer_comparisons = ambiguous wide pointer comparison, the
|
||||||
.addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses
|
.addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses
|
||||||
.addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
.addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
||||||
|
|
||||||
lint_array_into_iter =
|
|
||||||
this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust 2021
|
|
||||||
.use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
|
||||||
.remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value
|
|
||||||
.use_explicit_into_iter_suggestion =
|
|
||||||
or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
|
||||||
|
|
||||||
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
|
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
|
||||||
.note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
|
.note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
|
||||||
.suggestion = you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
|
.suggestion = you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
|
||||||
|
@ -565,6 +558,13 @@ lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}`
|
||||||
|
|
||||||
lint_requested_level = requested on the command line with `{$level} {$lint_name}`
|
lint_requested_level = requested on the command line with `{$level} {$lint_name}`
|
||||||
|
|
||||||
|
lint_shadowed_into_iter =
|
||||||
|
this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition}
|
||||||
|
.use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
||||||
|
.remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value
|
||||||
|
.use_explicit_into_iter_suggestion =
|
||||||
|
or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
||||||
|
|
||||||
lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`
|
lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`
|
||||||
|
|
||||||
lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion
|
lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
use crate::{
|
|
||||||
lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub},
|
|
||||||
LateContext, LateLintPass, LintContext,
|
|
||||||
};
|
|
||||||
use rustc_hir as hir;
|
|
||||||
use rustc_middle::bug;
|
|
||||||
use rustc_middle::ty;
|
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
|
||||||
use rustc_session::lint::FutureIncompatibilityReason;
|
|
||||||
use rustc_session::{declare_lint, impl_lint_pass};
|
|
||||||
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.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
///
|
|
||||||
/// ```rust,edition2018
|
|
||||||
/// # #![allow(unused)]
|
|
||||||
/// [1, 2, 3].into_iter().for_each(|n| { *n; });
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// {{produces}}
|
|
||||||
///
|
|
||||||
/// ### Explanation
|
|
||||||
///
|
|
||||||
/// 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 in Rust 2015 and 2018",
|
|
||||||
@future_incompatible = FutureIncompatibleInfo {
|
|
||||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
|
|
||||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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, receiver_arg, ..) = &expr.kind {
|
|
||||||
if call.ident.name != sym::into_iter {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the method call actually calls the libcore
|
|
||||||
// `IntoIterator::into_iter`.
|
|
||||||
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
|
|
||||||
match cx.tcx.trait_of_item(def_id) {
|
|
||||||
Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {}
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
// As this is a method call expression, we have at least one argument.
|
|
||||||
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
|
|
||||||
let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
|
|
||||||
|
|
||||||
let Some(Adjustment { kind: Adjust::Borrow(_), target }) = adjustments.last() else {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found_array {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit lint diagnostic.
|
|
||||||
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
|
|
||||||
// array cannot be coerced to something other than a reference
|
|
||||||
// to an array or to a slice.
|
|
||||||
_ => bug!("array type coerced to something other than array or slice"),
|
|
||||||
};
|
|
||||||
let sub = if self.for_expr_span == expr.span {
|
|
||||||
Some(ArrayIntoIterDiagSub::RemoveIntoIter {
|
|
||||||
span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
|
|
||||||
})
|
|
||||||
} else if receiver_ty.is_array() {
|
|
||||||
Some(ArrayIntoIterDiagSub::UseExplicitIntoIter {
|
|
||||||
start_span: expr.span.shrink_to_lo(),
|
|
||||||
end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
cx.emit_span_lint(
|
|
||||||
ARRAY_INTO_ITER,
|
|
||||||
call.ident.span,
|
|
||||||
ArrayIntoIterDiag { target, suggestion: call.ident.span, sub },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,7 +42,6 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate tracing;
|
extern crate tracing;
|
||||||
|
|
||||||
mod array_into_iter;
|
|
||||||
mod async_fn_in_trait;
|
mod async_fn_in_trait;
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
mod context;
|
mod context;
|
||||||
|
@ -76,18 +75,18 @@ mod passes;
|
||||||
mod ptr_nulls;
|
mod ptr_nulls;
|
||||||
mod redundant_semicolon;
|
mod redundant_semicolon;
|
||||||
mod reference_casting;
|
mod reference_casting;
|
||||||
|
mod shadowed_into_iter;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod types;
|
mod types;
|
||||||
mod unit_bindings;
|
mod unit_bindings;
|
||||||
mod unused;
|
mod unused;
|
||||||
|
|
||||||
pub use array_into_iter::ARRAY_INTO_ITER;
|
pub use shadowed_into_iter::ARRAY_INTO_ITER;
|
||||||
|
|
||||||
use rustc_hir::def_id::LocalModDefId;
|
use rustc_hir::def_id::LocalModDefId;
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
use array_into_iter::ArrayIntoIter;
|
|
||||||
use async_fn_in_trait::AsyncFnInTrait;
|
use async_fn_in_trait::AsyncFnInTrait;
|
||||||
use builtin::*;
|
use builtin::*;
|
||||||
use deref_into_dyn_supertrait::*;
|
use deref_into_dyn_supertrait::*;
|
||||||
|
@ -112,6 +111,7 @@ use pass_by_value::*;
|
||||||
use ptr_nulls::*;
|
use ptr_nulls::*;
|
||||||
use redundant_semicolon::*;
|
use redundant_semicolon::*;
|
||||||
use reference_casting::*;
|
use reference_casting::*;
|
||||||
|
use shadowed_into_iter::ShadowedIntoIter;
|
||||||
use traits::*;
|
use traits::*;
|
||||||
use types::*;
|
use types::*;
|
||||||
use unit_bindings::*;
|
use unit_bindings::*;
|
||||||
|
@ -215,7 +215,7 @@ late_lint_methods!(
|
||||||
DerefNullPtr: DerefNullPtr,
|
DerefNullPtr: DerefNullPtr,
|
||||||
UnstableFeatures: UnstableFeatures,
|
UnstableFeatures: UnstableFeatures,
|
||||||
UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller,
|
UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller,
|
||||||
ArrayIntoIter: ArrayIntoIter::default(),
|
ShadowedIntoIter: ShadowedIntoIter,
|
||||||
DropTraitConstraints: DropTraitConstraints,
|
DropTraitConstraints: DropTraitConstraints,
|
||||||
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
|
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
|
||||||
NonPanicFmt: NonPanicFmt,
|
NonPanicFmt: NonPanicFmt,
|
||||||
|
|
|
@ -22,17 +22,18 @@ use crate::{
|
||||||
|
|
||||||
// array_into_iter.rs
|
// array_into_iter.rs
|
||||||
#[derive(LintDiagnostic)]
|
#[derive(LintDiagnostic)]
|
||||||
#[diag(lint_array_into_iter)]
|
#[diag(lint_shadowed_into_iter)]
|
||||||
pub struct ArrayIntoIterDiag<'a> {
|
pub struct ShadowedIntoIterDiag {
|
||||||
pub target: &'a str,
|
pub target: &'static str,
|
||||||
|
pub edition: &'static str,
|
||||||
#[suggestion(lint_use_iter_suggestion, code = "iter", applicability = "machine-applicable")]
|
#[suggestion(lint_use_iter_suggestion, code = "iter", applicability = "machine-applicable")]
|
||||||
pub suggestion: Span,
|
pub suggestion: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
pub sub: Option<ArrayIntoIterDiagSub>,
|
pub sub: Option<ShadowedIntoIterDiagSub>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
pub enum ArrayIntoIterDiagSub {
|
pub enum ShadowedIntoIterDiagSub {
|
||||||
#[suggestion(lint_remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")]
|
#[suggestion(lint_remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")]
|
||||||
RemoveIntoIter {
|
RemoveIntoIter {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
use crate::lints::{ShadowedIntoIterDiag, ShadowedIntoIterDiagSub};
|
||||||
|
use crate::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
use rustc_session::lint::FutureIncompatibilityReason;
|
||||||
|
use rustc_session::{declare_lint, impl_lint_pass};
|
||||||
|
use rustc_span::edition::Edition;
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `array_into_iter` lint detects calling `into_iter` on arrays.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,edition2018
|
||||||
|
/// # #![allow(unused)]
|
||||||
|
/// [1, 2, 3].into_iter().for_each(|n| { *n; });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// {{produces}}
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// 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 in Rust 2015 and 2018",
|
||||||
|
@future_incompatible = FutureIncompatibleInfo {
|
||||||
|
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
|
||||||
|
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `boxed_slice_into_iter` lint detects calling `into_iter` on boxed slices.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,edition2021
|
||||||
|
/// # #![allow(unused)]
|
||||||
|
/// vec![1, 2, 3].into_boxed_slice().into_iter().for_each(|n| { *n; });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// {{produces}}
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// Since Rust CURRENT_RUSTC_VERSION, boxed slices implement `IntoIterator`. However, to avoid
|
||||||
|
/// breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still
|
||||||
|
/// behave as `(&boxed_slice).into_iter()`, returning an iterator over
|
||||||
|
/// references, just like in Rust CURRENT_RUSTC_VERSION and earlier.
|
||||||
|
/// This only applies to the method call syntax `boxed_slice.into_iter()`, not to
|
||||||
|
/// any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`.
|
||||||
|
pub BOXED_SLICE_INTO_ITER,
|
||||||
|
Warn,
|
||||||
|
"detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
|
||||||
|
@future_incompatible = FutureIncompatibleInfo {
|
||||||
|
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ShadowedIntoIter;
|
||||||
|
|
||||||
|
impl_lint_pass!(ShadowedIntoIter => [ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||||
|
let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the method call actually calls the libcore
|
||||||
|
// `IntoIterator::into_iter`.
|
||||||
|
let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if Some(method_def_id) != cx.tcx.lang_items().into_iter_fn() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As this is a method call expression, we have at least one argument.
|
||||||
|
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
|
||||||
|
let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
|
||||||
|
|
||||||
|
let adjusted_receiver_tys: Vec<_> =
|
||||||
|
[receiver_ty].into_iter().chain(adjustments.iter().map(|adj| adj.target)).collect();
|
||||||
|
|
||||||
|
fn is_ref_to_array(ty: Ty<'_>) -> bool {
|
||||||
|
if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false }
|
||||||
|
}
|
||||||
|
fn is_boxed_slice(ty: Ty<'_>) -> bool {
|
||||||
|
ty.is_box() && ty.boxed_ty().is_slice()
|
||||||
|
}
|
||||||
|
fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool {
|
||||||
|
if let ty::Ref(_, pointee_ty, _) = *ty.kind() {
|
||||||
|
is_boxed_slice(pointee_ty)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (lint, target, edition, can_suggest_ufcs) =
|
||||||
|
if is_ref_to_array(*adjusted_receiver_tys.last().unwrap())
|
||||||
|
&& let Some(idx) = adjusted_receiver_tys
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.take_while(|ty| !is_ref_to_array(*ty))
|
||||||
|
.position(|ty| ty.is_array())
|
||||||
|
{
|
||||||
|
(ARRAY_INTO_ITER, "[T; N]", "2021", idx == 0)
|
||||||
|
} else if is_ref_to_boxed_slice(*adjusted_receiver_tys.last().unwrap())
|
||||||
|
&& let Some(idx) = adjusted_receiver_tys
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.take_while(|ty| !is_ref_to_boxed_slice(*ty))
|
||||||
|
.position(|ty| is_boxed_slice(ty))
|
||||||
|
{
|
||||||
|
(BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If this expression comes from the `IntoIter::into_iter` inside of a for loop,
|
||||||
|
// we should just suggest removing the `.into_iter()` or changing it to `.iter()`
|
||||||
|
// to disambiguate if we want to iterate by-value or by-ref.
|
||||||
|
let sub = if let Some((_, hir::Node::Expr(parent_expr))) =
|
||||||
|
cx.tcx.hir().parent_iter(expr.hir_id).nth(1)
|
||||||
|
&& let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) =
|
||||||
|
&parent_expr.kind
|
||||||
|
&& let hir::ExprKind::Call(path, [_]) = &arg.kind
|
||||||
|
&& let hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoIterIntoIter, ..)) =
|
||||||
|
&path.kind
|
||||||
|
{
|
||||||
|
Some(ShadowedIntoIterDiagSub::RemoveIntoIter {
|
||||||
|
span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
|
||||||
|
})
|
||||||
|
} else if can_suggest_ufcs {
|
||||||
|
Some(ShadowedIntoIterDiagSub::UseExplicitIntoIter {
|
||||||
|
start_span: expr.span.shrink_to_lo(),
|
||||||
|
end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.emit_span_lint(
|
||||||
|
lint,
|
||||||
|
call.ident.span,
|
||||||
|
ShadowedIntoIterDiag { target, edition, suggestion: call.ident.span, sub },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,11 +39,16 @@ pub struct TraitDef {
|
||||||
/// also have already switched to the new trait solver.
|
/// also have already switched to the new trait solver.
|
||||||
pub is_coinductive: bool,
|
pub is_coinductive: bool,
|
||||||
|
|
||||||
/// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]`
|
/// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]`
|
||||||
/// attribute, indicating that editions before 2021 should not consider this trait
|
/// attribute, indicating that editions before 2021 should not consider this trait
|
||||||
/// during method dispatch if the receiver is an array.
|
/// during method dispatch if the receiver is an array.
|
||||||
pub skip_array_during_method_dispatch: bool,
|
pub skip_array_during_method_dispatch: bool,
|
||||||
|
|
||||||
|
/// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(boxed_slice)]`
|
||||||
|
/// attribute, indicating that editions before 2024 should not consider this trait
|
||||||
|
/// during method dispatch if the receiver is a boxed slice.
|
||||||
|
pub skip_boxed_slice_during_method_dispatch: bool,
|
||||||
|
|
||||||
/// Used to determine whether the standard library is allowed to specialize
|
/// Used to determine whether the standard library is allowed to specialize
|
||||||
/// on this trait.
|
/// on this trait.
|
||||||
pub specialization_kind: TraitSpecializationKind,
|
pub specialization_kind: TraitSpecializationKind,
|
||||||
|
|
|
@ -505,6 +505,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitDef {
|
||||||
is_marker: self.is_marker,
|
is_marker: self.is_marker,
|
||||||
is_coinductive: self.is_coinductive,
|
is_coinductive: self.is_coinductive,
|
||||||
skip_array_during_method_dispatch: self.skip_array_during_method_dispatch,
|
skip_array_during_method_dispatch: self.skip_array_during_method_dispatch,
|
||||||
|
skip_boxed_slice_during_method_dispatch: self.skip_boxed_slice_during_method_dispatch,
|
||||||
specialization_kind: self.specialization_kind.stable(tables),
|
specialization_kind: self.specialization_kind.stable(tables),
|
||||||
must_implement_one_of: self
|
must_implement_one_of: self
|
||||||
.must_implement_one_of
|
.must_implement_one_of
|
||||||
|
|
|
@ -1633,7 +1633,7 @@ symbols! {
|
||||||
rustc_reservation_impl,
|
rustc_reservation_impl,
|
||||||
rustc_safe_intrinsic,
|
rustc_safe_intrinsic,
|
||||||
rustc_serialize,
|
rustc_serialize,
|
||||||
rustc_skip_array_during_method_dispatch,
|
rustc_skip_during_method_dispatch,
|
||||||
rustc_specialization_trait,
|
rustc_specialization_trait,
|
||||||
rustc_std_internal_symbol,
|
rustc_std_internal_symbol,
|
||||||
rustc_strict_coherence,
|
rustc_strict_coherence,
|
||||||
|
|
|
@ -1205,6 +1205,7 @@ pub struct TraitDecl {
|
||||||
pub is_marker: bool,
|
pub is_marker: bool,
|
||||||
pub is_coinductive: bool,
|
pub is_coinductive: bool,
|
||||||
pub skip_array_during_method_dispatch: bool,
|
pub skip_array_during_method_dispatch: bool,
|
||||||
|
pub skip_boxed_slice_during_method_dispatch: bool,
|
||||||
pub specialization_kind: TraitSpecializationKind,
|
pub specialization_kind: TraitSpecializationKind,
|
||||||
pub must_implement_one_of: Option<Vec<Ident>>,
|
pub must_implement_one_of: Option<Vec<Ident>>,
|
||||||
pub implement_via_object: bool,
|
pub implement_via_object: bool,
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub struct IntoIter<T, const N: usize> {
|
||||||
alive: IndexRange,
|
alive: IndexRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator`
|
// Note: the `#[rustc_skip_during_method_dispatch(array)]` on `trait IntoIterator`
|
||||||
// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
|
// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
|
||||||
// so those calls will still resolve to the slice implementation, by reference.
|
// so those calls will still resolve to the slice implementation, by reference.
|
||||||
#[stable(feature = "array_into_iter_impl", since = "1.53.0")]
|
#[stable(feature = "array_into_iter_impl", since = "1.53.0")]
|
||||||
|
|
|
@ -268,7 +268,6 @@ where
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[rustc_diagnostic_item = "IntoIterator"]
|
#[rustc_diagnostic_item = "IntoIterator"]
|
||||||
#[rustc_skip_array_during_method_dispatch]
|
|
||||||
#[rustc_on_unimplemented(
|
#[rustc_on_unimplemented(
|
||||||
on(
|
on(
|
||||||
_Self = "core::ops::range::RangeTo<Idx>",
|
_Self = "core::ops::range::RangeTo<Idx>",
|
||||||
|
@ -312,6 +311,8 @@ where
|
||||||
label = "`{Self}` is not an iterator",
|
label = "`{Self}` is not an iterator",
|
||||||
message = "`{Self}` is not an iterator"
|
message = "`{Self}` is not an iterator"
|
||||||
)]
|
)]
|
||||||
|
#[cfg_attr(bootstrap, rustc_skip_array_during_method_dispatch)]
|
||||||
|
#[cfg_attr(not(bootstrap), rustc_skip_during_method_dispatch(array, boxed_slice))]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub trait IntoIterator {
|
pub trait IntoIterator {
|
||||||
/// The type of the elements being iterated over.
|
/// The type of the elements being iterated over.
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
//@ check-pass
|
||||||
|
//@ edition:2018
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::slice::Iter;
|
||||||
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let boxed_slice = vec![0; 10].into_boxed_slice();
|
||||||
|
|
||||||
|
// Before 2024, the method dispatched to `IntoIterator for Box<[T]>`,
|
||||||
|
// which we continue to support for compatibility.
|
||||||
|
let _: Iter<'_, i32> = boxed_slice.into_iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
|
||||||
|
let _: Iter<'_, i32> = Box::new(boxed_slice.clone()).into_iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
|
||||||
|
let _: Iter<'_, i32> = Rc::new(boxed_slice.clone()).into_iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
let _: Iter<'_, i32> = Array(boxed_slice.clone()).into_iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
|
||||||
|
// But you can always use the trait method explicitly as an boxed_slice.
|
||||||
|
let _: IntoIter<i32> = IntoIterator::into_iter(boxed_slice);
|
||||||
|
|
||||||
|
for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {}
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
}
|
||||||
|
|
||||||
|
/// User type that dereferences to a boxed slice.
|
||||||
|
struct Array(Box<[i32]>);
|
||||||
|
|
||||||
|
impl Deref for Array {
|
||||||
|
type Target = Box<[i32]>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-2021.rs:14:40
|
||||||
|
|
|
||||||
|
LL | let _: Iter<'_, i32> = boxed_slice.into_iter();
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: `#[warn(boxed_slice_into_iter)]` on by default
|
||||||
|
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
||||||
|
|
|
||||||
|
LL | let _: Iter<'_, i32> = boxed_slice.iter();
|
||||||
|
| ~~~~
|
||||||
|
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
||||||
|
|
|
||||||
|
LL | let _: Iter<'_, i32> = IntoIterator::into_iter(boxed_slice);
|
||||||
|
| ++++++++++++++++++++++++ ~
|
||||||
|
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-2021.rs:18:58
|
||||||
|
|
|
||||||
|
LL | let _: Iter<'_, i32> = Box::new(boxed_slice.clone()).into_iter();
|
||||||
|
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-2021.rs:22:57
|
||||||
|
|
|
||||||
|
LL | let _: Iter<'_, i32> = Rc::new(boxed_slice.clone()).into_iter();
|
||||||
|
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-2021.rs:25:55
|
||||||
|
|
|
||||||
|
LL | let _: Iter<'_, i32> = Array(boxed_slice.clone()).into_iter();
|
||||||
|
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-2021.rs:32:48
|
||||||
|
|
|
||||||
|
LL | for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
||||||
|
|
|
||||||
|
LL | for _ in (Box::new([1, 2, 3]) as Box<[_]>).iter() {}
|
||||||
|
| ~~~~
|
||||||
|
help: or remove `.into_iter()` to iterate by value
|
||||||
|
|
|
||||||
|
LL - for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {}
|
||||||
|
LL + for _ in (Box::new([1, 2, 3]) as Box<[_]>) {}
|
||||||
|
|
|
||||||
|
|
||||||
|
warning: 5 warnings emitted
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
//@ check-pass
|
||||||
|
//@ edition:2024
|
||||||
|
//@ compile-flags: -Zunstable-options
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let boxed_slice = vec![0; 10].into_boxed_slice();
|
||||||
|
|
||||||
|
// In 2021, the method dispatches to `IntoIterator for [T; N]`.
|
||||||
|
let _: IntoIter<i32> = boxed_slice.clone().into_iter();
|
||||||
|
|
||||||
|
// And through other boxes.
|
||||||
|
let _: IntoIter<i32> = Box::new(boxed_slice.clone()).into_iter();
|
||||||
|
|
||||||
|
// You can always use the trait method explicitly as a boxed_slice.
|
||||||
|
let _: IntoIter<i32> = IntoIterator::into_iter(boxed_slice.clone());
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//@ run-pass
|
||||||
|
//@ run-rustfix
|
||||||
|
//@ rustfix-only-machine-applicable
|
||||||
|
|
||||||
|
#[allow(unused_must_use, unused_allocation)]
|
||||||
|
fn main() {
|
||||||
|
let boxed = vec![1, 2].into_boxed_slice();
|
||||||
|
|
||||||
|
// Expressions that should trigger the lint
|
||||||
|
boxed.iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
Box::new(boxed.clone()).iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
Box::new(Box::new(boxed.clone())).iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
|
||||||
|
// Expressions that should not
|
||||||
|
(&boxed).into_iter();
|
||||||
|
|
||||||
|
for _ in &boxed {}
|
||||||
|
(&boxed as &[_]).into_iter();
|
||||||
|
boxed[..].into_iter();
|
||||||
|
std::iter::IntoIterator::into_iter(&boxed);
|
||||||
|
|
||||||
|
#[allow(boxed_slice_into_iter)]
|
||||||
|
boxed.into_iter();
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//@ run-pass
|
||||||
|
//@ run-rustfix
|
||||||
|
//@ rustfix-only-machine-applicable
|
||||||
|
|
||||||
|
#[allow(unused_must_use, unused_allocation)]
|
||||||
|
fn main() {
|
||||||
|
let boxed = vec![1, 2].into_boxed_slice();
|
||||||
|
|
||||||
|
// Expressions that should trigger the lint
|
||||||
|
boxed.into_iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
Box::new(boxed.clone()).into_iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
Box::new(Box::new(boxed.clone())).into_iter();
|
||||||
|
//~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
|
||||||
|
//~| WARNING this changes meaning
|
||||||
|
|
||||||
|
// Expressions that should not
|
||||||
|
(&boxed).into_iter();
|
||||||
|
|
||||||
|
for _ in &boxed {}
|
||||||
|
(&boxed as &[_]).into_iter();
|
||||||
|
boxed[..].into_iter();
|
||||||
|
std::iter::IntoIterator::into_iter(&boxed);
|
||||||
|
|
||||||
|
#[allow(boxed_slice_into_iter)]
|
||||||
|
boxed.into_iter();
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-lint.rs:10:11
|
||||||
|
|
|
||||||
|
LL | boxed.into_iter();
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: `#[warn(boxed_slice_into_iter)]` on by default
|
||||||
|
help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
|
||||||
|
|
|
||||||
|
LL | boxed.iter();
|
||||||
|
| ~~~~
|
||||||
|
help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
||||||
|
|
|
||||||
|
LL | IntoIterator::into_iter(boxed);
|
||||||
|
| ++++++++++++++++++++++++ ~
|
||||||
|
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-lint.rs:13:29
|
||||||
|
|
|
||||||
|
LL | Box::new(boxed.clone()).into_iter();
|
||||||
|
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
|
||||||
|
warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
|
||||||
|
--> $DIR/into-iter-on-boxed-slices-lint.rs:16:39
|
||||||
|
|
|
||||||
|
LL | Box::new(Box::new(boxed.clone())).into_iter();
|
||||||
|
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
|
||||||
|
warning: 3 warnings emitted
|
||||||
|
|
|
@ -76,8 +76,8 @@ LL | x.cmp(&x);
|
||||||
which is required by `&mut dyn T: Iterator`
|
which is required by `&mut dyn T: Iterator`
|
||||||
= help: items from traits can only be used if the trait is implemented and in scope
|
= help: items from traits can only be used if the trait is implemented and in scope
|
||||||
= note: the following traits define an item `cmp`, perhaps you need to implement one of them:
|
= note: the following traits define an item `cmp`, perhaps you need to implement one of them:
|
||||||
candidate #1: `Iterator`
|
candidate #1: `Ord`
|
||||||
candidate #2: `Ord`
|
= note: the trait `Iterator` defines an item `cmp`, but is explicitly unimplemented
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue