]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/loops/explicit_iter_loop.rs
9683e59a3962d2acac1613a92f1cd864ee155af3
[rust.git] / clippy_lints / src / loops / explicit_iter_loop.rs
1 use super::EXPLICIT_ITER_LOOP;
2 use crate::utils::{match_trait_method, snippet_with_applicability, span_lint_and_sugg};
3 use rustc_errors::Applicability;
4 use rustc_hir::{Expr, Mutability};
5 use rustc_lint::LateContext;
6 use rustc_middle::ty::{self, Ty, TyS};
7 use rustc_span::sym;
8
9 use crate::utils::{is_type_diagnostic_item, match_type, paths};
10
11 pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
12     let should_lint = match method_name {
13         "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
14         "into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => {
15             let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
16             let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
17             let ref_receiver_ty = cx.tcx.mk_ref(
18                 cx.tcx.lifetimes.re_erased,
19                 ty::TypeAndMut {
20                     ty: receiver_ty,
21                     mutbl: Mutability::Not,
22                 },
23             );
24             TyS::same_type(receiver_ty_adjusted, ref_receiver_ty)
25         },
26         _ => false,
27     };
28
29     if !should_lint {
30         return;
31     }
32
33     let mut applicability = Applicability::MachineApplicable;
34     let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
35     let muta = if method_name == "iter_mut" { "mut " } else { "" };
36     span_lint_and_sugg(
37         cx,
38         EXPLICIT_ITER_LOOP,
39         arg.span,
40         "it is more concise to loop over references to containers instead of using explicit \
41          iteration methods",
42         "to write this more concisely, try",
43         format!("&{}{}", muta, object),
44         applicability,
45     )
46 }
47
48 /// Returns `true` if the type of expr is one that provides `IntoIterator` impls
49 /// for `&T` and `&mut T`, such as `Vec`.
50 #[rustfmt::skip]
51 fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
52     // no walk_ptrs_ty: calling iter() on a reference can make sense because it
53     // will allow further borrows afterwards
54     let ty = cx.typeck_results().expr_ty(e);
55     is_iterable_array(ty, cx) ||
56     is_type_diagnostic_item(cx, ty, sym::vec_type) ||
57     match_type(cx, ty, &paths::LINKED_LIST) ||
58     is_type_diagnostic_item(cx, ty, sym::hashmap_type) ||
59     is_type_diagnostic_item(cx, ty, sym::hashset_type) ||
60     is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
61     match_type(cx, ty, &paths::BINARY_HEAP) ||
62     match_type(cx, ty, &paths::BTREEMAP) ||
63     match_type(cx, ty, &paths::BTREESET)
64 }
65
66 fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
67     // IntoIterator is currently only implemented for array sizes <= 32 in rustc
68     match ty.kind() {
69         ty::Array(_, n) => n
70             .try_eval_usize(cx.tcx, cx.param_env)
71             .map_or(false, |val| (0..=32).contains(&val)),
72         _ => false,
73     }
74 }