about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2023-02-26 23:16:27 -0500
committerJason Newcomb <jsnewcomb@pm.me>2023-06-09 19:24:55 -0400
commitb6fa4d43d3d800224f724baa322f301bf09a5031 (patch)
tree2603fa6fc7a9f14b12aead15afb3af7c2a6ae4ef
parent476efe92e773dcc7c7363a0421810f5d5ca3a751 (diff)
downloadrust-b6fa4d43d3d800224f724baa322f301bf09a5031.tar.gz
rust-b6fa4d43d3d800224f724baa322f301bf09a5031.zip
Extend `explicit_iter_loop` to all types
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/loops/explicit_iter_loop.rs200
-rw-r--r--clippy_lints/src/loops/mod.rs90
-rw-r--r--clippy_utils/src/msrvs.rs2
-rw-r--r--clippy_utils/src/ty.rs48
-rw-r--r--tests/ui/for_loop_fixable.fixed91
-rw-r--r--tests/ui/for_loop_fixable.rs85
-rw-r--r--tests/ui/for_loop_fixable.stderr28
8 files changed, 460 insertions, 86 deletions
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 22ff5ef5859..30842cd6510 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -679,7 +679,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     });
     store.register_late_pass(|_| Box::<shadow::Shadow>::default());
     store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
-    store.register_late_pass(|_| Box::new(loops::Loops));
+    store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv())));
     store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
     store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
     store.register_late_pass(|_| Box::new(entry::HashMapPass));
diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs
index 151c7f1d5d2..ba66af62375 100644
--- a/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -1,18 +1,37 @@
 use super::EXPLICIT_ITER_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{implements_trait_with_env, make_normalized_projection_with_regions, normalize_with_regions,
+    make_normalized_projection, implements_trait, is_copy};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
+use rustc_middle::ty::{self, EarlyBinder, TypeAndMut, 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) => {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, method_name: &str, msrv: &Msrv) {
+    let borrow_kind = match method_name {
+        "iter" | "iter_mut" => match is_ref_iterable(cx, self_arg, call_expr) {
+            Some((kind, ty)) => {
+                if let ty::Array(_, count) = *ty.peel_refs().kind() {
+                    if !ty.is_ref() {
+                        if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
+                            return;
+                        }
+                    } else if count.try_eval_target_usize(cx.tcx, cx.param_env).map_or(true, |x| x > 32)
+                        && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN)
+                    {
+                        return
+                    }
+                }
+                kind
+            },
+            None => return,
+        },
+        "into_iter" if is_trait_method(cx, call_expr, 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(
@@ -22,54 +41,159 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, m
                     mutbl: Mutability::Not,
                 },
             );
-            receiver_ty_adjusted == ref_receiver_ty
+            if receiver_ty_adjusted == ref_receiver_ty {
+                AdjustKind::None
+            } else {
+                return;
+            }
         },
-        _ => false,
+        _ => return,
     };
 
