diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 21611e9c58600..7f00dd2edff69 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -284,6 +284,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { self.one_bound_for_assoc_type( || traits::supertraits(tcx, trait_ref), trait_ref.skip_binder().print_only_trait_name(), + None, binding.item_name, path_span, match binding.kind { diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index ed4dde419c4f9..0cac434f4507c 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -6,10 +6,9 @@ use crate::errors::{ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::FulfillmentError; -use rustc_middle::ty::TyCtxt; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident}; @@ -102,6 +101,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, all_candidates: impl Fn() -> I, ty_param_name: &str, + ty_param_def_id: Option, assoc_name: Ident, span: Span, ) -> ErrorGuaranteed @@ -190,13 +190,60 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) .collect::>()[..] { + let trait_name = self.tcx().def_path_str(*best_trait); err.span_label( assoc_name.span, format!( - "there is a similarly named associated type `{suggested_name}` in the trait `{}`", - self.tcx().def_path_str(*best_trait) + "there is a similarly named associated type `{suggested_name}` in the \ + trait `{trait_name}`", ), ); + let hir = self.tcx().hir(); + if let Some(def_id) = ty_param_def_id + && let parent = hir.get_parent_item(hir.local_def_id_to_hir_id(def_id)) + && let Some(generics) = hir.get_generics(parent.def_id) + { + if generics.bounds_for_param(def_id) + .flat_map(|pred| pred.bounds.iter()) + .any(|b| match b { + hir::GenericBound::Trait(t, ..) => { + t.trait_ref.trait_def_id().as_ref() == Some(best_trait) + } + _ => false, + }) + { + // The type param already has a bound for `trait_name`, we just need to + // change the associated type. + err.span_suggestion_verbose( + assoc_name.span, + format!( + "change the associated type name to use `{suggested_name}` from \ + `{trait_name}`", + ), + suggested_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else if suggest_constraining_type_param( + self.tcx(), + generics, + &mut err, + &ty_param_name, + &trait_name, + None, + None, + ) + && suggested_name != assoc_name.name + { + // We suggested constraining a type parameter, but the associated type on it + // was also not an exact match, so we also suggest changing it. + err.span_suggestion_verbose( + assoc_name.span, + "and also change the associated type name", + suggested_name.to_string(), + Applicability::MaybeIncorrect, + ); + } + } return err.emit(); } } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 56b1fd36973f7..bbfe7f114b066 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1061,6 +1061,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) }, param_name, + Some(ty_param_def_id), assoc_name, span, None, @@ -1074,6 +1075,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, all_candidates: impl Fn() -> I, ty_param_name: impl Display, + ty_param_def_id: Option, assoc_name: Ident, span: Span, is_equality: Option>, @@ -1095,6 +1097,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let reported = self.complain_about_assoc_type_not_found( all_candidates, &ty_param_name.to_string(), + ty_param_def_id, assoc_name, span, ); @@ -1142,30 +1145,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.span_label( bound_span, format!( - "ambiguous `{}` from `{}`", - assoc_name, + "ambiguous `{assoc_name}` from `{}`", bound.print_only_trait_path(), ), ); if let Some(constraint) = &is_equality { where_bounds.push(format!( - " T: {trait}::{assoc} = {constraint}", + " T: {trait}::{assoc_name} = {constraint}", trait=bound.print_only_trait_path(), - assoc=assoc_name, - constraint=constraint, )); } else { err.span_suggestion_verbose( span.with_hi(assoc_name.span.lo()), "use fully qualified syntax to disambiguate", - format!("<{} as {}>::", ty_param_name, bound.print_only_trait_path()), + format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), Applicability::MaybeIncorrect, ); } } else { err.note(format!( - "associated type `{}` could derive from `{}`", - ty_param_name, + "associated type `{ty_param_name}` could derive from `{}`", bound.print_only_trait_path(), )); } @@ -1173,8 +1172,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if !where_bounds.is_empty() { err.help(format!( "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {},\n{}", - ty_param_name, + \n where\n T: {ty_param_name},\n{}", where_bounds.join(",\n"), )); } @@ -1394,6 +1392,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) }, kw::SelfUpper, + None, assoc_ident, span, None, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 15f2ba809a4a6..674965098e953 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -364,7 +364,7 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) - /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but /// it can also be an `impl Trait` param that needs to be decomposed to a type /// param for cleaner code. -fn suggest_restriction<'tcx>( +pub fn suggest_restriction<'tcx>( tcx: TyCtxt<'tcx>, item_id: LocalDefId, hir_generics: &hir::Generics<'tcx>, diff --git a/tests/ui/resolve/issue-55673.fixed b/tests/ui/resolve/issue-55673.fixed new file mode 100644 index 0000000000000..c3377857535f0 --- /dev/null +++ b/tests/ui/resolve/issue-55673.fixed @@ -0,0 +1,14 @@ +// run-rustfix +#![allow(dead_code)] +trait Foo { + type Bar; +} + +fn foo() +where + T::Bar: std::fmt::Debug, + //~^ ERROR associated type `Baa` not found for `T` +{ +} + +fn main() {} diff --git a/tests/ui/resolve/issue-55673.rs b/tests/ui/resolve/issue-55673.rs index 0436bd397424c..d4adacc1a3b64 100644 --- a/tests/ui/resolve/issue-55673.rs +++ b/tests/ui/resolve/issue-55673.rs @@ -1,3 +1,5 @@ +// run-rustfix +#![allow(dead_code)] trait Foo { type Bar; } diff --git a/tests/ui/resolve/issue-55673.stderr b/tests/ui/resolve/issue-55673.stderr index 39318f959056f..7171c8b4933be 100644 --- a/tests/ui/resolve/issue-55673.stderr +++ b/tests/ui/resolve/issue-55673.stderr @@ -1,8 +1,13 @@ error[E0220]: associated type `Baa` not found for `T` - --> $DIR/issue-55673.rs:7:8 + --> $DIR/issue-55673.rs:9:8 | LL | T::Baa: std::fmt::Debug, | ^^^ there is a similarly named associated type `Bar` in the trait `Foo` + | +help: change the associated type name to use `Bar` from `Foo` + | +LL | T::Bar: std::fmt::Debug, + | ~~~ error: aborting due to previous error diff --git a/tests/ui/type-alias-impl-trait/not_well_formed.fixed b/tests/ui/type-alias-impl-trait/not_well_formed.fixed new file mode 100644 index 0000000000000..d98e83ff6dd02 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/not_well_formed.fixed @@ -0,0 +1,19 @@ +// run-rustfix +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +fn main() {} + +trait TraitWithAssoc { + type Assoc; +} + +type Foo = impl Trait; //~ associated type `Assoc` not found for `V` + +trait Trait {} + +impl Trait for () {} + +fn foo_desugared(_: T) -> Foo { + () +} diff --git a/tests/ui/type-alias-impl-trait/not_well_formed.rs b/tests/ui/type-alias-impl-trait/not_well_formed.rs index fbb7a4d58e4ba..18f173a693d22 100644 --- a/tests/ui/type-alias-impl-trait/not_well_formed.rs +++ b/tests/ui/type-alias-impl-trait/not_well_formed.rs @@ -1,4 +1,6 @@ +// run-rustfix #![feature(type_alias_impl_trait)] +#![allow(dead_code)] fn main() {} diff --git a/tests/ui/type-alias-impl-trait/not_well_formed.stderr b/tests/ui/type-alias-impl-trait/not_well_formed.stderr index c36b95f47e837..e89cae2d10444 100644 --- a/tests/ui/type-alias-impl-trait/not_well_formed.stderr +++ b/tests/ui/type-alias-impl-trait/not_well_formed.stderr @@ -1,8 +1,13 @@ error[E0220]: associated type `Assoc` not found for `V` - --> $DIR/not_well_formed.rs:9:29 + --> $DIR/not_well_formed.rs:11:29 | LL | type Foo = impl Trait; | ^^^^^ there is a similarly named associated type `Assoc` in the trait `TraitWithAssoc` + | +help: consider restricting type parameter `V` + | +LL | type Foo = impl Trait; + | ++++++++++++++++ error: aborting due to previous error