diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index c209bf20c43..367f6e17e7f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -20,8 +20,13 @@ hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identi } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg = + consider fully qualifying{$identically_named -> + [true] {""} + *[false] {" "}and renaming + } the associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` -hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name +hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 8e8b3b3d32a..90861ba1ff8 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -126,6 +126,22 @@ pub enum AssocItemNotFoundSugg<'a> { assoc_kind: &'static str, suggested_name: Symbol, }, + #[multipart_suggestion( + hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg, + style = "verbose", + applicability = "maybe-incorrect" + )] + SimilarInOtherTraitQPath { + #[suggestion_part(code = "<")] + lo: Span, + #[suggestion_part(code = " as {trait_}>")] + mi: Span, + #[suggestion_part(code = "{suggested_name}")] + hi: Option, + trait_: &'a str, + suggested_name: Symbol, + identically_named: bool, + }, #[suggestion( hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}", diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index ca741e4fbb6..fd2bbabe779 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -219,7 +219,7 @@ pub(super) fn complain_about_assoc_item_not_found( suggested_name, identically_named: suggested_name == assoc_name.name, }); - if let AssocItemQSelf::TyParam(ty_param_def_id) = qself + if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're // inside an opaque type while we're interested in the overarching type alias (TAIT). // FIXME: However, for trait aliases, this incorrectly returns the enclosing module... @@ -251,27 +251,44 @@ pub(super) fn complain_about_assoc_item_not_found( return self.dcx().emit_err(err); } - let mut err = self.dcx().create_err(err); - if suggest_constraining_type_param( - tcx, - generics, - &mut err, - &qself_str, - &trait_name, - None, - None, - ) && suggested_name != assoc_name.name + let identically_named = suggested_name == assoc_name.name; + + if let DefKind::TyAlias = tcx.def_kind(item_def_id) + && !tcx.type_alias_is_lazy(item_def_id) { - // We suggested constraining a type parameter, but the associated item on it - // was also not an exact match, so we also suggest changing it. - err.span_suggestion_verbose( - assoc_name.span, - fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath { + lo: ty_param_span.shrink_to_lo(), + mi: ty_param_span.shrink_to_hi(), + hi: (!identically_named).then_some(assoc_name.span), + // FIXME(fmease): Use a full trait ref here (with placeholders). + trait_: &trait_name, + identically_named, suggested_name, - Applicability::MaybeIncorrect, - ); + }); + } else { + let mut err = self.dcx().create_err(err); + if suggest_constraining_type_param( + tcx, + generics, + &mut err, + &qself_str, + // FIXME(fmease): Use a full trait ref here (with placeholders). + &trait_name, + None, + None, + ) && !identically_named + { + // We suggested constraining a type parameter, but the associated item on it + // was also not an exact match, so we also suggest changing it. + err.span_suggestion_verbose( + assoc_name.span, + fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + suggested_name, + Applicability::MaybeIncorrect, + ); + } + return err.emit(); } - return err.emit(); } return self.dcx().emit_err(err); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 8d9055076a3..ce298641e60 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -197,7 +197,7 @@ fn lowerer(&self) -> &dyn HirTyLowerer<'tcx> /// For diagnostic purposes only. enum AssocItemQSelf { Trait(DefId), - TyParam(LocalDefId), + TyParam(LocalDefId, Span), SelfTyAlias, } @@ -205,7 +205,7 @@ impl AssocItemQSelf { fn to_string(&self, tcx: TyCtxt<'_>) -> String { match *self { Self::Trait(def_id) => tcx.def_path_str(def_id), - Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(), + Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(), Self::SelfTyAlias => kw::SelfUpper.to_string(), } } @@ -820,6 +820,7 @@ fn lower_path_segment( fn probe_single_ty_param_bound_for_assoc_ty( &self, ty_param_def_id: LocalDefId, + ty_param_span: Span, assoc_name: Ident, span: Span, ) -> Result, ErrorGuaranteed> { @@ -836,7 +837,7 @@ fn probe_single_ty_param_bound_for_assoc_ty( .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) }, - AssocItemQSelf::TyParam(ty_param_def_id), + AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), ty::AssocKind::Type, assoc_name, span, @@ -1080,6 +1081,7 @@ pub fn lower_assoc_path( Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), ) => self.probe_single_ty_param_bound_for_assoc_ty( param_did.expect_local(), + qself.span, assoc_ident, span, )?, diff --git a/tests/ui/resolve/issue-55673.stderr b/tests/ui/resolve/issue-55673.stderr index ffc3252230a..4069b35a998 100644 --- a/tests/ui/resolve/issue-55673.stderr +++ b/tests/ui/resolve/issue-55673.stderr @@ -19,7 +19,7 @@ help: consider further restricting type parameter `T` | LL | T::Baa: std::fmt::Debug, T: Foo | ~~~~~~~~ -help: and also change the associated type name +help: ...and changing the associated type name | LL | T::Bar: std::fmt::Debug, | ~~~ diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr new file mode 100644 index 00000000000..8e6490c548d --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr @@ -0,0 +1,25 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + | +LL | type AssocOf = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying the associated type + | +LL | type AssocOf = ::Assoc; + | + +++++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + | +LL | type AssokOf = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying and renaming the associated type + | +LL | type AssokOf = ::Assoc; + | + +++++++++ ~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr new file mode 100644 index 00000000000..051f181cb6f --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr @@ -0,0 +1,29 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + | +LL | type AssocOf = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssocOf = T::Assoc; + | +++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + | +LL | type AssokOf = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssokOf = T::Assok; + | +++++++ +help: ...and changing the associated type name + | +LL | type AssokOf = T::Assoc; + | ~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs new file mode 100644 index 00000000000..75e4eb496b4 --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs @@ -0,0 +1,20 @@ +// Ensure that we don't suggest *type alias bounds* for **eager** type aliases. +// issue: rust-lang/rust#125789 + +//@ revisions: eager lazy +#![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))] + +// FIXME(fmease): Suggest a full trait ref (with placeholders) instead of just a trait name. + +trait Trait { type Assoc; } + +type AssocOf = T::Assoc; //~ ERROR associated type `Assoc` not found for `T` +//[eager]~^ HELP consider fully qualifying the associated type +//[lazy]~| HELP consider restricting type parameter `T` + +type AssokOf = T::Assok; //~ ERROR associated type `Assok` not found for `T` +//[eager]~^ HELP consider fully qualifying and renaming the associated type +//[lazy]~| HELP consider restricting type parameter `T` +//[lazy]~| HELP and changing the associated type name + +fn main() {}