Skip to content

Commit 18f3689

Browse files
committed
use the correct node args for substitution
1 parent cf10690 commit 18f3689

File tree

4 files changed

+137
-23
lines changed

4 files changed

+137
-23
lines changed

clippy_lints/src/useless_conversion.rs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt;
1212
use rustc_infer::traits::Obligation;
1313
use rustc_lint::{LateContext, LateLintPass};
1414
use rustc_middle::traits::ObligationCause;
15-
use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty};
15+
use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
1616
use rustc_session::{declare_tool_lint, impl_lint_pass};
1717
use rustc_span::{sym, Span};
1818
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -93,27 +93,35 @@ fn into_iter_bound<'tcx>(
9393

9494
for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates {
9595
if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() {
96-
if tr.def_id() == into_iter_did && tr.self_ty().is_param(param_index) {
97-
into_iter_span = Some(*span);
98-
} else {
99-
// Substitute generics in the predicate and replace the IntoIterator type parameter with the
100-
// `.into_iter()` receiver to see if the bound also holds for that type.
101-
let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| {
102-
if i == param_index as usize {
103-
GenericArg::from(into_iter_receiver)
104-
} else {
105-
arg
96+
if tr.self_ty().is_param(param_index) {
97+
if tr.def_id() == into_iter_did {
98+
into_iter_span = Some(*span);
99+
} else {
100+
let tr = cx.tcx.erase_regions(tr);
101+
if tr.has_escaping_bound_vars() {
102+
return None;
103+
}
104+
105+
// Substitute generics in the predicate and replace the IntoIterator type parameter with the
106+
// `.into_iter()` receiver to see if the bound also holds for that type.
107+
let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| {
108+
if i == param_index as usize {
109+
GenericArg::from(into_iter_receiver)
110+
} else {
111+
arg
112+
}
113+
}));
114+
115+
let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args);
116+
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate);
117+
if !cx
118+
.tcx
119+
.infer_ctxt()
120+
.build()
121+
.predicate_must_hold_modulo_regions(&obligation)
122+
{
123+
return None;
106124
}
107-
}));
108-
let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args);
109-
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate);
110-
if !cx
111-
.tcx
112-
.infer_ctxt()
113-
.build()
114-
.predicate_must_hold_modulo_regions(&obligation)
115-
{
116-
return None;
117125
}
118126
}
119127
}
@@ -216,7 +224,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
216224
return (
217225
did,
218226
args,
219-
cx.typeck_results().node_args(recv.hir_id),
227+
cx.typeck_results().node_args(parent.hir_id),
220228
MethodOrFunction::Method
221229
);
222230
})

tests/ui/useless_conversion.fixed

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,47 @@ mod issue11300 {
244244
// and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary.
245245
foo3([1, 2, 3]);
246246
}
247+
248+
fn ice() {
249+
struct S1;
250+
impl S1 {
251+
pub fn foo<I: IntoIterator>(&self, _: I) {}
252+
}
253+
254+
S1.foo([1, 2]);
255+
256+
// ICE that occured in itertools
257+
trait Itertools {
258+
fn interleave_shortest<J>(self, other: J)
259+
where
260+
J: IntoIterator,
261+
Self: Sized;
262+
}
263+
impl<I: Iterator> Itertools for I {
264+
fn interleave_shortest<J>(self, other: J)
265+
where
266+
J: IntoIterator,
267+
Self: Sized,
268+
{
269+
}
270+
}
271+
let v0: Vec<i32> = vec![0, 2, 4];
272+
let v1: Vec<i32> = vec![1, 3, 5, 7];
273+
v0.into_iter().interleave_shortest(v1);
274+
275+
trait TraitWithLifetime<'a> {}
276+
impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {}
277+
278+
struct Helper;
279+
impl<'a> Helper {
280+
fn with_lt<I>(&self, _: I)
281+
where
282+
I: IntoIterator + TraitWithLifetime<'a>,
283+
{
284+
}
285+
}
286+
Helper.with_lt([&1, &2].into_iter());
287+
}
247288
}
248289

249290
#[derive(Copy, Clone)]

tests/ui/useless_conversion.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,47 @@ mod issue11300 {
244244
// and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary.
245245
foo3([1, 2, 3].into_iter());
246246
}
247+
248+
fn ice() {
249+
struct S1;
250+
impl S1 {
251+
pub fn foo<I: IntoIterator>(&self, _: I) {}
252+
}
253+
254+
S1.foo([1, 2].into_iter());
255+
256+
// ICE that occured in itertools
257+
trait Itertools {
258+
fn interleave_shortest<J>(self, other: J)
259+
where
260+
J: IntoIterator,
261+
Self: Sized;
262+
}
263+
impl<I: Iterator> Itertools for I {
264+
fn interleave_shortest<J>(self, other: J)
265+
where
266+
J: IntoIterator,
267+
Self: Sized,
268+
{
269+
}
270+
}
271+
let v0: Vec<i32> = vec![0, 2, 4];
272+
let v1: Vec<i32> = vec![1, 3, 5, 7];
273+
v0.into_iter().interleave_shortest(v1.into_iter());
274+
275+
trait TraitWithLifetime<'a> {}
276+
impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {}
277+
278+
struct Helper;
279+
impl<'a> Helper {
280+
fn with_lt<I>(&self, _: I)
281+
where
282+
I: IntoIterator + TraitWithLifetime<'a>,
283+
{
284+
}
285+
}
286+
Helper.with_lt([&1, &2].into_iter());
287+
}
247288
}
248289

249290
#[derive(Copy, Clone)]

tests/ui/useless_conversion.stderr

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,5 +202,29 @@ note: this parameter accepts any `IntoIterator`, so you don't need to call `.int
202202
LL | I: IntoIterator<Item = i32>,
203203
| ^^^^^^^^^^^^^^^^^^^^^^^^
204204

205-
error: aborting due to 26 previous errors
205+
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
206+
--> $DIR/useless_conversion.rs:254:16
207+
|
208+
LL | S1.foo([1, 2].into_iter());
209+
| ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]`
210+
|
211+
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
212+
--> $DIR/useless_conversion.rs:251:27
213+
|
214+
LL | pub fn foo<I: IntoIterator>(&self, _: I) {}
215+
| ^^^^^^^^^^^^
216+
217+
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
218+
--> $DIR/useless_conversion.rs:273:44
219+
|
220+
LL | v0.into_iter().interleave_shortest(v1.into_iter());
221+
| ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1`
222+
|
223+
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
224+
--> $DIR/useless_conversion.rs:260:20
225+
|
226+
LL | J: IntoIterator,
227+
| ^^^^^^^^^^^^
228+
229+
error: aborting due to 28 previous errors
206230

0 commit comments

Comments
 (0)