76 lines
2.8 KiB
Rust
76 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_target_usize(cx.tcx, cx.param_env)
|
|
.map_or(false, |val| (0..=32).contains(&val)),
|
|
_ => false,
|
|
}
|
|
}
|