mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 07:00:55 +00:00
Extend explicit_iter_loop
to all types
This commit is contained in:
parent
476efe92e7
commit
b6fa4d43d3
8 changed files with 460 additions and 86 deletions
|
@ -679,7 +679,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
});
|
||||
store.register_late_pass(|_| Box::<shadow::Shadow>::default());
|
||||
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
|
||||
store.register_late_pass(|_| Box::new(loops::Loops));
|
||||
store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv())));
|
||||
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
|
||||
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
|
||||
store.register_late_pass(|_| Box::new(entry::HashMapPass));
|
||||
|
|
|
@ -1,18 +1,37 @@
|
|||
use super::EXPLICIT_ITER_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::ty::{implements_trait_with_env, make_normalized_projection_with_regions, normalize_with_regions,
|
||||
make_normalized_projection, implements_trait, is_copy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, EarlyBinder, TypeAndMut, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
|
||||
let should_lint = match method_name {
|
||||
"iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
|
||||
"into_iter" if is_trait_method(cx, arg, sym::IntoIterator) => {
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, method_name: &str, msrv: &Msrv) {
|
||||
let borrow_kind = match method_name {
|
||||
"iter" | "iter_mut" => match is_ref_iterable(cx, self_arg, call_expr) {
|
||||
Some((kind, ty)) => {
|
||||
if let ty::Array(_, count) = *ty.peel_refs().kind() {
|
||||
if !ty.is_ref() {
|
||||
if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
|
||||
return;
|
||||
}
|
||||
} else if count.try_eval_target_usize(cx.tcx, cx.param_env).map_or(true, |x| x > 32)
|
||||
&& !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN)
|
||||
{
|
||||
return
|
||||
}
|
||||
}
|
||||
kind
|
||||
},
|
||||
None => return,
|
||||
},
|
||||
"into_iter" if is_trait_method(cx, call_expr, sym::IntoIterator) => {
|
||||
let receiver_ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
||||
let ref_receiver_ty = cx.tcx.mk_ref(
|
||||
|
@ -22,54 +41,159 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, m
|
|||
mutbl: Mutability::Not,
|
||||
},
|
||||
);
|
||||
receiver_ty_adjusted == ref_receiver_ty
|
||||
if receiver_ty_adjusted == ref_receiver_ty {
|
||||
AdjustKind::None
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if !should_lint {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
|
||||
let muta = if method_name == "iter_mut" { "mut " } else { "" };
|
||||
let prefix = match borrow_kind {
|
||||
AdjustKind::None => "",
|
||||
AdjustKind::Borrow => "&",
|
||||
AdjustKind::BorrowMut => "&mut ",
|
||||
AdjustKind::Deref => "*",
|
||||
AdjustKind::Reborrow => "&*",
|
||||
AdjustKind::ReborrowMut => "&mut *",
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
EXPLICIT_ITER_LOOP,
|
||||
arg.span,
|
||||
call_expr.span,
|
||||
"it is more concise to loop over references to containers instead of using explicit \
|
||||
iteration methods",
|
||||
"to write this more concisely, try",
|
||||
format!("&{muta}{object}"),
|
||||
format!("{prefix}{object}"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
|
||||
/// for `&T` and `&mut T`, such as `Vec`.
|
||||
#[rustfmt::skip]
|
||||
fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
// no walk_ptrs_ty: calling iter() on a reference can make sense because it
|
||||
// will allow further borrows afterwards
|
||||
let ty = cx.typeck_results().expr_ty(e);
|
||||
is_iterable_array(ty, cx) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::Vec) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::HashMap) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::HashSet) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::BTreeSet)
|
||||
enum AdjustKind {
|
||||
None,
|
||||
Borrow,
|
||||
BorrowMut,
|
||||
Deref,
|
||||
Reborrow,
|
||||
ReborrowMut,
|
||||
}
|
||||
impl AdjustKind {
|
||||
fn borrow(mutbl: Mutability) -> Self {
|
||||
match mutbl {
|
||||
Mutability::Not => Self::Borrow,
|
||||
Mutability::Mut => Self::BorrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
|
||||
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
||||
match ty.kind() {
|
||||
ty::Array(_, n) => n
|
||||
.try_eval_target_usize(cx.tcx, cx.param_env)
|
||||
.map_or(false, |val| (0..=32).contains(&val)),
|
||||
_ => false,
|
||||
fn auto_borrow(mutbl: AutoBorrowMutability) -> Self {
|
||||
match mutbl {
|
||||
AutoBorrowMutability::Not => Self::Borrow,
|
||||
AutoBorrowMutability::Mut { .. } => Self::BorrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow(mutbl: AutoBorrowMutability) -> Self {
|
||||
match mutbl {
|
||||
AutoBorrowMutability::Not => Self::Reborrow,
|
||||
AutoBorrowMutability::Mut { .. } => Self::ReborrowMut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an `iter` or `iter_mut` call returns `IntoIterator::IntoIter`. Returns how the
|
||||
/// argument needs to be adjusted.
|
||||
fn is_ref_iterable<'tcx>(cx: &LateContext<'tcx>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) -> Option<(AdjustKind, Ty<'tcx>)> {
|
||||
let typeck = cx.typeck_results();
|
||||
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
||||
&& let Some(fn_id) = typeck.type_dependent_def_id(call_expr.hir_id)
|
||||
&& let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
|
||||
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
|
||||
&& let param_env = cx.tcx.param_env(fn_id)
|
||||
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, [])
|
||||
&& let Some(into_iter_ty) =
|
||||
make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
|
||||
&& let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)
|
||||
&& into_iter_ty == req_res_ty
|
||||
{
|
||||
let adjustments = typeck.expr_adjustments(self_arg);
|
||||
let self_ty = typeck.expr_ty(self_arg);
|
||||
let self_is_copy = is_copy(cx, self_ty);
|
||||
|
||||
if adjustments.is_empty() && self_is_copy {
|
||||
return Some((AdjustKind::None, self_ty));
|
||||
}
|
||||
|
||||
let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty).subst(cx.tcx, typeck.node_substs(call_expr.hir_id)));
|
||||
if !adjustments.is_empty() && self_is_copy {
|
||||
if implements_trait(cx, self_ty, trait_id, &[])
|
||||
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
|
||||
&& ty == res_ty
|
||||
{
|
||||
return Some((AdjustKind::None, self_ty));
|
||||
}
|
||||
}
|
||||
|
||||
let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
|
||||
Some(mutbl)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(mutbl) = mutbl
|
||||
&& !self_ty.is_ref()
|
||||
{
|
||||
let self_ty = cx.tcx.mk_ref(cx.tcx.lifetimes.re_erased, TypeAndMut {
|
||||
ty: self_ty,
|
||||
mutbl,
|
||||
});
|
||||
if implements_trait(cx, self_ty, trait_id, &[])
|
||||
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
|
||||
&& ty == res_ty
|
||||
{
|
||||
return Some((AdjustKind::borrow(mutbl), self_ty));
|
||||
}
|
||||
}
|
||||
|
||||
match adjustments {
|
||||
[] => Some((AdjustKind::None, self_ty)),
|
||||
&[Adjustment { kind: Adjust::Deref(_), ..}, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)), target }, ..] => {
|
||||
if target != self_ty
|
||||
&& implements_trait(cx, target, trait_id, &[])
|
||||
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
|
||||
&& ty == res_ty
|
||||
{
|
||||
Some((AdjustKind::reborrow(mutbl), target))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
&[Adjustment { kind: Adjust::Deref(_), target }, ..] => {
|
||||
if is_copy(cx, target)
|
||||
&& implements_trait(cx, target, trait_id, &[])
|
||||
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
|
||||
&& ty == res_ty
|
||||
{
|
||||
Some((AdjustKind::Deref, target))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
&[Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)), target }, ..] => {
|
||||
if self_ty.is_ref()
|
||||
&& implements_trait(cx, target, trait_id, &[])
|
||||
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
|
||||
&& ty == res_ty
|
||||
{
|
||||
Some((AdjustKind::auto_borrow(mutbl), target))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,10 @@ mod while_let_loop;
|
|||
mod while_let_on_iterator;
|
||||
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
|
||||
|
||||
|
@ -606,7 +607,15 @@ declare_clippy_lint! {
|
|||
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Loops => [
|
||||
pub struct Loops {
|
||||
msrv: Msrv,
|
||||
}
|
||||
impl Loops {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
impl_lint_pass!(Loops => [
|
||||
MANUAL_MEMCPY,
|
||||
MANUAL_FLATTEN,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
|
@ -645,7 +654,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
|||
if body.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
check_for_loop(cx, pat, arg, body, expr, span);
|
||||
self.check_for_loop(cx, pat, arg, body, expr, span);
|
||||
if let ExprKind::Block(block, _) = body.kind {
|
||||
never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
|
||||
}
|
||||
|
@ -680,44 +689,47 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_for_loop<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
|
||||
if !is_manual_memcpy_triggered {
|
||||
needless_range_loop::check(cx, pat, arg, body, expr);
|
||||
explicit_counter_loop::check(cx, pat, arg, body, expr);
|
||||
impl Loops {
|
||||
fn check_for_loop<'tcx>(
|
||||
&self,
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
|
||||
if !is_manual_memcpy_triggered {
|
||||
needless_range_loop::check(cx, pat, arg, body, expr);
|
||||
explicit_counter_loop::check(cx, pat, arg, body, expr);
|
||||
}
|
||||
self.check_for_loop_arg(cx, pat, arg);
|
||||
for_kv_map::check(cx, pat, arg, body);
|
||||
mut_range_bound::check(cx, arg, body);
|
||||
single_element_loop::check(cx, pat, arg, body, expr);
|
||||
same_item_push::check(cx, pat, arg, body, expr);
|
||||
manual_flatten::check(cx, pat, arg, body, span);
|
||||
manual_find::check(cx, pat, arg, body, span, expr);
|
||||
}
|
||||
check_for_loop_arg(cx, pat, arg);
|
||||
for_kv_map::check(cx, pat, arg, body);
|
||||
mut_range_bound::check(cx, arg, body);
|
||||
single_element_loop::check(cx, pat, arg, body, expr);
|
||||
same_item_push::check(cx, pat, arg, body, expr);
|
||||
manual_flatten::check(cx, pat, arg, body, span);
|
||||
manual_find::check(cx, pat, arg, body, span, expr);
|
||||
}
|
||||
|
||||
fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
|
||||
let method_name = method.ident.as_str();
|
||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||
match method_name {
|
||||
"iter" | "iter_mut" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
||||
},
|
||||
"into_iter" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
||||
explicit_into_iter_loop::check(cx, self_arg, arg);
|
||||
},
|
||||
"next" => {
|
||||
iter_next_loop::check(cx, arg);
|
||||
},
|
||||
_ => {},
|
||||
fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
|
||||
let method_name = method.ident.as_str();
|
||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||
match method_name {
|
||||
"iter" | "iter_mut" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name, &self.msrv);
|
||||
},
|
||||
"into_iter" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name, &self.msrv);
|
||||
explicit_into_iter_loop::check(cx, self_arg, arg);
|
||||
},
|
||||
"next" => {
|
||||
iter_next_loop::check(cx, arg);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ msrv_aliases! {
|
|||
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
|
||||
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
|
||||
1,50,0 { BOOL_THEN, CLAMP }
|
||||
1,47,0 { TAU, IS_ASCII_DIGIT_CONST }
|
||||
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
|
||||
1,46,0 { CONST_IF_MATCH }
|
||||
1,45,0 { STR_STRIP_PREFIX }
|
||||
1,43,0 { LOG2_10, LOG10_2 }
|
||||
|
|
|
@ -1180,3 +1180,51 @@ pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_normalized_projection_with_regions<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
container_id: DefId,
|
||||
assoc_ty: Symbol,
|
||||
substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some((i, subst)) = ty
|
||||
.substs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, subst)| subst.has_late_bound_regions())
|
||||
{
|
||||
debug_assert!(
|
||||
false,
|
||||
"substs contain late-bound region at index `{i}` which can't be normalized.\n\
|
||||
use `TyCtxt::erase_late_bound_regions`\n\
|
||||
note: subst is `{subst:#?}`",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
let cause = rustc_middle::traits::ObligationCause::dummy();
|
||||
match tcx
|
||||
.infer_ctxt()
|
||||
.build()
|
||||
.at(&cause, param_env)
|
||||
.query_normalize(tcx.mk_projection(ty.def_id, ty.substs))
|
||||
{
|
||||
Ok(ty) => Some(ty.value),
|
||||
Err(e) => {
|
||||
debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
|
||||
}
|
||||
|
||||
pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let cause = rustc_middle::traits::ObligationCause::dummy();
|
||||
match tcx.infer_ctxt().build().at(&cause, param_env).query_normalize(ty) {
|
||||
Ok(ty) => ty.value,
|
||||
Err(_) => ty,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//@run-rustfix
|
||||
#![allow(dead_code, unused)]
|
||||
#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
|
||||
#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::deref_addrof)]
|
||||
|
||||
use std::collections::*;
|
||||
|
||||
|
@ -52,11 +52,11 @@ fn main() {
|
|||
|
||||
for _v in &[1, 2, 3] {}
|
||||
|
||||
for _v in (&mut [1, 2, 3]).iter() {} // no error
|
||||
for _v in &*(&mut [1, 2, 3]) {} // no error
|
||||
|
||||
for _v in &[0; 32] {}
|
||||
|
||||
for _v in [0; 33].iter() {} // no error
|
||||
for _v in &[0; 33] {} // no error
|
||||
|
||||
let ll: LinkedList<()> = LinkedList::new();
|
||||
for _v in &ll {}
|
||||
|
@ -272,7 +272,7 @@ mod issue_4958 {
|
|||
let rr = &&t;
|
||||
|
||||
// This case is handled by `explicit_iter_loop`. No idea why.
|
||||
for _ in &t {}
|
||||
for _ in t {}
|
||||
|
||||
for _ in r {}
|
||||
|
||||
|
@ -307,3 +307,86 @@ mod issue_6900 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IntoIterDiffTy;
|
||||
impl IntoIterator for &'_ IntoIterDiffTy {
|
||||
type Item = &'static ();
|
||||
type IntoIter = core::slice::Iter<'static, ()>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl IntoIterDiffTy {
|
||||
fn iter(&self) -> core::slice::Iter<'static, i32> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct IntoIterDiffSig;
|
||||
impl IntoIterator for &'_ IntoIterDiffSig {
|
||||
type Item = &'static ();
|
||||
type IntoIter = core::slice::Iter<'static, ()>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl IntoIterDiffSig {
|
||||
fn iter(&self, _: u32) -> core::slice::Iter<'static, ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct IntoIterDiffLt<'a>(&'a ());
|
||||
impl<'a> IntoIterator for &'a IntoIterDiffLt<'_> {
|
||||
type Item = &'a ();
|
||||
type IntoIter = core::slice::Iter<'a, ()>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<'a> IntoIterDiffLt<'a> {
|
||||
fn iter(&self) -> core::slice::Iter<'a, ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomType;
|
||||
impl<'a> IntoIterator for &'a CustomType {
|
||||
type Item = &'a u32;
|
||||
type IntoIter = core::slice::Iter<'a, u32>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<'a> IntoIterator for &'a mut CustomType {
|
||||
type Item = &'a mut u32;
|
||||
type IntoIter = core::slice::IterMut<'a, u32>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl CustomType {
|
||||
fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> core::slice::IterMut<'_, u32> {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(clippy::explicit_iter_loop)]
|
||||
fn _f() {
|
||||
let x = IntoIterDiffTy;
|
||||
for _ in x.iter() {}
|
||||
|
||||
let x = IntoIterDiffSig;
|
||||
for _ in x.iter(0) {}
|
||||
|
||||
let x = IntoIterDiffLt(&());
|
||||
for _ in x.iter() {}
|
||||
|
||||
let mut x = CustomType;
|
||||
for _ in &x {}
|
||||
for _ in &mut x {}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//@run-rustfix
|
||||
#![allow(dead_code, unused)]
|
||||
#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
|
||||
#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::deref_addrof)]
|
||||
|
||||
use std::collections::*;
|
||||
|
||||
|
@ -307,3 +307,86 @@ mod issue_6900 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IntoIterDiffTy;
|
||||
impl IntoIterator for &'_ IntoIterDiffTy {
|
||||
type Item = &'static ();
|
||||
type IntoIter = core::slice::Iter<'static, ()>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl IntoIterDiffTy {
|
||||
fn iter(&self) -> core::slice::Iter<'static, i32> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct IntoIterDiffSig;
|
||||
impl IntoIterator for &'_ IntoIterDiffSig {
|
||||
type Item = &'static ();
|
||||
type IntoIter = core::slice::Iter<'static, ()>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl IntoIterDiffSig {
|
||||
fn iter(&self, _: u32) -> core::slice::Iter<'static, ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct IntoIterDiffLt<'a>(&'a ());
|
||||
impl<'a> IntoIterator for &'a IntoIterDiffLt<'_> {
|
||||
type Item = &'a ();
|
||||
type IntoIter = core::slice::Iter<'a, ()>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<'a> IntoIterDiffLt<'a> {
|
||||
fn iter(&self) -> core::slice::Iter<'a, ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomType;
|
||||
impl<'a> IntoIterator for &'a CustomType {
|
||||
type Item = &'a u32;
|
||||
type IntoIter = core::slice::Iter<'a, u32>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<'a> IntoIterator for &'a mut CustomType {
|
||||
type Item = &'a mut u32;
|
||||
type IntoIter = core::slice::IterMut<'a, u32>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl CustomType {
|
||||
fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> core::slice::IterMut<'_, u32> {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(clippy::explicit_iter_loop)]
|
||||
fn _f() {
|
||||
let x = IntoIterDiffTy;
|
||||
for _ in x.iter() {}
|
||||
|
||||
let x = IntoIterDiffSig;
|
||||
for _ in x.iter(0) {}
|
||||
|
||||
let x = IntoIterDiffLt(&());
|
||||
for _ in x.iter() {}
|
||||
|
||||
let mut x = CustomType;
|
||||
for _ in x.iter() {}
|
||||
for _ in x.iter_mut() {}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,24 @@ error: it is more concise to loop over references to containers instead of using
|
|||
LL | for _v in [1, 2, 3].iter() {}
|
||||
| ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
|
||||
|
||||
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||
--> $DIR/for_loop_fixable.rs:55:15
|
||||
|
|
||||
LL | for _v in (&mut [1, 2, 3]).iter() {} // no error
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*(&mut [1, 2, 3])`
|
||||
|
||||
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||
--> $DIR/for_loop_fixable.rs:57:15
|
||||
|
|
||||
LL | for _v in [0; 32].iter() {}
|
||||
| ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
|
||||
|
||||
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||
--> $DIR/for_loop_fixable.rs:59:15
|
||||
|
|
||||
LL | for _v in [0; 33].iter() {} // no error
|
||||
| ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 33]`
|
||||
|
||||
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||
--> $DIR/for_loop_fixable.rs:62:15
|
||||
|
|
||||
|
@ -84,7 +96,7 @@ error: it is more concise to loop over references to containers instead of using
|
|||
--> $DIR/for_loop_fixable.rs:275:18
|
||||
|
|
||||
LL | for _ in t.into_iter() {}
|
||||
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
|
||||
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `t`
|
||||
|
||||
error: it is more concise to loop over containers instead of using explicit iteration methods
|
||||
--> $DIR/for_loop_fixable.rs:277:18
|
||||
|
@ -92,5 +104,17 @@ error: it is more concise to loop over containers instead of using explicit iter
|
|||
LL | for _ in r.into_iter() {}
|
||||
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||
--> $DIR/for_loop_fixable.rs:390:14
|
||||
|
|
||||
LL | for _ in x.iter() {}
|
||||
| ^^^^^^^^ help: to write this more concisely, try: `&x`
|
||||
|
||||
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||
--> $DIR/for_loop_fixable.rs:391:14
|
||||
|
|
||||
LL | for _ in x.iter_mut() {}
|
||||
| ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut x`
|
||||
|
||||
error: aborting due to 19 previous errors
|
||||
|
||||
|
|
Loading…
Reference in a new issue