Skip to content

Commit

Permalink
Do not suggest 'Trait<Assoc=arg>' when in trait impl
Browse files Browse the repository at this point in the history
because that would be illegal syntax
  • Loading branch information
gurry committed Oct 10, 2023
1 parent 37fda98 commit 5f82f51
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,47 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
if self.missing_lifetimes() { "lifetime" } else { "generic" }
}

fn is_trait(&self) -> bool {
self.tcx.is_trait(self.def_id)
}

/// 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 {
self.is_trait() && {
// Here we check if the reference to the generic type
// is from the 'of_trait' field of the enclosing impl

let path_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, .. }) = path_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
}
}

fn num_provided_args(&self) -> usize {
if self.missing_lifetimes() {
self.num_provided_lifetime_args()
Expand Down Expand Up @@ -948,20 +989,26 @@ 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() {
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} = ")))
.collect::<Vec<_>>();

if !suggestions.is_empty() {
err.multipart_suggestion_verbose(
format!(
"replace the generic bound{s} with the associated type{s}",
s = pluralize!(unbound_types.len())
),
suggestions,
Applicability::MaybeIncorrect,
);
// 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} = "))
})
.collect::<Vec<_>>();

if !suggestions.is_empty() {
err.multipart_suggestion_verbose(
format!(
"replace the generic bound{s} with the associated type{s}",
s = pluralize!(unbound_types.len())
),
suggestions,
Applicability::MaybeIncorrect,
);
}
}
} else if remove_entire_generics {
let span = self
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {
}
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 5f82f51

Please sign in to comment.