diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5afb1f76df8c..f32e7edad6cb 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 f3504bc62b45..ed8387b7eb2c 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(&self, _: I) {} + } + + S1.foo([1, 2]); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = 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(&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 a4f4722927fe..991a7762fc64 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(&self, _: I) {} + } + + S1.foo([1, 2].into_iter()); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = 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(&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 81a0898bdf62..c1f8b6b4aa96 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, | ^^^^^^^^^^^^^^^^^^^^^^^^ -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(&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