1 use super::EXPLICIT_ITER_LOOP;
2 use clippy_utils::diagnostics::span_lint_and_sugg;
3 use clippy_utils::is_trait_method;
4 use clippy_utils::source::snippet_with_applicability;
5 use clippy_utils::ty::is_type_diagnostic_item;
6 use rustc_errors::Applicability;
7 use rustc_hir::{Expr, Mutability};
8 use rustc_lint::LateContext;
9 use rustc_middle::ty::{self, Ty, TyS};
12 pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
13 let should_lint = match method_name {
14 "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
15 "into_iter" if is_trait_method(cx, arg, sym::IntoIterator) => {
16 let receiver_ty = cx.typeck_results().expr_ty(self_arg);
17 let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
18 let ref_receiver_ty = cx.tcx.mk_ref(
19 cx.tcx.lifetimes.re_erased,
22 mutbl: Mutability::Not,
25 TyS::same_type(receiver_ty_adjusted, ref_receiver_ty)
34 let mut applicability = Applicability::MachineApplicable;
35 let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
36 let muta = if method_name == "iter_mut" { "mut " } else { "" };
41 "it is more concise to loop over references to containers instead of using explicit \
43 "to write this more concisely, try",
44 format!("&{}{}", muta, object),
49 /// Returns `true` if the type of expr is one that provides `IntoIterator` impls
50 /// for `&T` and `&mut T`, such as `Vec`.
52 fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
53 // no walk_ptrs_ty: calling iter() on a reference can make sense because it
54 // will allow further borrows afterwards
55 let ty = cx.typeck_results().expr_ty(e);
56 is_iterable_array(ty, cx) ||
57 is_type_diagnostic_item(cx, ty, sym::Vec) ||
58 is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
59 is_type_diagnostic_item(cx, ty, sym::HashMap) ||
60 is_type_diagnostic_item(cx, ty, sym::HashSet) ||
61 is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
62 is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
63 is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
64 is_type_diagnostic_item(cx, ty, sym::BTreeSet)
67 fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
68 // IntoIterator is currently only implemented for array sizes <= 32 in rustc
71 .try_eval_usize(cx.tcx, cx.param_env)
72 .map_or(false, |val| (0..=32).contains(&val)),