|
| 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::symbol::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 | +} |
0 commit comments