]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
Merge commit 'ac0e10aa68325235069a842f47499852b2dee79e' into clippyup
[rust.git] / src / tools / clippy / clippy_lints / src / loops / explicit_iter_loop.rs
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};
10 use rustc_span::sym;
11
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,
20                 ty::TypeAndMut {
21                     ty: receiver_ty,
22                     mutbl: Mutability::Not,
23                 },
24             );
25             receiver_ty_adjusted == ref_receiver_ty
26         },
27         _ => false,
28     };
29
30     if !should_lint {
31         return;
32     }
33
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 { "" };
37     span_lint_and_sugg(
38         cx,
39         EXPLICIT_ITER_LOOP,
40         arg.span,
41         "it is more concise to loop over references to containers instead of using explicit \
42          iteration methods",
43         "to write this more concisely, try",
44         format!("&{muta}{object}"),
45         applicability,
46     );
47 }
48
49 /// Returns `true` if the type of expr is one that provides `IntoIterator` impls
50 /// for `&T` and `&mut T`, such as `Vec`.
51 #[rustfmt::skip]
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)
65 }
66
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
69     match ty.kind() {
70         ty::Array(_, n) => n
71             .try_eval_usize(cx.tcx, cx.param_env)
72             .map_or(false, |val| (0..=32).contains(&val)),
73         _ => false,
74     }
75 }