-    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 { "" };
+    let prefix = match borrow_kind {
+        AdjustKind::None => "",
+        AdjustKind::Borrow => "&",
+        AdjustKind::BorrowMut => "&mut ",
+        AdjustKind::Deref => "*",
+        AdjustKind::Reborrow => "&*",
+        AdjustKind::ReborrowMut => "&mut *",
+    };
     span_lint_and_sugg(
         cx,
         EXPLICIT_ITER_LOOP,
-        arg.span,
+        call_expr.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}"),
+        format!("{prefix}{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)
+enum AdjustKind {
+    None,
+    Borrow,
+    BorrowMut,
+    Deref,
+    Reborrow,
+    ReborrowMut,
+}
+impl AdjustKind {
+    fn borrow(mutbl: Mutability) -> Self {
+        match mutbl {
+            Mutability::Not => Self::Borrow,
+            Mutability::Mut => Self::BorrowMut,
+        }
+    }
+
+    fn auto_borrow(mutbl: AutoBorrowMutability) -> Self {
+        match mutbl {
+            AutoBorrowMutability::Not => Self::Borrow,
+            AutoBorrowMutability::Mut { .. } => Self::BorrowMut,
+        }
+    }
+
+    fn reborrow(mutbl: AutoBorrowMutability) -> Self {
+        match mutbl {
+            AutoBorrowMutability::Not => Self::Reborrow,
+            AutoBorrowMutability::Mut { .. } => Self::ReborrowMut,
+        }
+    }
 }
 
-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,
+/// Checks if an `iter` or `iter_mut` call returns `IntoIterator::IntoIter`. Returns how the
+/// argument needs to be adjusted.
+fn is_ref_iterable<'tcx>(cx: &LateContext<'tcx>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) -> Option<(AdjustKind, Ty<'tcx>)> {
+    let typeck = cx.typeck_results();
+    if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
+        && let Some(fn_id) = typeck.type_dependent_def_id(call_expr.hir_id)
+        && let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
+        && let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
+        && let param_env = cx.tcx.param_env(fn_id)
+        && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, [])
+        && let Some(into_iter_ty) =
+            make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
+        && let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)
+        && into_iter_ty == req_res_ty
+    {
+        let adjustments = typeck.expr_adjustments(self_arg);
+        let self_ty = typeck.expr_ty(self_arg);
+        let self_is_copy = is_copy(cx, self_ty);
+
+        if adjustments.is_empty() && self_is_copy {
+            return Some((AdjustKind::None, self_ty));
+        }
+
+        let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty).subst(cx.tcx, typeck.node_substs(call_expr.hir_id)));
+        if !adjustments.is_empty() && self_is_copy {
+            if implements_trait(cx, self_ty, trait_id, &[])
+                && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
+                && ty == res_ty
+            {
+                return Some((AdjustKind::None, self_ty));
+            }
+        }
+
+        let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
+            Some(mutbl)
+        } else {
+            None
+        };
+        if let Some(mutbl) = mutbl
+            && !self_ty.is_ref()
+        {
+            let self_ty = cx.tcx.mk_ref(cx.tcx.lifetimes.re_erased, TypeAndMut {
+                ty: self_ty,
+                mutbl,
+            });
+            if implements_trait(cx, self_ty, trait_id, &[])
+                && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
+                && ty == res_ty
+            {
+                return Some((AdjustKind::borrow(mutbl), self_ty));
+            }
+        }
+
+        match adjustments {
+            [] => Some((AdjustKind::None, self_ty)),
+            &[Adjustment { kind: Adjust::Deref(_), ..}, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)), target }, ..] => {
+                if target != self_ty
+                    && implements_trait(cx, target, trait_id, &[])
+                    && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+                    && ty == res_ty
+                {
+                    Some((AdjustKind::reborrow(mutbl), target))
+                } else {
+                    None
+                }
+            }
+            &[Adjustment { kind: Adjust::Deref(_), target }, ..] => {
+                if is_copy(cx, target)
+                    && implements_trait(cx, target, trait_id, &[])
+                    && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+                    && ty == res_ty
+                {
+                    Some((AdjustKind::Deref, target))
+                } else {
+                    None
+                }
+            }
+            &[Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)), target }, ..] => {
+                if self_ty.is_ref()
+                    && implements_trait(cx, target, trait_id, &[])
+                    && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+                    && ty == res_ty
+                {
+                    Some((AdjustKind::auto_borrow(mutbl), target))
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        }
+    } else {
+        None
     }
 }
diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs
index c7c11809752..068166bdeb2 100644
--- a/clippy_lints/src/loops/mod.rs
+++ b/clippy_lints/src/loops/mod.rs
@@ -20,9 +20,10 @@ mod while_let_loop;
 mod while_let_on_iterator;
 
 use clippy_utils::higher;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
 
@@ -606,7 +607,15 @@ declare_clippy_lint! {
     "checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
 }
 
