Auto merge of #11301 - y21:issue11300, r=dswij
[`useless_conversion`]: don't lint if type parameter has unsatisfiable bounds for `.into_iter()` receiver Fixes #11300. Before this PR, clippy assumed that if it sees a `f(x.into_iter())` call and the type at that argument position is generic over any `IntoIterator`, then the `.into_iter()` call must be useless because `x` already implements `IntoIterator`, *however* this assumption is not right if the generic parameter has more than just the `IntoIterator` bound (because other traits can be implemented for the IntoIterator target type but not the IntoIterator implementor, as can be seen in the linked issue: `<[i32; 3] as IntoIterator>::IntoIter` satisfies `ExactSizeIterator`, but `[i32; 3]` does not). So, this PR makes it check that the type parameter only has a single `IntoIterator` bound. It *might* be possible to check if the type of `x` in `f(x.into_iter())` satisfies all the bounds on the generic type parameter as defined on the function (which would allow removing the `.into_iter()` call even with multiple bounds), however I'm not sure how to do that, and the current fix should always work. **Edit:** This PR has been changed to check if any of the bounds don't hold for the type of the `.into_iter()` receiver, so we can still lint in some cases. changelog: [`useless_conversion`]: don't lint `.into_iter()` if type parameter has multiple bounds
This commit is contained in:
commit
59636a2aa3
4 changed files with 331 additions and 31 deletions
|
|
@ -151,6 +151,8 @@ fn main() {
|
|||
let _ = s3;
|
||||
let s4: Foo<'a'> = Foo;
|
||||
let _ = vec![s4, s4, s4].into_iter();
|
||||
|
||||
issue11300::bar();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -196,6 +198,95 @@ fn explicit_into_iter_fn_arg() {
|
|||
b(macro_generated!());
|
||||
}
|
||||
|
||||
mod issue11300 {
|
||||
pub fn foo<I>(i: I)
|
||||
where
|
||||
I: IntoIterator<Item = i32> + ExactSizeIterator,
|
||||
{
|
||||
assert_eq!(i.len(), 3);
|
||||
}
|
||||
|
||||
trait Helper<T: ?Sized> {}
|
||||
impl Helper<i32> for [i32; 3] {}
|
||||
impl Helper<i32> for std::array::IntoIter<i32, 3> {}
|
||||
impl Helper<()> for std::array::IntoIter<i32, 3> {}
|
||||
|
||||
fn foo2<X: ?Sized, I>(_: I)
|
||||
where
|
||||
I: IntoIterator<Item = i32> + Helper<X>,
|
||||
{
|
||||
}
|
||||
|
||||
trait Helper2<T> {}
|
||||
impl Helper2<std::array::IntoIter<i32, 3>> for i32 {}
|
||||
impl Helper2<[i32; 3]> for i32 {}
|
||||
fn foo3<I>(_: I)
|
||||
where
|
||||
I: IntoIterator<Item = i32>,
|
||||
i32: Helper2<I>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn bar() {
|
||||
// This should not trigger the lint:
|
||||
// `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be
|
||||
// removed and is not useless.
|
||||
foo([1, 2, 3].into_iter());
|
||||
|
||||
// This should trigger the lint, receiver type [i32; 3] also implements `Helper`
|
||||
foo2::<i32, _>([1, 2, 3]);
|
||||
|
||||
// This again should *not* lint, since X = () and I = std::array::IntoIter<i32, 3>,
|
||||
// and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter<i32, 3>: Helper<()>` is).
|
||||
foo2::<(), _>([1, 2, 3].into_iter());
|
||||
|
||||
// This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`,
|
||||
// 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)]
|
||||
struct Foo<const C: char>;
|
||||
|
||||
|
|
|
|||
|
|
@ -151,6 +151,8 @@ fn main() {
|
|||
let _ = Foo::<'a'>::from(s3);
|
||||
let s4: Foo<'a'> = Foo;
|
||||
let _ = vec![s4, s4, s4].into_iter().into_iter();
|
||||
|
||||
issue11300::bar();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -196,6 +198,95 @@ fn explicit_into_iter_fn_arg() {
|
|||
b(macro_generated!());
|
||||
}
|
||||
|
||||
mod issue11300 {
|
||||
pub fn foo<I>(i: I)
|
||||
where
|
||||
I: IntoIterator<Item = i32> + ExactSizeIterator,
|
||||
{
|
||||
assert_eq!(i.len(), 3);
|
||||
}
|
||||
|
||||
trait Helper<T: ?Sized> {}
|
||||
impl Helper<i32> for [i32; 3] {}
|
||||
impl Helper<i32> for std::array::IntoIter<i32, 3> {}
|
||||
impl Helper<()> for std::array::IntoIter<i32, 3> {}
|
||||
|
||||
fn foo2<X: ?Sized, I>(_: I)
|
||||
where
|
||||
I: IntoIterator<Item = i32> + Helper<X>,
|
||||
{
|
||||
}
|
||||
|
||||
trait Helper2<T> {}
|
||||
impl Helper2<std::array::IntoIter<i32, 3>> for i32 {}
|
||||
impl Helper2<[i32; 3]> for i32 {}
|
||||
fn foo3<I>(_: I)
|
||||
where
|
||||
I: IntoIterator<Item = i32>,
|
||||
i32: Helper2<I>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn bar() {
|
||||
// This should not trigger the lint:
|
||||
// `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be
|
||||
// removed and is not useless.
|
||||
foo([1, 2, 3].into_iter());
|
||||
|
||||
// This should trigger the lint, receiver type [i32; 3] also implements `Helper`
|
||||
foo2::<i32, _>([1, 2, 3].into_iter());
|
||||
|
||||
// This again should *not* lint, since X = () and I = std::array::IntoIter<i32, 3>,
|
||||
// and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter<i32, 3>: Helper<()>` is).
|
||||
foo2::<(), _>([1, 2, 3].into_iter());
|
||||
|
||||
// This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`,
|
||||
// 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)]
|
||||
struct Foo<const C: char>;
|
||||
|
||||
|
|
|
|||
|
|
@ -119,64 +119,112 @@ LL | let _ = vec![s4, s4, s4].into_iter().into_iter();
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()`
|
||||
|
||||
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
|
||||
--> $DIR/useless_conversion.rs:183:7
|
||||
--> $DIR/useless_conversion.rs:185:7
|
||||
|
|
||||
LL | b(vec![1, 2].into_iter());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]`
|
||||
|
|
||||
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
|
||||
--> $DIR/useless_conversion.rs:173:13
|
||||
--> $DIR/useless_conversion.rs:175:13
|
||||
|
|
||||
LL | fn b<T: IntoIterator<Item = i32>>(_: T) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
|
||||
--> $DIR/useless_conversion.rs:184:7
|
||||
--> $DIR/useless_conversion.rs:186:7
|
||||
|
|
||||
LL | c(vec![1, 2].into_iter());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]`
|
||||
|
|
||||
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
|
||||
--> $DIR/useless_conversion.rs:174:18
|
||||
--> $DIR/useless_conversion.rs:176:18
|
||||
|
|
||||
LL | fn c(_: impl IntoIterator<Item = i32>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
|
||||
--> $DIR/useless_conversion.rs:185:7
|
||||
--> $DIR/useless_conversion.rs:187:7
|
||||
|
|
||||
LL | d(vec![1, 2].into_iter());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]`
|
||||
|
|
||||
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
|
||||
--> $DIR/useless_conversion.rs:177:12
|
||||
--> $DIR/useless_conversion.rs:179:12
|
||||
|
|
||||
LL | T: IntoIterator<Item = i32>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
|
||||
--> $DIR/useless_conversion.rs:188:7
|
||||
--> $DIR/useless_conversion.rs:190:7
|
||||
|
|
||||
LL | b(vec![1, 2].into_iter().into_iter());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]`
|
||||
|
|
||||
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
|
||||
--> $DIR/useless_conversion.rs:173:13
|
||||
--> $DIR/useless_conversion.rs:175:13
|
||||
|
|
||||
LL | fn b<T: IntoIterator<Item = i32>>(_: T) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
|
||||
--> $DIR/useless_conversion.rs:189:7
|
||||
--> $DIR/useless_conversion.rs:191:7
|
||||
|
|
||||
LL | b(vec![1, 2].into_iter().into_iter().into_iter());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]`
|
||||
|
|
||||
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
|
||||
--> $DIR/useless_conversion.rs:173:13
|
||||
--> $DIR/useless_conversion.rs:175:13
|
||||
|
|
||||
LL | fn b<T: IntoIterator<Item = i32>>(_: T) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 24 previous errors
|
||||
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
|
||||
--> $DIR/useless_conversion.rs:237:24
|
||||
|
|
||||
LL | foo2::<i32, _>([1, 2, 3].into_iter());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]`
|
||||
|
|
||||
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
|
||||
--> $DIR/useless_conversion.rs:216:12
|
||||
|
|
||||
LL | I: IntoIterator<Item = i32> + Helper<X>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit call to `.into_iter()` in function argument accepting `IntoIterator`
|
||||
--> $DIR/useless_conversion.rs:245:14
|
||||
|
|
||||
LL | foo3([1, 2, 3].into_iter());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]`
|
||||
|
|
||||
note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`
|
||||
--> $DIR/useless_conversion.rs:225:12
|
||||
|
|
||||
LL | I: IntoIterator<Item = i32>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue