Skip to content

Commit d513dc2

Browse files
committed
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.
1 parent 831e291 commit d513dc2

File tree

4 files changed

+139
-1
lines changed

4 files changed

+139
-1
lines changed

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
441441
span,
442442
leaf_trait_predicate,
443443
);
444-
self.note_version_mismatch(&mut err, leaf_trait_predicate);
444+
self.note_version_mismatch(&mut err, &obligation, leaf_trait_predicate);
445445
self.suggest_remove_await(&obligation, &mut err);
446446
self.suggest_derive(&obligation, &mut err, leaf_trait_predicate);
447447

@@ -2337,6 +2337,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
23372337
fn note_version_mismatch(
23382338
&self,
23392339
err: &mut Diag<'_>,
2340+
obligation: &PredicateObligation<'tcx>,
23402341
trait_pred: ty::PolyTraitPredicate<'tcx>,
23412342
) -> bool {
23422343
let get_trait_impls = |trait_def_id| {
@@ -2363,6 +2364,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
23632364
let traits_with_same_path =
23642365
traits_with_same_path.into_items().into_sorted_stable_ord_by_key(|(p, _)| p);
23652366
let mut suggested = false;
2367+
23662368
for (_, trait_with_same_path) in traits_with_same_path {
23672369
let trait_impls = get_trait_impls(trait_with_same_path);
23682370
if trait_impls.is_empty() {
@@ -2380,6 +2382,71 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
23802382
err.note(crate_msg);
23812383
suggested = true;
23822384
}
2385+
2386+
if suggested {
2387+
return true;
2388+
}
2389+
2390+
let trait_def_id = trait_pred.def_id();
2391+
let trait_name = self.tcx.item_name(trait_def_id);
2392+
let trait_krate_name = self.tcx.crate_name(trait_def_id.krate);
2393+
2394+
// If there are multiple different versions of a crate in the dependency graph, there is already
2395+
// a suggestion designed for this purpose in the rustc_hir_typeck compiler crate
2396+
if self
2397+
.tcx
2398+
.all_traits_including_private()
2399+
.find(|def_id| {
2400+
def_id.krate != trait_def_id.krate
2401+
&& self.tcx.crate_name(def_id.krate) == trait_krate_name
2402+
&& self.tcx.item_name(def_id) == trait_name
2403+
})
2404+
.is_some()
2405+
{
2406+
return false;
2407+
}
2408+
2409+
let trait_has_same_params = |other_trait_def_id: DefId| -> bool {
2410+
let trait_generics = self.tcx.generics_of(trait_def_id);
2411+
let other_trait_generics = self.tcx.generics_of(other_trait_def_id);
2412+
2413+
if trait_generics.count() != other_trait_generics.count() {
2414+
return false;
2415+
}
2416+
trait_generics.own_params.iter().zip(other_trait_generics.own_params.iter()).all(
2417+
|(a, b)| {
2418+
(matches!(a.kind, ty::GenericParamDefKind::Type { .. })
2419+
&& matches!(b.kind, ty::GenericParamDefKind::Type { .. }))
2420+
|| (matches!(a.kind, ty::GenericParamDefKind::Lifetime,)
2421+
&& matches!(b.kind, ty::GenericParamDefKind::Lifetime))
2422+
|| (matches!(a.kind, ty::GenericParamDefKind::Const { .. })
2423+
&& matches!(b.kind, ty::GenericParamDefKind::Const { .. }))
2424+
},
2425+
)
2426+
};
2427+
let trait_name = self.tcx.item_name(trait_def_id);
2428+
if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| {
2429+
trait_def_id != *def_id
2430+
&& trait_name == self.tcx.item_name(def_id)
2431+
&& trait_has_same_params(*def_id)
2432+
&& self.predicate_must_hold_modulo_regions(&Obligation::new(
2433+
self.tcx,
2434+
obligation.cause.clone(),
2435+
obligation.param_env,
2436+
trait_pred.map_bound(|tr| ty::TraitPredicate {
2437+
trait_ref: ty::TraitRef::new(self.tcx, *def_id, tr.trait_ref.args),
2438+
..tr
2439+
}),
2440+
))
2441+
}) {
2442+
err.note(format!(
2443+
"`{}` implements similarly named `{}`, but not `{}`",
2444+
trait_pred.self_ty(),
2445+
self.tcx.def_path_str(other_trait_def_id),
2446+
trait_pred.print_modifiers_and_trait_path()
2447+
));
2448+
suggested = true;
2449+
}
23832450
suggested
23842451
}
23852452

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
trait Trait {} //~ HELP this trait has no implementations, consider adding one
2+
trait TraitWithParam<T> {} //~ HELP this trait has no implementations, consider adding one
3+
4+
mod m {
5+
pub trait Trait {}
6+
pub trait TraitWithParam<T> {}
7+
pub struct St;
8+
impl Trait for St {}
9+
impl<T> TraitWithParam<T> for St {}
10+
}
11+
12+
fn func<T: Trait>(_: T) {} //~ NOTE required by a bound in `func`
13+
//~^ NOTE required by this bound in `func`
14+
15+
fn func2<T: TraitWithParam<T>> (_: T) {} //~ NOTE required by a bound in `func2`
16+
//~^ NOTE required by this bound in `func2`
17+
18+
fn main() {
19+
func(m::St); //~ ERROR the trait bound `St: Trait` is not satisfied
20+
//~^ NOTE the trait `Trait` is not implemented for `St`
21+
//~| NOTE required by a bound introduced by this call
22+
//~| NOTE `St` implements similarly named `m::Trait`, but not `Trait`
23+
func2(m::St); //~ ERROR the trait bound `St: TraitWithParam<St>` is not satisfied
24+
//~^ NOTE the trait `TraitWithParam<St>` is not implemented for `St`
25+
//~| NOTE required by a bound introduced by this call
26+
//~| NOTE `St` implements similarly named `m::TraitWithParam`, but not `TraitWithParam<St>`
27+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error[E0277]: the trait bound `St: Trait` is not satisfied
2+
--> $DIR/similarly_named_trait.rs:19:10
3+
|
4+
LL | func(m::St);
5+
| ---- ^^^^^ the trait `Trait` is not implemented for `St`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= note: `St` implements similarly named `m::Trait`, but not `Trait`
10+
help: this trait has no implementations, consider adding one
11+
--> $DIR/similarly_named_trait.rs:1:1
12+
|
13+
LL | trait Trait {}
14+
| ^^^^^^^^^^^
15+
note: required by a bound in `func`
16+
--> $DIR/similarly_named_trait.rs:12:12
17+
|
18+
LL | fn func<T: Trait>(_: T) {}
19+
| ^^^^^ required by this bound in `func`
20+
21+
error[E0277]: the trait bound `St: TraitWithParam<St>` is not satisfied
22+
--> $DIR/similarly_named_trait.rs:23:11
23+
|
24+
LL | func2(m::St);
25+
| ----- ^^^^^ the trait `TraitWithParam<St>` is not implemented for `St`
26+
| |
27+
| required by a bound introduced by this call
28+
|
29+
= note: `St` implements similarly named `m::TraitWithParam`, but not `TraitWithParam<St>`
30+
help: this trait has no implementations, consider adding one
31+
--> $DIR/similarly_named_trait.rs:2:1
32+
|
33+
LL | trait TraitWithParam<T> {}
34+
| ^^^^^^^^^^^^^^^^^^^^^^^
35+
note: required by a bound in `func2`
36+
--> $DIR/similarly_named_trait.rs:15:13
37+
|
38+
LL | fn func2<T: TraitWithParam<T>> (_: T) {}
39+
| ^^^^^^^^^^^^^^^^^ required by this bound in `func2`
40+
41+
error: aborting due to 2 previous errors
42+
43+
For more information about this error, try `rustc --explain E0277`.

tests/ui/union/issue-81199.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ error[E0277]: the trait bound `T: Pointee` is not satisfied
1616
LL | components: PtrComponents<T>,
1717
| ^^^^^^^^^^^^^^^^ the trait `Pointee` is not implemented for `T`
1818
|
19+
= note: `T` implements similarly named `std::ptr::Pointee`, but not `Pointee`
1920
note: required by a bound in `PtrComponents`
2021
--> $DIR/issue-81199.rs:11:25
2122
|

0 commit comments

Comments
 (0)