-declare_lint_pass!(Loops => [
+pub struct Loops {
+    msrv: Msrv,
+}
+impl Loops {
+    pub fn new(msrv: Msrv) -> Self {
+        Self { msrv }
+    }
+}
+impl_lint_pass!(Loops => [
     MANUAL_MEMCPY,
     MANUAL_FLATTEN,
     NEEDLESS_RANGE_LOOP,
@@ -645,7 +654,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
             if body.span.from_expansion() {
                 return;
             }
-            check_for_loop(cx, pat, arg, body, expr, span);
+            self.check_for_loop(cx, pat, arg, body, expr, span);
             if let ExprKind::Block(block, _) = body.kind {
                 never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
             }
@@ -680,44 +689,47 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
     }
 }
 
-fn check_for_loop<'tcx>(
-    cx: &LateContext<'tcx>,
-    pat: &'tcx Pat<'_>,
-    arg: &'tcx Expr<'_>,
-    body: &'tcx Expr<'_>,
-    expr: &'tcx Expr<'_>,
-    span: Span,
-) {
-    let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
-    if !is_manual_memcpy_triggered {
-        needless_range_loop::check(cx, pat, arg, body, expr);
-        explicit_counter_loop::check(cx, pat, arg, body, expr);
+impl Loops {
+    fn check_for_loop<'tcx>(
+        &self,
+        cx: &LateContext<'tcx>,
+        pat: &'tcx Pat<'_>,
+        arg: &'tcx Expr<'_>,
+        body: &'tcx Expr<'_>,
+        expr: &'tcx Expr<'_>,
+        span: Span,
+    ) {
+        let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
+        if !is_manual_memcpy_triggered {
+            needless_range_loop::check(cx, pat, arg, body, expr);
+            explicit_counter_loop::check(cx, pat, arg, body, expr);
+        }
+        self.check_for_loop_arg(cx, pat, arg);
+        for_kv_map::check(cx, pat, arg, body);
+        mut_range_bound::check(cx, arg, body);
+        single_element_loop::check(cx, pat, arg, body, expr);
+        same_item_push::check(cx, pat, arg, body, expr);
+        manual_flatten::check(cx, pat, arg, body, span);
+        manual_find::check(cx, pat, arg, body, span, expr);
     }
-    check_for_loop_arg(cx, pat, arg);
-    for_kv_map::check(cx, pat, arg, body);
-    mut_range_bound::check(cx, arg, body);
-    single_element_loop::check(cx, pat, arg, body, expr);
-    same_item_push::check(cx, pat, arg, body, expr);
-    manual_flatten::check(cx, pat, arg, body, span);
-    manual_find::check(cx, pat, arg, body, span, expr);
-}
 
-fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
-    if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
-        let method_name = method.ident.as_str();
-        // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
-        match method_name {
-            "iter" | "iter_mut" => {
-                explicit_iter_loop::check(cx, self_arg, arg, method_name);
-            },
-            "into_iter" => {
-                explicit_iter_loop::check(cx, self_arg, arg, method_name);
-                explicit_into_iter_loop::check(cx, self_arg, arg);
-            },
-            "next" => {
-                iter_next_loop::check(cx, arg);
-            },
-            _ => {},
+    fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
+        if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
+            let method_name = method.ident.as_str();
+            // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
+            match method_name {
+                "iter" | "iter_mut" => {
+                    explicit_iter_loop::check(cx, self_arg, arg, method_name, &self.msrv);
+                },
+                "into_iter" => {
+                    explicit_iter_loop::check(cx, self_arg, arg, method_name, &self.msrv);
+                    explicit_into_iter_loop::check(cx, self_arg, arg);
+                },
+                "next" => {
+                    iter_next_loop::check(cx, arg);
+                },
+                _ => {},
+            }
         }
     }
 }
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index 6f102308f0b..e1b1a6f7184 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -28,7 +28,7 @@ msrv_aliases! {
     1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
     1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
     1,50,0 { BOOL_THEN, CLAMP }
-    1,47,0 { TAU, IS_ASCII_DIGIT_CONST }
+    1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
     1,46,0 { CONST_IF_MATCH }
     1,45,0 { STR_STRIP_PREFIX }
     1,43,0 { LOG2_10, LOG10_2 }
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index 7b4ed77e8ed..78b14dc7abb 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -1180,3 +1180,51 @@ pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         _ => false,
     }
 }
