From 079b29043976afd44cf49eec74486bf2fc15973d Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Wed, 25 Oct 2023 09:11:16 +0530 Subject: [PATCH] Do not suggest 'Trait' when in trait impl because that would be illegal syntax --- .../wrong_number_of_generic_args.rs | 72 +++++++++--- ...lid-assoc-type-suggestion-in-trait-impl.rs | 43 +++++++ ...assoc-type-suggestion-in-trait-impl.stderr | 109 ++++++++++++++++++ 3 files changed, 210 insertions(+), 14 deletions(-) create mode 100644 tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs create mode 100644 tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 61b182b1be782..fe0576358dd8f 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -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,20 +986,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::>(); - - 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::>(); + + 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 diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs new file mode 100644 index 0000000000000..e0edd522431a5 --- /dev/null +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs @@ -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 { + type Assoc; +} + +impl Trait for i32 { + type Assoc = String; +} + +// Should not not trigger suggestion here... +impl Trait 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: 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> { +//~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied + a: T +} + +trait AnotherTrait> {} +//~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied + +impl> Struct {} +//~^ 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, U> YetAnotherTrait for Struct {} +//~^ ERROR struct takes 1 generic argument but 2 generic arguments were supplied + + +fn main() { +} diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr new file mode 100644 index 0000000000000..711ccf1b6682c --- /dev/null +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr @@ -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 Trait 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 { + | ^^^^^ - + +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: 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 { + | ^^^^^ - +help: replace the generic bound with the associated type + | +LL | fn func>(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: 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 { + | ^^^^^ - +help: replace the generic bound with the associated type + | +LL | fn func>(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> { + | ^^^^^ 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 { + | ^^^^^ - +help: replace the generic bound with the associated type + | +LL | struct Struct> { + | +++++++ + +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> {} + | ^^^^^ 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 { + | ^^^^^ - +help: replace the generic bound with the associated type + | +LL | trait AnotherTrait> {} + | +++++++ + +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> Struct {} + | ^^^^^ 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 { + | ^^^^^ - +help: replace the generic bound with the associated type + | +LL | impl> Struct {} + | +++++++ + +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, U> YetAnotherTrait for Struct {} + | ^^^^^^ - 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> { + | ^^^^^^ - + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0107`.