diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 214a6e86d391a..df79f9d036b96 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -441,7 +441,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, leaf_trait_predicate, ); - self.note_version_mismatch(&mut err, leaf_trait_predicate); + self.note_version_mismatch(&mut err, &obligation, leaf_trait_predicate); self.suggest_remove_await(&obligation, &mut err); self.suggest_derive(&obligation, &mut err, leaf_trait_predicate); @@ -2337,6 +2337,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn note_version_mismatch( &self, err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let get_trait_impls = |trait_def_id| { @@ -2363,6 +2364,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let traits_with_same_path = traits_with_same_path.into_items().into_sorted_stable_ord_by_key(|(p, _)| p); let mut suggested = false; + for (_, trait_with_same_path) in traits_with_same_path { let trait_impls = get_trait_impls(trait_with_same_path); if trait_impls.is_empty() { @@ -2380,6 +2382,71 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.note(crate_msg); suggested = true; } + + if suggested { + return true; + } + + let trait_def_id = trait_pred.def_id(); + let trait_name = self.tcx.item_name(trait_def_id); + let trait_krate_name = self.tcx.crate_name(trait_def_id.krate); + + // If there are multiple different versions of a crate in the dependency graph, there is already + // a suggestion designed for this purpose in the rustc_hir_typeck compiler crate + if self + .tcx + .all_traits_including_private() + .find(|def_id| { + def_id.krate != trait_def_id.krate + && self.tcx.crate_name(def_id.krate) == trait_krate_name + && self.tcx.item_name(def_id) == trait_name + }) + .is_some() + { + return false; + } + + let trait_has_same_params = |other_trait_def_id: DefId| -> bool { + let trait_generics = self.tcx.generics_of(trait_def_id); + let other_trait_generics = self.tcx.generics_of(other_trait_def_id); + + if trait_generics.count() != other_trait_generics.count() { + return false; + } + trait_generics.own_params.iter().zip(other_trait_generics.own_params.iter()).all( + |(a, b)| { + (matches!(a.kind, ty::GenericParamDefKind::Type { .. }) + && matches!(b.kind, ty::GenericParamDefKind::Type { .. })) + || (matches!(a.kind, ty::GenericParamDefKind::Lifetime,) + && matches!(b.kind, ty::GenericParamDefKind::Lifetime)) + || (matches!(a.kind, ty::GenericParamDefKind::Const { .. }) + && matches!(b.kind, ty::GenericParamDefKind::Const { .. })) + }, + ) + }; + let trait_name = self.tcx.item_name(trait_def_id); + if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| { + trait_def_id != *def_id + && trait_name == self.tcx.item_name(def_id) + && trait_has_same_params(*def_id) + && self.predicate_must_hold_modulo_regions(&Obligation::new( + self.tcx, + obligation.cause.clone(), + obligation.param_env, + trait_pred.map_bound(|tr| ty::TraitPredicate { + trait_ref: ty::TraitRef::new(self.tcx, *def_id, tr.trait_ref.args), + ..tr + }), + )) + }) { + err.note(format!( + "`{}` implements similarly named `{}`, but not `{}`", + trait_pred.self_ty(), + self.tcx.def_path_str(other_trait_def_id), + trait_pred.print_modifiers_and_trait_path() + )); + suggested = true; + } suggested } diff --git a/tests/ui/traits/similarly_named_trait.rs b/tests/ui/traits/similarly_named_trait.rs new file mode 100644 index 0000000000000..e49c6df97b505 --- /dev/null +++ b/tests/ui/traits/similarly_named_trait.rs @@ -0,0 +1,27 @@ +trait Trait {} //~ HELP this trait has no implementations, consider adding one +trait TraitWithParam {} //~ HELP this trait has no implementations, consider adding one + +mod m { + pub trait Trait {} + pub trait TraitWithParam {} + pub struct St; + impl Trait for St {} + impl TraitWithParam for St {} +} + +fn func(_: T) {} //~ NOTE required by a bound in `func` +//~^ NOTE required by this bound in `func` + +fn func2> (_: T) {} //~ NOTE required by a bound in `func2` +//~^ NOTE required by this bound in `func2` + +fn main() { + func(m::St); //~ ERROR the trait bound `St: Trait` is not satisfied + //~^ NOTE the trait `Trait` is not implemented for `St` + //~| NOTE required by a bound introduced by this call + //~| NOTE `St` implements similarly named `m::Trait`, but not `Trait` + func2(m::St); //~ ERROR the trait bound `St: TraitWithParam` is not satisfied + //~^ NOTE the trait `TraitWithParam` is not implemented for `St` + //~| NOTE required by a bound introduced by this call + //~| NOTE `St` implements similarly named `m::TraitWithParam`, but not `TraitWithParam` +} diff --git a/tests/ui/traits/similarly_named_trait.stderr b/tests/ui/traits/similarly_named_trait.stderr new file mode 100644 index 0000000000000..655ee34bbb981 --- /dev/null +++ b/tests/ui/traits/similarly_named_trait.stderr @@ -0,0 +1,43 @@ +error[E0277]: the trait bound `St: Trait` is not satisfied + --> $DIR/similarly_named_trait.rs:19:10 + | +LL | func(m::St); + | ---- ^^^^^ the trait `Trait` is not implemented for `St` + | | + | required by a bound introduced by this call + | + = note: `St` implements similarly named `m::Trait`, but not `Trait` +help: this trait has no implementations, consider adding one + --> $DIR/similarly_named_trait.rs:1:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `func` + --> $DIR/similarly_named_trait.rs:12:12 + | +LL | fn func(_: T) {} + | ^^^^^ required by this bound in `func` + +error[E0277]: the trait bound `St: TraitWithParam` is not satisfied + --> $DIR/similarly_named_trait.rs:23:11 + | +LL | func2(m::St); + | ----- ^^^^^ the trait `TraitWithParam` is not implemented for `St` + | | + | required by a bound introduced by this call + | + = note: `St` implements similarly named `m::TraitWithParam`, but not `TraitWithParam` +help: this trait has no implementations, consider adding one + --> $DIR/similarly_named_trait.rs:2:1 + | +LL | trait TraitWithParam {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `func2` + --> $DIR/similarly_named_trait.rs:15:13 + | +LL | fn func2> (_: T) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `func2` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/union/issue-81199.stderr b/tests/ui/union/issue-81199.stderr index 7deba88fc803c..dbc4a0ab55346 100644 --- a/tests/ui/union/issue-81199.stderr +++ b/tests/ui/union/issue-81199.stderr @@ -16,6 +16,7 @@ error[E0277]: the trait bound `T: Pointee` is not satisfied LL | components: PtrComponents, | ^^^^^^^^^^^^^^^^ the trait `Pointee` is not implemented for `T` | + = note: `T` implements similarly named `std::ptr::Pointee`, but not `Pointee` note: required by a bound in `PtrComponents` --> $DIR/issue-81199.rs:11:25 |