+
+pub fn make_normalized_projection_with_regions<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    container_id: DefId,
+    assoc_ty: Symbol,
+    substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
+) -> Option<Ty<'tcx>> {
+    fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
+        #[cfg(debug_assertions)]
+        if let Some((i, subst)) = ty
+            .substs
+            .iter()
+            .enumerate()
+            .find(|(_, subst)| subst.has_late_bound_regions())
+        {
+            debug_assert!(
+                false,
+                "substs contain late-bound region at index `{i}` which can't be normalized.\n\
+                    use `TyCtxt::erase_late_bound_regions`\n\
+                    note: subst is `{subst:#?}`",
+            );
+            return None;
+        }
+        let cause = rustc_middle::traits::ObligationCause::dummy();
+        match tcx
+            .infer_ctxt()
+            .build()
+            .at(&cause, param_env)
+            .query_normalize(tcx.mk_projection(ty.def_id, ty.substs))
+        {
+            Ok(ty) => Some(ty.value),
+            Err(e) => {
+                debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
+                None
+            },
+        }
+    }
+    helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
+}
+
+pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+    let cause = rustc_middle::traits::ObligationCause::dummy();
+    match tcx.infer_ctxt().build().at(&cause, param_env).query_normalize(ty) {
+        Ok(ty) => ty.value,
+        Err(_) => ty,
+    }
+}
diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed
index 3e74e2a02d0..99230856446 100644
--- a/tests/ui/for_loop_fixable.fixed
+++ b/tests/ui/for_loop_fixable.fixed
@@ -1,6 +1,6 @@
 //@run-rustfix
 #![allow(dead_code, unused)]
-#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
+#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::deref_addrof)]
 
 use std::collections::*;
 
@@ -52,11 +52,11 @@ fn main() {
 
     for _v in &[1, 2, 3] {}
 
-    for _v in (&mut [1, 2, 3]).iter() {} // no error
+    for _v in &*(&mut [1, 2, 3]) {} // no error
 
     for _v in &[0; 32] {}
 
-    for _v in [0; 33].iter() {} // no error
+    for _v in &[0; 33] {} // no error
 
     let ll: LinkedList<()> = LinkedList::new();
     for _v in &ll {}
@@ -272,7 +272,7 @@ mod issue_4958 {
         let rr = &&t;
 
         // This case is handled by `explicit_iter_loop`. No idea why.
-        for _ in &t {}
+        for _ in t {}
 
         for _ in r {}
 
@@ -307,3 +307,86 @@ mod issue_6900 {
         }
     }
 }
+
+struct IntoIterDiffTy;
+impl IntoIterator for &'_ IntoIterDiffTy {
+    type Item = &'static ();
+    type IntoIter = core::slice::Iter<'static, ()>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl IntoIterDiffTy {
+    fn iter(&self) -> core::slice::Iter<'static, i32> {
+        unimplemented!()
+    }
+}
+
+struct IntoIterDiffSig;
+impl IntoIterator for &'_ IntoIterDiffSig {
+    type Item = &'static ();
+    type IntoIter = core::slice::Iter<'static, ()>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl IntoIterDiffSig {
+    fn iter(&self, _: u32) -> core::slice::Iter<'static, ()> {
+        unimplemented!()
+    }
+}
+
+struct IntoIterDiffLt<'a>(&'a ());
+impl<'a> IntoIterator for &'a IntoIterDiffLt<'_> {
+    type Item = &'a ();
+    type IntoIter = core::slice::Iter<'a, ()>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl<'a> IntoIterDiffLt<'a> {
+    fn iter(&self) -> core::slice::Iter<'a, ()> {
+        unimplemented!()
+    }
+}
+
+struct CustomType;
+impl<'a> IntoIterator for &'a CustomType {
+    type Item = &'a u32;
+    type IntoIter = core::slice::Iter<'a, u32>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl<'a> IntoIterator for &'a mut CustomType {
+    type Item = &'a mut u32;
+    type IntoIter = core::slice::IterMut<'a, u32>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl CustomType {
+    fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
+        panic!()
+    }
+
+    fn iter_mut(&mut self) -> core::slice::IterMut<'_, u32> {
+        panic!()
+    }
+}
+
+#[warn(clippy::explicit_iter_loop)]
+fn _f() {
+    let x = IntoIterDiffTy;
+    for _ in x.iter() {}
+
+    let x = IntoIterDiffSig;
+    for _ in x.iter(0) {}
+
+    let x = IntoIterDiffLt(&());
+    for _ in x.iter() {}
+
+    let mut x = CustomType;
+    for _ in &x {}
+    for _ in &mut x {}
+}
diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs
index 45c6760f89e..0d7fe3c0314 100644
--- a/tests/ui/for_loop_fixable.rs
+++ b/tests/ui/for_loop_fixable.rs
@@ -1,6 +1,6 @@
 //@run-rustfix
 #![allow(dead_code, unused)]
-#![allow(clippy::uninlined_format_args, clippy::useless_vec)]
+#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::deref_addrof)]
 
 use std::collections::*;
 
