mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-12-20 18:13:36 +00:00
99af4c8971
it ignored regions and constants in adts, but didn't do so for references or any other types. This seemed quite weird
75 lines
2.8 KiB
Rust
75 lines
2.8 KiB
Rust
use super::EXPLICIT_ITER_LOOP;
|
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
use clippy_utils::is_trait_method;
|
|
use clippy_utils::source::snippet_with_applicability;
|
|
use clippy_utils::ty::is_type_diagnostic_item;
|
|
use rustc_errors::Applicability;
|
|
use rustc_hir::{Expr, Mutability};
|
|
use rustc_lint::LateContext;
|
|
use rustc_middle::ty::{self, 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) => {
|
|
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(
|
|
cx.tcx.lifetimes.re_erased,
|
|
ty::TypeAndMut {
|
|
ty: receiver_ty,
|
|
mutbl: Mutability::Not,
|
|
},
|
|
);
|
|
receiver_ty_adjusted == ref_receiver_ty
|
|
},
|
|
_ => false,
|
|
};
|
|
|
|
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 { "" };
|
|
span_lint_and_sugg(
|
|
cx,
|
|
EXPLICIT_ITER_LOOP,
|
|
arg.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),
|
|
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)
|
|
}
|
|
|
|
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_usize(cx.tcx, cx.param_env)
|
|
.map_or(false, |val| (0..=32).contains(&val)),
|
|
_ => false,
|
|
}
|
|
}
|