From abdb0872605be237b97405c892352c0ef3ce2b0d Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Wed, 23 Jul 2025 12:44:27 +0200 Subject: [PATCH] Add a note when a type implements a trait with the same name as the required one This is useful when you have two dependencies that use different trait for the same thing and with the same name. The user can accidentally implement the bad one which might be confusing. --- .../traits/fulfillment_errors.rs | 2 + .../src/error_reporting/traits/suggestions.rs | 38 +++++++++++++++++++ .../foo.stderr | 1 + .../multiple-dep-versions.stderr | 2 + tests/ui/traits/bound/same-crate-name.stderr | 2 + tests/ui/traits/similarly_named_trait.rs | 17 +++++++++ tests/ui/traits/similarly_named_trait.stderr | 23 +++++++++++ 7 files changed, 85 insertions(+) create mode 100644 tests/ui/traits/similarly_named_trait.rs create mode 100644 tests/ui/traits/similarly_named_trait.stderr 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 1ac309da101f1..d1fe95fc1a68a 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 @@ -487,6 +487,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } + self.suggest_impl_similarly_named_trait(&mut err, &obligation, leaf_trait_predicate); + self.try_to_add_help_message( &root_obligation, &obligation, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index c182fd99b17bd..60fe835ceea5f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5108,6 +5108,44 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => {} } } + pub(crate) fn suggest_impl_similarly_named_trait( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, + ) { + let trait_def_id = trait_predicate.def_id(); + 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) + && self + .tcx + .all_impls(*def_id) + .find(|impl_def_id| { + let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { + return false; + }; + self.predicate_must_hold_modulo_regions(&Obligation::new( + self.tcx, + obligation.cause.clone(), + obligation.param_env, + trait_ref + .skip_binder() + .with_self_ty(self.tcx, trait_predicate.skip_binder().self_ty()), + )) + }) + .is_some() + }) { + err.note(format!( + "`{}` implements similarly named `{}`, but not `{}`", + trait_predicate.self_ty(), + self.tcx.def_path_str(other_trait_def_id), + trait_predicate.print_modifiers_and_trait_path() + )); + } + () + } } /// Add a hint to add a missing borrow or remove an unnecessary one. diff --git a/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr b/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr index 9433a0ba00e80..989a046675aff 100644 --- a/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr +++ b/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr @@ -4,6 +4,7 @@ error[E0277]: the trait bound `foo::Struct: Trait` is not satisfied 13 | check_trait::(); | ^^^^^^^^^^^ the trait `Trait` is not implemented for `foo::Struct` | + = note: `foo::Struct` implements similarly named `foo::Trait`, but not `Trait` note: there are multiple different versions of crate `foo` in the dependency graph --> foo-current.rs:7:1 | diff --git a/tests/run-make/crate-loading/multiple-dep-versions.stderr b/tests/run-make/crate-loading/multiple-dep-versions.stderr index dea08bb96c979..ddfbf16a586c0 100644 --- a/tests/run-make/crate-loading/multiple-dep-versions.stderr +++ b/tests/run-make/crate-loading/multiple-dep-versions.stderr @@ -6,6 +6,7 @@ LL | do_something(Type); | | | required by a bound introduced by this call | + = note: `dep_2_reexport::Type` implements similarly named `dependency::Trait`, but not `Trait` note: there are multiple different versions of crate `dependency` in the dependency graph --> replaced | @@ -92,6 +93,7 @@ LL | do_something(OtherType); | | | required by a bound introduced by this call | + = note: `OtherType` implements similarly named `dependency::Trait`, but not `Trait` note: there are multiple different versions of crate `dependency` in the dependency graph --> replaced | diff --git a/tests/ui/traits/bound/same-crate-name.stderr b/tests/ui/traits/bound/same-crate-name.stderr index 71a8159fd8906..db293612ce711 100644 --- a/tests/ui/traits/bound/same-crate-name.stderr +++ b/tests/ui/traits/bound/same-crate-name.stderr @@ -12,6 +12,7 @@ help: trait impl with same name found LL | impl Bar for Foo {} | ^^^^^^^^^^^^^^^^ = note: perhaps two different versions of crate `crate_a2` are being used? + = note: `Foo` implements similarly named `main::a::Bar`, but not `main::a::Bar` = help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` note: required by a bound in `try_foo` --> $DIR/auxiliary/crate_a1.rs:3:24 @@ -48,6 +49,7 @@ help: trait impl with same name found LL | impl Bar for ImplementsWrongTraitConditionally {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: perhaps two different versions of crate `crate_a2` are being used? + = note: `ImplementsWrongTraitConditionally` implements similarly named `main::a::Bar`, but not `main::a::Bar` = help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` note: required by a bound in `try_foo` --> $DIR/auxiliary/crate_a1.rs:3:24 diff --git a/tests/ui/traits/similarly_named_trait.rs b/tests/ui/traits/similarly_named_trait.rs new file mode 100644 index 0000000000000..6d5882740bc71 --- /dev/null +++ b/tests/ui/traits/similarly_named_trait.rs @@ -0,0 +1,17 @@ +trait Trait {} //~ HELP this trait has no implementations, consider adding one + +mod m { + pub trait Trait {} + pub struct St; + impl Trait for St {} +} + +fn func(_: T) {} //~ NOTE required by a bound in `func` +//~^ NOTE required by this bound in `func` + +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` +} diff --git a/tests/ui/traits/similarly_named_trait.stderr b/tests/ui/traits/similarly_named_trait.stderr new file mode 100644 index 0000000000000..27c55da77dc10 --- /dev/null +++ b/tests/ui/traits/similarly_named_trait.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `St: Trait` is not satisfied + --> $DIR/similarly_named_trait.rs:13: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:9:12 + | +LL | fn func(_: T) {} + | ^^^^^ required by this bound in `func` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.