@@ -307,3 +307,86 @@ mod issue_6900 {
         }
     }
 }
+
+struct IntoIterDiffTy;
+impl IntoIterator for &'_ IntoIterDiffTy {
+    type Item = &'static ();
+    type IntoIter = core::slice::Iter<'static, ()>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl IntoIterDiffTy {
+    fn iter(&self) -> core::slice::Iter<'static, i32> {
+        unimplemented!()
+    }
+}
+
+struct IntoIterDiffSig;
+impl IntoIterator for &'_ IntoIterDiffSig {
+    type Item = &'static ();
+    type IntoIter = core::slice::Iter<'static, ()>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl IntoIterDiffSig {
+    fn iter(&self, _: u32) -> core::slice::Iter<'static, ()> {
+        unimplemented!()
+    }
+}
+
+struct IntoIterDiffLt<'a>(&'a ());
+impl<'a> IntoIterator for &'a IntoIterDiffLt<'_> {
+    type Item = &'a ();
+    type IntoIter = core::slice::Iter<'a, ()>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl<'a> IntoIterDiffLt<'a> {
+    fn iter(&self) -> core::slice::Iter<'a, ()> {
+        unimplemented!()
+    }
+}
+
+struct CustomType;
+impl<'a> IntoIterator for &'a CustomType {
+    type Item = &'a u32;
+    type IntoIter = core::slice::Iter<'a, u32>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl<'a> IntoIterator for &'a mut CustomType {
+    type Item = &'a mut u32;
+    type IntoIter = core::slice::IterMut<'a, u32>;
+    fn into_iter(self) -> Self::IntoIter {
+        unimplemented!()
+    }
+}
+impl CustomType {
+    fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
+        panic!()
+    }
+
+    fn iter_mut(&mut self) -> core::slice::IterMut<'_, u32> {
+        panic!()
+    }
+}
+
+#[warn(clippy::explicit_iter_loop)]
+fn _f() {
+    let x = IntoIterDiffTy;
+    for _ in x.iter() {}
+
+    let x = IntoIterDiffSig;
+    for _ in x.iter(0) {}
+
+    let x = IntoIterDiffLt(&());
+    for _ in x.iter() {}
+
+    let mut x = CustomType;
+    for _ in x.iter() {}
+    for _ in x.iter_mut() {}
+}
diff --git a/tests/ui/for_loop_fixable.stderr b/tests/ui/for_loop_fixable.stderr
index ddfe66d675f..4ad23e0b9db 100644
--- a/tests/ui/for_loop_fixable.stderr
+++ b/tests/ui/for_loop_fixable.stderr
@@ -27,12 +27,24 @@ LL |     for _v in [1, 2, 3].iter() {}
    |               ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
+  --> $DIR/for_loop_fixable.rs:55:15
+   |
+LL |     for _v in (&mut [1, 2, 3]).iter() {} // no error
+   |               ^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*(&mut [1, 2, 3])`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
   --> $DIR/for_loop_fixable.rs:57:15
    |
 LL |     for _v in [0; 32].iter() {}
    |               ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
+  --> $DIR/for_loop_fixable.rs:59:15
+   |
+LL |     for _v in [0; 33].iter() {} // no error
+   |               ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 33]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
   --> $DIR/for_loop_fixable.rs:62:15
    |
 LL |     for _v in ll.iter() {}
@@ -84,7 +96,7 @@ error: it is more concise to loop over references to containers instead of using
   --> $DIR/for_loop_fixable.rs:275:18
    |
 LL |         for _ in t.into_iter() {}
-   |                  ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
+   |                  ^^^^^^^^^^^^^ help: to write this more concisely, try: `t`
 
 error: it is more concise to loop over containers instead of using explicit iteration methods
   --> $DIR/for_loop_fixable.rs:277:18
@@ -92,5 +104,17 @@ error: it is more concise to loop over containers instead of using explicit iter
 LL |         for _ in r.into_iter() {}
    |                  ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
 
-error: aborting due to 15 previous errors
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+  --> $DIR/for_loop_fixable.rs:390:14
+   |
+LL |     for _ in x.iter() {}
+   |              ^^^^^^^^ help: to write this more concisely, try: `&x`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+  --> $DIR/for_loop_fixable.rs:391:14
+   |
+LL |     for _ in x.iter_mut() {}
+   |              ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut x`
+
+error: aborting due to 19 previous errors