about summary refs log tree commit diff
diff options
context:
space:
mode:
authory21 <30553356+y21@users.noreply.github.com>2023-09-05 19:08:34 +0200
committery21 <30553356+y21@users.noreply.github.com>2023-09-05 19:08:34 +0200
commit18f36897ef0844dc5e5a0d995356247cbee72ff3 (patch)
tree869115a75640e4e41efe4c797f64b6c325cb8a8d
parentcf10690ad4e1c4df0794fcf072f65515cf8bbadd (diff)
downloadrust-18f36897ef0844dc5e5a0d995356247cbee72ff3.tar.gz
rust-18f36897ef0844dc5e5a0d995356247cbee72ff3.zip
use the correct node args for substitution
-rw-r--r--clippy_lints/src/useless_conversion.rs52
-rw-r--r--tests/ui/useless_conversion.fixed41
-rw-r--r--tests/ui/useless_conversion.rs41
-rw-r--r--tests/ui/useless_conversion.stderr26
4 files changed, 137 insertions, 23 deletions
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 5afb1f76df8..f32e7edad6c 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -12,7 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::Obligation;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty};
+use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, Span};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -93,27 +93,35 @@ fn into_iter_bound<'tcx>(
 
     for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates {
         if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() {
-            if tr.def_id() == into_iter_did && tr.self_ty().is_param(param_index) {
-                into_iter_span = Some(*span);
-            } else {
-                // Substitute generics in the predicate and replace the IntoIterator type parameter with the
-                // `.into_iter()` receiver to see if the bound also holds for that type.
-                let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| {
-                    if i == param_index as usize {
-                        GenericArg::from(into_iter_receiver)
-                    } else {
-                        arg
+            if tr.self_ty().is_param(param_index) {
+                if tr.def_id() == into_iter_did {
+                    into_iter_span = Some(*span);
+                } else {
+                    let tr = cx.tcx.erase_regions(tr);
+                    if tr.has_escaping_bound_vars() {
+                        return None;
+                    }
+
+                    // Substitute generics in the predicate and replace the IntoIterator type parameter with the
+                    // `.into_iter()` receiver to see if the bound also holds for that type.
+                    let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| {
+                        if i == param_index as usize {
+                            GenericArg::from(into_iter_receiver)
+                        } else {
+                            arg
+                        }
+                    }));
+
+                    let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args);
+                    let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate);
+                    if !cx
+                        .tcx
+                        .infer_ctxt()
+                        .build()
+                        .predicate_must_hold_modulo_regions(&obligation)
+                    {
+                        return None;
                     }
-                }));
-                let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args);
-                let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate);
-                if !cx
-                    .tcx
-                    .infer_ctxt()
-                    .build()
-                    .predicate_must_hold_modulo_regions(&obligation)
-                {
-                    return None;
                 }
             }
         }
@@ -216,7 +224,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                                         return (
                                             did,
                                             args,
-                                            cx.typeck_results().node_args(recv.hir_id),
+                                            cx.typeck_results().node_args(parent.hir_id),
                                             MethodOrFunction::Method
                                         );
                                     })
diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed
index f3504bc62b4..ed8387b7eb2 100644
--- a/tests/ui/useless_conversion.fixed
+++ b/tests/ui/useless_conversion.fixed
@@ -244,6 +244,47 @@ mod issue11300 {
         // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary.
         foo3([1, 2, 3]);
     }
+
+    fn ice() {
+        struct S1;
+        impl S1 {
+            pub fn foo<I: IntoIterator>(&self, _: I) {}
+        }
+
+        S1.foo([1, 2]);
+
+        // ICE that occured in itertools
+        trait Itertools {
+            fn interleave_shortest<J>(self, other: J)
+            where
+                J: IntoIterator,
+                Self: Sized;
+        }
+        impl<I: Iterator> Itertools for I {
+            fn interleave_shortest<J>(self, other: J)
+            where
+                J: IntoIterator,
+                Self: Sized,
+            {
+            }
+        }
+        let v0: Vec<i32> = vec![0, 2, 4];
+        let v1: Vec<i32> = vec![1, 3, 5, 7];
+        v0.into_iter().interleave_shortest(v1);
+
+        trait TraitWithLifetime<'a> {}
+        impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {}
+
+        struct Helper;
+        impl<'a> Helper {
+            fn with_lt<I>(&self, _: I)
+            where
+                I: IntoIterator + TraitWithLifetime<'a>,
+            {
+            }
+        }
+        Helper.with_lt([&1, &2].into_iter());
+    }
 }
 
 #[derive(Copy, Clone)]
diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs
index a4f4722927f..991a7762fc6 100644
--- a/tests/ui/useless_conversion.rs
+++ b/tests/ui/useless_conversion.rs
@@ -244,6 +244,47 @@ mod issue11300 {
         // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary.
         foo3([1, 2, 3].into_iter());
     }
+
+    fn ice() {
+        struct S1;
+        impl S1 {
+            pub fn foo<I: IntoIterator>(&self, _: I) {}
+        }
+
+        S1.foo([1, 2].into_iter());
+
+        // ICE that occured in itertools
+        trait Itertools {
+            fn interleave_shortest<J>(self, other: J)
+            where
+                J: IntoIterator,
+                Self: Sized;
+        }
+        impl<I: Iterator> Itertools for I {
+            fn interleave_shortest<J>(self, other: J)
+            where
+                J: IntoIterator,
+                Self: Sized,
+            {
+            }
+        }
+        let v0: Vec<i32> = vec![0, 2, 4];
+        let v1: Vec<i32> = vec![1, 3, 5, 7];
+        v0.into_iter().interleave_shortest(v1.into_iter());
+
+        trait TraitWithLifetime<'a> {}
+        impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {}
+
+        struct Helper;
+        impl<'a> Helper {
+            fn with_lt<I>(&self, _: I)
+            where
+                I: IntoIterator + TraitWithLifetime<'a>,
+            {
+            }
+        }
+        Helper.with_lt([&1, &2].into_iter());
+    }
 }
 
 #[derive(Copy, Clone)]
diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr
index 81a0898bdf6..c1f8b6b4aa9 100644
--- a/tests/ui/useless_conversion.stderr
+++ b/tests/ui/useless_conversion.stderr
@@ -202,5 +202,29 @@ note: this parameter accepts any `IntoIterator`, so you don't need to call `.int
 LL |         I: IntoIterator<Item = i32>,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 26 previous errors
+error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
+  --> $DIR/useless_conversion.rs:254:16
+   |
+LL |         S1.foo([1, 2].into_iter());
+   |                ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]`
+   |
+note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
+  --> $DIR/useless_conversion.rs:251:27
+   |
+LL |             pub fn foo<I: IntoIterator>(&self, _: I) {}
+   |                           ^^^^^^^^^^^^
+
+error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
+  --> $DIR/useless_conversion.rs:273:44
+   |
+LL |         v0.into_iter().interleave_shortest(v1.into_iter());
+   |                                            ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1`
+   |
+note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
+  --> $DIR/useless_conversion.rs:260:20
+   |
+LL |                 J: IntoIterator,
+   |                    ^^^^^^^^^^^^
+
+error: aborting due to 28 previous errors