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::<shadow::Shadow>::default());
|
||||||
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
|
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::<main_recursion::MainRecursion>::default());
|
||||||
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
|
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
|
||||||
store.register_late_pass(|_| Box::new(entry::HashMapPass));
|
store.register_late_pass(|_| Box::new(entry::HashMapPass));
|
||||||
|
|
|
@ -1,18 +1,37 @@
|
||||||
use super::EXPLICIT_ITER_LOOP;
|
use super::EXPLICIT_ITER_LOOP;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::is_trait_method;
|
use clippy_utils::is_trait_method;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
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_errors::Applicability;
|
||||||
use rustc_hir::{Expr, Mutability};
|
use rustc_hir::{Expr, Mutability};
|
||||||
use rustc_lint::LateContext;
|
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;
|
use rustc_span::sym;
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
|
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, method_name: &str, msrv: &Msrv) {
|
||||||
let should_lint = match method_name {
|
let borrow_kind = match method_name {
|
||||||
"iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
|
"iter" | "iter_mut" => match is_ref_iterable(cx, self_arg, call_expr) {
|
||||||
"into_iter" if is_trait_method(cx, arg, sym::IntoIterator) => {
|
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 = cx.typeck_results().expr_ty(self_arg);
|
||||||
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
||||||
let ref_receiver_ty = cx.tcx.mk_ref(
|
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,
|
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 mut applicability = Applicability::MachineApplicable;
|
||||||
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
|
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(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
EXPLICIT_ITER_LOOP,
|
EXPLICIT_ITER_LOOP,
|
||||||
arg.span,
|
call_expr.span,
|
||||||
"it is more concise to loop over references to containers instead of using explicit \
|
"it is more concise to loop over references to containers instead of using explicit \
|
||||||
iteration methods",
|
iteration methods",
|
||||||
"to write this more concisely, try",
|
"to write this more concisely, try",
|
||||||
format!("&{muta}{object}"),
|
format!("{prefix}{object}"),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
|
enum AdjustKind {
|
||||||
/// for `&T` and `&mut T`, such as `Vec`.
|
None,
|
||||||
#[rustfmt::skip]
|
Borrow,
|
||||||
fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
BorrowMut,
|
||||||
// no walk_ptrs_ty: calling iter() on a reference can make sense because it
|
Deref,
|
||||||
// will allow further borrows afterwards
|
Reborrow,
|
||||||
let ty = cx.typeck_results().expr_ty(e);
|
ReborrowMut,
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
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 {
|
fn auto_borrow(mutbl: AutoBorrowMutability) -> Self {
|
||||||
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
match mutbl {
|
||||||
match ty.kind() {
|
AutoBorrowMutability::Not => Self::Borrow,
|
||||||
ty::Array(_, n) => n
|
AutoBorrowMutability::Mut { .. } => Self::BorrowMut,
|
||||||
.try_eval_target_usize(cx.tcx, cx.param_env)
|
}
|
||||||
.map_or(false, |val| (0..=32).contains(&val)),
|
}
|
||||||
_ => false,
|
|
||||||
|
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;
|
mod while_let_on_iterator;
|
||||||
|
|
||||||
use clippy_utils::higher;
|
use clippy_utils::higher;
|
||||||
|
use clippy_utils::msrvs::Msrv;
|
||||||
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
|
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
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 rustc_span::source_map::Span;
|
||||||
use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
|
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"
|
"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_MEMCPY,
|
||||||
MANUAL_FLATTEN,
|
MANUAL_FLATTEN,
|
||||||
NEEDLESS_RANGE_LOOP,
|
NEEDLESS_RANGE_LOOP,
|
||||||
|
@ -645,7 +654,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
||||||
if body.span.from_expansion() {
|
if body.span.from_expansion() {
|
||||||
return;
|
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 {
|
if let ExprKind::Block(block, _) = body.kind {
|
||||||
never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
|
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>(
|
impl Loops {
|
||||||
cx: &LateContext<'tcx>,
|
fn check_for_loop<'tcx>(
|
||||||
pat: &'tcx Pat<'_>,
|
&self,
|
||||||
arg: &'tcx Expr<'_>,
|
cx: &LateContext<'tcx>,
|
||||||
body: &'tcx Expr<'_>,
|
pat: &'tcx Pat<'_>,
|
||||||
expr: &'tcx Expr<'_>,
|
arg: &'tcx Expr<'_>,
|
||||||
span: Span,
|
body: &'tcx Expr<'_>,
|
||||||
) {
|
expr: &'tcx Expr<'_>,
|
||||||
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
|
span: Span,
|
||||||
if !is_manual_memcpy_triggered {
|
) {
|
||||||
needless_range_loop::check(cx, pat, arg, body, expr);
|
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
|
||||||
explicit_counter_loop::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<'_>) {
|
fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
|
||||||
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
|
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
|
||||||
let method_name = method.ident.as_str();
|
let method_name = method.ident.as_str();
|
||||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||||
match method_name {
|
match method_name {
|
||||||
"iter" | "iter_mut" => {
|
"iter" | "iter_mut" => {
|
||||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
explicit_iter_loop::check(cx, self_arg, arg, method_name, &self.msrv);
|
||||||
},
|
},
|
||||||
"into_iter" => {
|
"into_iter" => {
|
||||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
explicit_iter_loop::check(cx, self_arg, arg, method_name, &self.msrv);
|
||||||
explicit_into_iter_loop::check(cx, self_arg, arg);
|
explicit_into_iter_loop::check(cx, self_arg, arg);
|
||||||
},
|
},
|
||||||
"next" => {
|
"next" => {
|
||||||
iter_next_loop::check(cx, arg);
|
iter_next_loop::check(cx, arg);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ msrv_aliases! {
|
||||||
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
|
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
|
||||||
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
|
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
|
||||||
1,50,0 { BOOL_THEN, CLAMP }
|
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,46,0 { CONST_IF_MATCH }
|
||||||
1,45,0 { STR_STRIP_PREFIX }
|
1,45,0 { STR_STRIP_PREFIX }
|
||||||
1,43,0 { LOG2_10, LOG10_2 }
|
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,
|
_ => 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
|
//@run-rustfix
|
||||||
#![allow(dead_code, unused)]
|
#![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::*;
|
use std::collections::*;
|
||||||
|
|
||||||
|
@ -52,11 +52,11 @@ fn main() {
|
||||||
|
|
||||||
for _v in &[1, 2, 3] {}
|
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; 32] {}
|
||||||
|
|
||||||
for _v in [0; 33].iter() {} // no error
|
for _v in &[0; 33] {} // no error
|
||||||
|
|
||||||
let ll: LinkedList<()> = LinkedList::new();
|
let ll: LinkedList<()> = LinkedList::new();
|
||||||
for _v in &ll {}
|
for _v in &ll {}
|
||||||
|
@ -272,7 +272,7 @@ mod issue_4958 {
|
||||||
let rr = &&t;
|
let rr = &&t;
|
||||||
|
|
||||||
// This case is handled by `explicit_iter_loop`. No idea why.
|
// This case is handled by `explicit_iter_loop`. No idea why.
|
||||||
for _ in &t {}
|
for _ in t {}
|
||||||
|
|
||||||
for _ in r {}
|
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
|
//@run-rustfix
|
||||||
#![allow(dead_code, unused)]
|
#![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::*;
|
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() {}
|
LL | for _v in [1, 2, 3].iter() {}
|
||||||
| ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
|
| ^^^^^^^^^^^^^^^^ 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
|
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||||
--> $DIR/for_loop_fixable.rs:57:15
|
--> $DIR/for_loop_fixable.rs:57:15
|
||||||
|
|
|
|
||||||
LL | for _v in [0; 32].iter() {}
|
LL | for _v in [0; 32].iter() {}
|
||||||
| ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
|
| ^^^^^^^^^^^^^^ 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
|
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||||
--> $DIR/for_loop_fixable.rs:62:15
|
--> $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
|
--> $DIR/for_loop_fixable.rs:275:18
|
||||||
|
|
|
|
||||||
LL | for _ in t.into_iter() {}
|
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
|
error: it is more concise to loop over containers instead of using explicit iteration methods
|
||||||
--> $DIR/for_loop_fixable.rs:277:18
|
--> $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() {}
|
LL | for _ in r.into_iter() {}
|
||||||
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
|
| ^^^^^^^^^^^^^ 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