Do not suggest 'Trait<Assoc=arg>' when in trait impl

because that would be illegal syntax
This commit is contained in:
Gurinder Singh 2023-10-25 09:11:16 +05:30
parent 37fda989ea
commit 079b290439
3 changed files with 209 additions and 13 deletions

View File

@ -129,6 +129,44 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
if self.missing_lifetimes() { "lifetime" } else { "generic" }
}
/// Returns true if the generic type is a trait
/// and is being referred to from one of its trait impls
fn is_in_trait_impl(&self) -> bool {
if self.tcx.is_trait(self.def_id) {
// Here we check if the reference to the generic type
// is from the 'of_trait' field of the enclosing impl
let parent = self.tcx.hir().get_parent(self.path_segment.hir_id);
let parent_item = self
.tcx
.hir()
.get_by_def_id(self.tcx.hir().get_parent_item(self.path_segment.hir_id).def_id);
// Get the HIR id of the trait ref
let hir::Node::TraitRef(hir::TraitRef { hir_ref_id: trait_ref_id, .. }) = parent else {
return false;
};
// Get the HIR id of the 'of_trait' field of the impl
let hir::Node::Item(hir::Item {
kind:
hir::ItemKind::Impl(hir::Impl {
of_trait: Some(hir::TraitRef { hir_ref_id: id_in_of_trait, .. }),
..
}),
..
}) = parent_item
else {
return false;
};
// Check that trait is referred to from the of_trait field of impl
trait_ref_id == id_in_of_trait
} else {
false
}
}
fn num_provided_args(&self) -> usize {
if self.missing_lifetimes() {
self.num_provided_lifetime_args()
@ -948,9 +986,14 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
// If there is a single unbound associated type and a single excess generic param
// suggest replacing the generic param with the associated type bound
if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
// Don't suggest if we're in a trait impl as
// that would result in invalid syntax (fixes #116464)
if !self.is_in_trait_impl() {
let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
let suggestions = iter::zip(unused_generics, &unbound_types)
.map(|(potential, name)| (potential.span().shrink_to_lo(), format!("{name} = ")))
.map(|(potential, name)| {
(potential.span().shrink_to_lo(), format!("{name} = "))
})
.collect::<Vec<_>>();
if !suggestions.is_empty() {
@ -963,6 +1006,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
}
} else if remove_entire_generics {
let span = self
.path_segment

View File

@ -0,0 +1,43 @@
// Regression test for #116464
// Checks that we do not suggest Trait<..., Assoc=arg> when the trait
// is referred to from one of its impls but do so at all other places
pub trait Trait<T> {
type Assoc;
}
impl<T, S> Trait<T> for i32 {
type Assoc = String;
}
// Should not not trigger suggestion here...
impl<T, S> Trait<T, S> for () {}
//~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied
//... but should do so in all of the below cases except the last one
fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), i32> {
//~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied
//~| ERROR trait takes 1 generic argument but 2 generic arguments were supplied
3
}
struct Struct<T: Trait<u32, String>> {
//~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied
a: T
}
trait AnotherTrait<T: Trait<T, i32>> {}
//~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied
impl<T: Trait<u32, String>> Struct<T> {}
//~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied
// Test for self type. Should not trigger suggestion as it doesn't have an
// associated type
trait YetAnotherTrait {}
impl<T: Trait<u32, Assoc=String>, U> YetAnotherTrait for Struct<T, U> {}
//~^ ERROR struct takes 1 generic argument but 2 generic arguments were supplied
fn main() {
}

View File

@ -0,0 +1,109 @@
error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:14:12
|
LL | impl<T, S> Trait<T, S> for () {}
| ^^^^^ expected 1 generic argument
|
note: trait defined here, with 1 generic parameter: `T`
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:5:11
|
LL | pub trait Trait<T> {
| ^^^^^ -
error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:18:12
|
LL | fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), i32> {
| ^^^^^ expected 1 generic argument
|
note: trait defined here, with 1 generic parameter: `T`
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:5:11
|
LL | pub trait Trait<T> {
| ^^^^^ -
help: replace the generic bound with the associated type
|
LL | fn func<T: Trait<u32, Assoc = String>>(t: T) -> impl Trait<(), i32> {
| +++++++
error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:18:46
|
LL | fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), i32> {
| ^^^^^ expected 1 generic argument
|
note: trait defined here, with 1 generic parameter: `T`
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:5:11
|
LL | pub trait Trait<T> {
| ^^^^^ -
help: replace the generic bound with the associated type
|
LL | fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), Assoc = i32> {
| +++++++
error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:24:18
|
LL | struct Struct<T: Trait<u32, String>> {
| ^^^^^ expected 1 generic argument
|
note: trait defined here, with 1 generic parameter: `T`
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:5:11
|
LL | pub trait Trait<T> {
| ^^^^^ -
help: replace the generic bound with the associated type
|
LL | struct Struct<T: Trait<u32, Assoc = String>> {
| +++++++
error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:29:23
|
LL | trait AnotherTrait<T: Trait<T, i32>> {}
| ^^^^^ expected 1 generic argument
|
note: trait defined here, with 1 generic parameter: `T`
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:5:11
|
LL | pub trait Trait<T> {
| ^^^^^ -
help: replace the generic bound with the associated type
|
LL | trait AnotherTrait<T: Trait<T, Assoc = i32>> {}
| +++++++
error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:32:9
|
LL | impl<T: Trait<u32, String>> Struct<T> {}
| ^^^^^ expected 1 generic argument
|
note: trait defined here, with 1 generic parameter: `T`
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:5:11
|
LL | pub trait Trait<T> {
| ^^^^^ -
help: replace the generic bound with the associated type
|
LL | impl<T: Trait<u32, Assoc = String>> Struct<T> {}
| +++++++
error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:38:58
|
LL | impl<T: Trait<u32, Assoc=String>, U> YetAnotherTrait for Struct<T, U> {}
| ^^^^^^ - help: remove this generic argument
| |
| expected 1 generic argument
|
note: struct defined here, with 1 generic parameter: `T`
--> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:24:8
|
LL | struct Struct<T: Trait<u32, String>> {
| ^^^^^^ -
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0107`.