about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/methods/double_ended_iterator_last.rs7
-rw-r--r--clippy_lints/src/methods/needless_collect.rs10
-rw-r--r--clippy_utils/src/ty/mod.rs46
-rw-r--r--tests/ui/crashes/ice-11230.fixed4
-rw-r--r--tests/ui/crashes/ice-11230.rs2
-rw-r--r--tests/ui/crashes/ice-11230.stderr11
-rw-r--r--tests/ui/double_ended_iterator_last.fixed32
-rw-r--r--tests/ui/double_ended_iterator_last.rs28
-rw-r--r--tests/ui/double_ended_iterator_last.stderr52
-rw-r--r--tests/ui/double_ended_iterator_last_unfixable.rs5
-rw-r--r--tests/ui/double_ended_iterator_last_unfixable.stderr24
-rw-r--r--tests/ui/needless_collect.fixed84
-rw-r--r--tests/ui/needless_collect.rs84
13 files changed, 292 insertions, 97 deletions
diff --git a/clippy_lints/src/methods/double_ended_iterator_last.rs b/clippy_lints/src/methods/double_ended_iterator_last.rs
index b5adc69e9a7..e666f31217c 100644
--- a/clippy_lints/src/methods/double_ended_iterator_last.rs
+++ b/clippy_lints/src/methods/double_ended_iterator_last.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::ty::implements_trait;
+use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait};
 use clippy_utils::{is_mutable, is_trait_method, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Node, PatKind};
@@ -27,10 +27,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp
         && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name().as_str() == "last")
         // if the resolved method is the same as the provided definition
         && fn_def.def_id() == last_def.def_id
