about summary refs log tree commit diff
diff options
context:
space:
mode:
authorllogiq <bogusandre@gmail.com>2025-05-16 06:49:42 +0000
committerGitHub <noreply@github.com>2025-05-16 06:49:42 +0000
commitff5e626837e282f78b86745f5fac5edf19785474 (patch)
tree6d41ee1c7bb294b2c0d01bce6cee3ba9cefb0fd2
parent0450db33a5d8587f7c1d4b6d233dac963605766b (diff)
parentf6e95a5b23d99081765ff746e18c34c8aaec7b68 (diff)
downloadrust-ff5e626837e282f78b86745f5fac5edf19785474.tar.gz
rust-ff5e626837e282f78b86745f5fac5edf19785474.zip
Fix false positive of `useless_conversion` when using `.into_iter().any()` (#14800)
Fixes: rust-lang/rust-clippy#14656

changelog: Fix [`useless_conversion`] false positive when using
`.into_iter().any()`.
-rw-r--r--clippy_lints/src/useless_conversion.rs29
-rw-r--r--tests/ui/useless_conversion.fixed15
-rw-r--r--tests/ui/useless_conversion.rs15
-rw-r--r--tests/ui/useless_conversion.stderr14
4 files changed, 71 insertions, 2 deletions
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 3a9c997a579..c04fcf622b9 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -7,7 +7,7 @@ use clippy_utils::{
 };
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
+use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::Obligation;
 use rustc_lint::{LateContext, LateLintPass};
@@ -298,6 +298,33 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                     // implements Copy, in which case .into_iter() returns a copy of the receiver and
                     // cannot be safely omitted.
                     if same_type_and_consts(a, b) && !is_copy(cx, b) {
+                        // Below we check if the parent method call meets the following conditions:
+                        // 1. First parameter is `&mut self` (requires mutable reference)
+                        // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any)
+                        // For methods satisfying these conditions (like any), .into_iter() must be preserved.
+                        if let Some(parent) = get_parent_expr(cx, e)
+                            && let ExprKind::MethodCall(_, recv, _, _) = parent.kind
+                            && recv.hir_id == e.hir_id
+                            && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
+                            && let sig = cx.tcx.fn_sig(def_id).skip_binder().skip_binder()
+                            && let inputs = sig.inputs()
+                            && inputs.len() >= 2
+                            && let Some(self_ty) = inputs.first()
+                            && let ty::Ref(_, _, Mutability::Mut) = self_ty.kind()
+                            && let Some(second_ty) = inputs.get(1)
+                            && let predicates = cx.tcx.param_env(def_id).caller_bounds()
+                            && predicates.iter().any(|pred| {
+                                if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() {
+                                    trait_pred.self_ty() == *second_ty
+                                        && cx.tcx.lang_items().fn_mut_trait() == Some(trait_pred.def_id())
+                                } else {
+                                    false
+                                }
+                            })
+                        {
+                            return;
+                        }
+
                         let sugg = snippet(cx, recv.span, "<expr>").into_owned();
                         span_lint_and_sugg(
                             cx,
diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed
index 489caacf212..ad30c94f347 100644
--- a/tests/ui/useless_conversion.fixed
+++ b/tests/ui/useless_conversion.fixed
@@ -427,3 +427,18 @@ mod issue11819 {
         }
     }
 }
+
+fn issue14739() {
+    use std::ops::Range;
+
+    const R: Range<u32> = 2..7;
+
+    R.into_iter().all(|_x| true); // no lint
+
+    R.into_iter().any(|_x| true); // no lint
+
+    R.for_each(|_x| {});
+    //~^ useless_conversion
+    let _ = R.map(|_x| 0);
+    //~^ useless_conversion
+}
diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs
index 4f3a3b00ea2..505afb34000 100644
--- a/tests/ui/useless_conversion.rs
+++ b/tests/ui/useless_conversion.rs
@@ -427,3 +427,18 @@ mod issue11819 {
         }
     }
 }
+
+fn issue14739() {
+    use std::ops::Range;
+
+    const R: Range<u32> = 2..7;
+
+    R.into_iter().all(|_x| true); // no lint
+
+    R.into_iter().any(|_x| true); // no lint
+
+    R.into_iter().for_each(|_x| {});
+    //~^ useless_conversion
+    let _ = R.into_iter().map(|_x| 0);
+    //~^ useless_conversion
+}
diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr
index 3cde2a786e4..3bfaf1411c2 100644
--- a/tests/ui/useless_conversion.stderr
+++ b/tests/ui/useless_conversion.stderr
@@ -377,5 +377,17 @@ LL -             takes_into_iter(self.my_field.into_iter());
 LL +             takes_into_iter(&mut *self.my_field);
    |
 
-error: aborting due to 41 previous errors
+error: useless conversion to the same type: `std::ops::Range<u32>`
+  --> tests/ui/useless_conversion.rs:440:5
+   |
+LL |     R.into_iter().for_each(|_x| {});
+   |     ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R`
+
+error: useless conversion to the same type: `std::ops::Range<u32>`
+  --> tests/ui/useless_conversion.rs:442:13
+   |
+LL |     let _ = R.into_iter().map(|_x| 0);
+   |             ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R`
+
+error: aborting due to 43 previous errors