Skip to content

Add a diagnostic for similarly named traits #144674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Comment on lines +5133 to +5135
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skip_binder is pretty much never correct:

https://rustc-dev-guide.rust-lang.org/ty_module/early_binder.html?highlight=Binder#earlybinder-and-instantiating-parameters

also see https://rustc-dev-guide.rust-lang.org/typing_parameter_envs.html

U will also need to properly handle the generic parameters of the traits here, e.g. what if we've got trait Trait {} and a separate trait Trait<T> {}

Also, check predicate must hold for the other trait, not per impl.

Copy link
Contributor Author

@rperier rperier Aug 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhhhh, I see. I have to document myself about this part, thanks for the links.

Also, check predicate must hold for the other trait, not per impl.

Would you have a usecase for this ? You're probably right, but I don't get it.

thanks !

))
})
.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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ error[E0277]: the trait bound `foo::Struct: Trait` is not satisfied
13 | check_trait::<foo::Struct>();
| ^^^^^^^^^^^ 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
|
Expand Down
2 changes: 2 additions & 0 deletions tests/run-make/crate-loading/multiple-dep-versions.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand Down Expand Up @@ -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
|
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/traits/bound/same-crate-name.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize>`
note: required by a bound in `try_foo`
--> $DIR/auxiliary/crate_a1.rs:3:24
Expand Down Expand Up @@ -48,6 +49,7 @@ help: trait impl with same name found
LL | impl Bar for ImplementsWrongTraitConditionally<isize> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: perhaps two different versions of crate `crate_a2` are being used?
= note: `ImplementsWrongTraitConditionally<isize>` implements similarly named `main::a::Bar`, but not `main::a::Bar`
= help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
note: required by a bound in `try_foo`
--> $DIR/auxiliary/crate_a1.rs:3:24
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/traits/similarly_named_trait.rs
Original file line number Diff line number Diff line change
@@ -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: Trait>(_: 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`
}
23 changes: 23 additions & 0 deletions tests/ui/traits/similarly_named_trait.stderr
Original file line number Diff line number Diff line change
@@ -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: Trait>(_: T) {}
| ^^^^^ required by this bound in `func`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Loading