+        && let self_ty = cx.typeck_results().expr_ty(self_expr)
+        && !has_non_owning_mutable_access(cx, self_ty)
     {
         let mut sugg = vec![(call_span, String::from("next_back()"))];
         let mut dont_apply = false;
+
         // if `self_expr` is a reference, it is mutable because it is used for `.last()`
+        // TODO: Change this to lint only when the referred iterator is not used later. If it is used later,
+        // changing to `next_back()` may change its behavior.
         if !(is_mutable(cx, self_expr) || self_type.is_ref()) {
             if let Some(hir_id) = path_to_local(self_expr)
                 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs
index 84d47f29fe8..6efaba525e3 100644
--- a/clippy_lints/src/methods/needless_collect.rs
+++ b/clippy_lints/src/methods/needless_collect.rs
@@ -4,7 +4,9 @@ use super::NEEDLESS_COLLECT;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection};
+use clippy_utils::ty::{
+    get_type_diagnostic_name, has_non_owning_mutable_access, make_normalized_projection, make_projection,
+};
 use clippy_utils::{
     CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local,
     path_to_local_id,
@@ -23,6 +25,7 @@ use rustc_span::{Span, sym};
 
 const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
 
+#[expect(clippy::too_many_lines)]
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     name_span: Span,
@@ -30,6 +33,11 @@ pub(super) fn check<'tcx>(
     iter_expr: &'tcx Expr<'tcx>,
     call_span: Span,
 ) {
+    let iter_ty = cx.typeck_results().expr_ty(iter_expr);
+    if has_non_owning_mutable_access(cx, iter_ty) {
+        return; // don't lint if the iterator has side effects
+    }
+
     match cx.tcx.parent_hir_node(collect_expr.hir_id) {
         Node::Expr(parent) => {
             check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs
index 14ccd35afdd..8db9cd593b3 100644
--- a/clippy_utils/src/ty/mod.rs
+++ b/clippy_utils/src/ty/mod.rs
@@ -1376,3 +1376,49 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
         _ => None,
     }
 }
+
+/// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if
+/// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze`
+/// types, or `PhantomData` types containing any of the previous. This can be used to check whether
+/// skipping iterating over an iterator will change its behavior.
+pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>) -> bool {
+    fn normalize_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+        cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty)
+    }
+
+    /// Check if `ty` contains mutable references or equivalent, which includes:
+    /// - A mutable reference/pointer.
+    /// - A reference/pointer to a non-`Freeze` type.
+    /// - A `PhantomData` type containing any of the previous.
+    fn has_non_owning_mutable_access_inner<'tcx>(
+        cx: &LateContext<'tcx>,
+        phantoms: &mut FxHashSet<Ty<'tcx>>,
+        ty: Ty<'tcx>,
+    ) -> bool {
+        match ty.kind() {
+            ty::Adt(adt_def, args) if adt_def.is_phantom_data() => {
+                phantoms.insert(ty)
+                    && args
+                        .types()
+                        .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty))
+            },
+            ty::Adt(adt_def, args) => adt_def.all_fields().any(|field| {
+                has_non_owning_mutable_access_inner(cx, phantoms, normalize_ty(cx, field.ty(cx.tcx, args)))
+            }),
+            ty::Array(elem_ty, _) | ty::Slice(elem_ty) => has_non_owning_mutable_access_inner(cx, phantoms, *elem_ty),
+            ty::RawPtr(pointee_ty, mutability) | ty::Ref(_, pointee_ty, mutability) => {
+                mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env())
+            },
+            ty::Closure(_, closure_args) => {
+                matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
+            },
+            ty::Tuple(tuple_args) => tuple_args
+                .iter()
+                .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)),
+            _ => false,
+        }
+    }
+
+    let mut phantoms = FxHashSet::default();
+    has_non_owning_mutable_access_inner(cx, &mut phantoms, iter_ty)
+}
diff --git a/tests/ui/crashes/ice-11230.fixed b/tests/ui/crashes/ice-11230.fixed
index 181e1ebbe5a..c49a419f0d4 100644
--- a/tests/ui/crashes/ice-11230.fixed
+++ b/tests/ui/crashes/ice-11230.fixed
@@ -12,7 +12,7 @@ fn main() {
 // needless_collect
 trait Helper<'a>: Iterator<Item = fn()> {}
 
+// Should not be linted because we have no idea whether the iterator has side effects
 fn x(w: &mut dyn for<'a> Helper<'a>) {
-    w.next().is_none();
-    //~^ needless_collect
+    w.collect::<Vec<_>>().is_empty();
 }
diff --git a/tests/ui/crashes/ice-11230.rs b/tests/ui/crashes/ice-11230.rs
index fb05dc781bc..f66b7e961c8 100644
--- a/tests/ui/crashes/ice-11230.rs
+++ b/tests/ui/crashes/ice-11230.rs
@@ -12,7 +12,7 @@ fn main() {
 // needless_collect
 trait Helper<'a>: Iterator<Item = fn()> {}
 
+// Should not be linted because we have no idea whether the iterator has side effects
 fn x(w: &mut dyn for<'a> Helper<'a>) {
     w.collect::<Vec<_>>().is_empty();
-    //~^ needless_collect
 }
diff --git a/tests/ui/crashes/ice-11230.stderr b/tests/ui/crashes/ice-11230.stderr
index b4a3f67081a..91d59121ac4 100644
--- a/tests/ui/crashes/ice-11230.stderr
+++ b/tests/ui/crashes/ice-11230.stderr
@@ -7,14 +7,5 @@ LL |     for v in A.iter() {}
    = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]`
 
-error: avoid using `collect()` when not needed
-  --> tests/ui/crashes/ice-11230.rs:16:7
-   |
-LL |     w.collect::<Vec<_>>().is_empty();
-   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
-   |
-   = note: `-D clippy::needless-collect` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/double_ended_iterator_last.fixed b/tests/ui/double_ended_iterator_last.fixed
index 17d0d71a885..2ce0c04c301 100644
--- a/tests/ui/double_ended_iterator_last.fixed
+++ b/tests/ui/double_ended_iterator_last.fixed
@@ -52,28 +52,35 @@ fn main() {
     let _ = CustomLast.last();
 }
 
+// Should not be linted because applying the lint would move the original iterator. This can only be
+// linted if the iterator is used thereafter.
 fn issue_14139() {
     let mut index = [true, true, false, false, false, true].iter();
-    let mut subindex = index.by_ref().take(3);
-    let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let subindex = index.by_ref().take(3);
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
     let mut subindex = index.by_ref().take(3);
-    let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
     let mut subindex = index.by_ref().take(3);
     let subindex = &mut subindex;
-    let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
     let mut subindex = index.by_ref().take(3);
     let subindex = &mut subindex;
-    let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
-    let (mut subindex, _) = (index.by_ref().take(3), 42);
-    let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let (subindex, _) = (index.by_ref().take(3), 42);
+    let _ = subindex.last();
+    let _ = index.next();
 }
 
 fn drop_order() {
@@ -90,3 +97,14 @@ fn drop_order() {
     //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
     println!("Done");
 }
+
+fn issue_14444() {
+    let mut squares = vec![];
+    let last_square = [1, 2, 3]
+        .into_iter()
+        .map(|x| {
+            squares.push(x * x);
+            Some(x * x)
+        })
+        .last();
+}
diff --git a/tests/ui/double_ended_iterator_last.rs b/tests/ui/double_ended_iterator_last.rs
index 41bc669b171..a4eb9b3337b 100644
--- a/tests/ui/double_ended_iterator_last.rs
+++ b/tests/ui/double_ended_iterator_last.rs
@@ -52,28 +52,35 @@ fn main() {
     let _ = CustomLast.last();
 }
 
+// Should not be linted because applying the lint would move the original iterator. This can only be
+// linted if the iterator is used thereafter.
 fn issue_14139() {
     let mut index = [true, true, false, false, false, true].iter();
     let subindex = index.by_ref().take(3);
-    let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
     let mut subindex = index.by_ref().take(3);
-    let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
     let mut subindex = index.by_ref().take(3);
     let subindex = &mut subindex;
-    let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
     let mut subindex = index.by_ref().take(3);
     let subindex = &mut subindex;
-    let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 
     let mut index = [true, true, false, false, false, true].iter();
     let (subindex, _) = (index.by_ref().take(3), 42);
-    let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.last();
+    let _ = index.next();
 }
 
 fn drop_order() {
@@ -90,3 +97,14 @@ fn drop_order() {
     //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
     println!("Done");
 }
+
+fn issue_14444() {
+    let mut squares = vec![];
+    let last_square = [1, 2, 3]
+        .into_iter()
+        .map(|x| {
+            squares.push(x * x);
+            Some(x * x)
+        })
+        .last();
+}
diff --git a/tests/ui/double_ended_iterator_last.stderr b/tests/ui/double_ended_iterator_last.stderr
index 1702a24d7a0..fe8cf2dcb25 100644
--- a/tests/ui/double_ended_iterator_last.stderr
+++ b/tests/ui/double_ended_iterator_last.stderr
@@ -18,55 +18,7 @@ LL |     let _ = DeIterator.last();
    |                        help: try: `next_back()`
 
 error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last.rs:58:13
-   |
-LL |     let _ = subindex.last();
-   |             ^^^^^^^^^^^^^^^
-   |
-help: try
-   |
-LL ~     let mut subindex = index.by_ref().take(3);
-LL ~     let _ = subindex.next_back();
-   |
-
-error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last.rs:62:13
-   |
-LL |     let _ = subindex.last();
-   |             ^^^^^^^^^------
-   |                      |
-   |                      help: try: `next_back()`
-
-error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last.rs:67:13
-   |
-LL |     let _ = subindex.last();
-   |             ^^^^^^^^^------
-   |                      |
-   |                      help: try: `next_back()`
-
-error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last.rs:72:13
-   |
-LL |     let _ = subindex.last();
-   |             ^^^^^^^^^------
-   |                      |
-   |                      help: try: `next_back()`
-
-error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last.rs:76:13
-   |
-LL |     let _ = subindex.last();
-   |             ^^^^^^^^^^^^^^^
-   |
-help: try
-   |
-LL ~     let (mut subindex, _) = (index.by_ref().take(3), 42);
-LL ~     let _ = subindex.next_back();
-   |
-
-error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last.rs:89:36
+  --> tests/ui/double_ended_iterator_last.rs:96:36
    |
 LL |     println!("Last element is {}", v.last().unwrap().0);
    |                                    ^^^^^^^^
@@ -78,5 +30,5 @@ LL ~     let mut v = v.into_iter();
 LL ~     println!("Last element is {}", v.next_back().unwrap().0);
    |
 
-error: aborting due to 8 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/double_ended_iterator_last_unfixable.rs b/tests/ui/double_ended_iterator_last_unfixable.rs
index 3f125c7f20c..7c5de8832d6 100644
--- a/tests/ui/double_ended_iterator_last_unfixable.rs
+++ b/tests/ui/double_ended_iterator_last_unfixable.rs
@@ -1,10 +1,13 @@
 //@no-rustfix
 #![warn(clippy::double_ended_iterator_last)]
 
+// Should not be linted because applying the lint would move the original iterator. This can only be
+// linted if the iterator is used thereafter.
 fn main() {
     let mut index = [true, true, false, false, false, true].iter();
     let subindex = (index.by_ref().take(3), 42);
-    let _ = subindex.0.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+    let _ = subindex.0.last();
+    let _ = index.next();
 }
 
 fn drop_order() {
diff --git a/tests/ui/double_ended_iterator_last_unfixable.stderr b/tests/ui/double_ended_iterator_last_unfixable.stderr
index f4be757d00d..845afc11f04 100644
--- a/tests/ui/double_ended_iterator_last_unfixable.stderr
+++ b/tests/ui/double_ended_iterator_last_unfixable.stderr
@@ -1,21 +1,5 @@
 error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13
-   |
-LL |     let _ = subindex.0.last();
-   |             ^^^^^^^^^^^------
-   |                        |
-   |                        help: try: `next_back()`
-   |
-note: this must be made mutable to use `.next_back()`
-  --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13
-   |
-LL |     let _ = subindex.0.last();
-   |             ^^^^^^^^^^
-   = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]`
-
-error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36
+  --> tests/ui/double_ended_iterator_last_unfixable.rs:23:36
    |
 LL |     println!("Last element is {}", v.0.last().unwrap().0);
    |                                    ^^^^------
@@ -24,10 +8,12 @@ LL |     println!("Last element is {}", v.0.last().unwrap().0);
    |
    = note: this change will alter drop order which may be undesirable
 note: this must be made mutable to use `.next_back()`
-  --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36
+  --> tests/ui/double_ended_iterator_last_unfixable.rs:23:36
    |
 LL |     println!("Last element is {}", v.0.last().unwrap().0);
    |                                    ^^^
+   = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]`
 
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed
index 6551fa56b42..b09efe9888f 100644
--- a/tests/ui/needless_collect.fixed
+++ b/tests/ui/needless_collect.fixed
@@ -126,3 +126,87 @@ fn main() {
 fn foo(_: impl IntoIterator<Item = usize>) {}
 fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {}
 fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {}
+
+mod issue9191 {
+    use std::cell::Cell;
+    use std::collections::HashSet;
+    use std::hash::Hash;
+    use std::marker::PhantomData;
+    use std::ops::Deref;
+
+    fn captures_ref_mut(xs: Vec<i32>, mut ys: HashSet<i32>) {
+        if xs.iter().map(|x| ys.remove(x)).collect::<Vec<_>>().contains(&true) {
+            todo!()
+        }
+    }
+
+    #[derive(Debug, Clone)]
+    struct MyRef<'a>(PhantomData<&'a mut Cell<HashSet<i32>>>, *mut Cell<HashSet<i32>>);
+
+    impl MyRef<'_> {
+        fn new(target: &mut Cell<HashSet<i32>>) -> Self {
+            MyRef(PhantomData, target)
+        }
+
+        fn get(&mut self) -> &mut Cell<HashSet<i32>> {
+            unsafe { &mut *self.1 }
+        }
+    }
+
+    fn captures_phantom(xs: Vec<i32>, mut ys: Cell<HashSet<i32>>) {
+        let mut ys_ref = MyRef::new(&mut ys);
+        if xs
+            .iter()
+            .map({
+                let mut ys_ref = ys_ref.clone();
+                move |x| ys_ref.get().get_mut().remove(x)
+            })
+            .collect::<Vec<_>>()
+            .contains(&true)
+        {
+            todo!()
+        }
+    }
+}
+
+pub fn issue8055(v: impl IntoIterator<Item = i32>) -> Result<impl Iterator<Item = i32>, usize> {
+    let mut zeros = 0;
+
+    let res: Vec<_> = v
+        .into_iter()
+        .filter(|i| {
+            if *i == 0 {
+                zeros += 1
+            };
+            *i != 0
+        })
+        .collect();
+
+    if zeros != 0 {
+        return Err(zeros);
+    }
+    Ok(res.into_iter())
+}
+
+mod issue8055_regression {
+    struct Foo<T> {
+        inner: T,
+        marker: core::marker::PhantomData<Self>,
+    }
+
+    impl<T: Iterator> Iterator for Foo<T> {
+        type Item = T::Item;
+        fn next(&mut self) -> Option<Self::Item> {
+            self.inner.next()
+        }
+    }
+
+    fn foo() {
+        Foo {
+            inner: [].iter(),
+            marker: core::marker::PhantomData,
+        }
+        .collect::<Vec<&i32>>()
+        .len();
+    }
+}
diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs
index 973c41c6875..da4182966bb 100644
--- a/tests/ui/needless_collect.rs
+++ b/tests/ui/needless_collect.rs
@@ -126,3 +126,87 @@ fn main() {
 fn foo(_: impl IntoIterator<Item = usize>) {}
 fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {}
 fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {}
+
+mod issue9191 {
+    use std::cell::Cell;
+    use std::collections::HashSet;
+    use std::hash::Hash;
+    use std::marker::PhantomData;
+    use std::ops::Deref;
+
+    fn captures_ref_mut(xs: Vec<i32>, mut ys: HashSet<i32>) {
+        if xs.iter().map(|x| ys.remove(x)).collect::<Vec<_>>().contains(&true) {
+            todo!()
+        }
+    }
+
+    #[derive(Debug, Clone)]
+    struct MyRef<'a>(PhantomData<&'a mut Cell<HashSet<i32>>>, *mut Cell<HashSet<i32>>);
+
+    impl MyRef<'_> {
+        fn new(target: &mut Cell<HashSet<i32>>) -> Self {
+            MyRef(PhantomData, target)
+        }
+
+        fn get(&mut self) -> &mut Cell<HashSet<i32>> {
+            unsafe { &mut *self.1 }
+        }
+    }
+
+    fn captures_phantom(xs: Vec<i32>, mut ys: Cell<HashSet<i32>>) {
+        let mut ys_ref = MyRef::new(&mut ys);
+        if xs
+            .iter()
+            .map({
+                let mut ys_ref = ys_ref.clone();
+                move |x| ys_ref.get().get_mut().remove(x)
+            })
+            .collect::<Vec<_>>()
+            .contains(&true)
+        {
+            todo!()
+        }
+    }
+}
+
+pub fn issue8055(v: impl IntoIterator<Item = i32>) -> Result<impl Iterator<Item = i32>, usize> {
+    let mut zeros = 0;
+
+    let res: Vec<_> = v
+        .into_iter()
+        .filter(|i| {
+            if *i == 0 {
+                zeros += 1
+            };
+            *i != 0
+        })
+        .collect();
+
+    if zeros != 0 {
+        return Err(zeros);
+    }
+    Ok(res.into_iter())
+}
+
+mod issue8055_regression {
+    struct Foo<T> {
+        inner: T,
+        marker: core::marker::PhantomData<Self>,
+    }
+
+    impl<T: Iterator> Iterator for Foo<T> {
+        type Item = T::Item;
+        fn next(&mut self) -> Option<Self::Item> {
+            self.inner.next()
+        }
+    }
+
+    fn foo() {
+        Foo {
+            inner: [].iter(),
+            marker: core::marker::PhantomData,
+        }
+        .collect::<Vec<&i32>>()
+        .len();
+    }
+}