Skip to content

Commit db2569b

Browse files
rperierRomain Perier
authored andcommitted
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 a1dbb44 commit db2569b

File tree

4 files changed

+155
-3
lines changed

4 files changed

+155
-3
lines changed

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

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
460460
span,
461461
leaf_trait_predicate,
462462
);
463-
self.note_version_mismatch(&mut err, leaf_trait_predicate);
463+
self.note_different_trait_with_same_name(&mut err, &obligation, leaf_trait_predicate);
464464
self.suggest_remove_await(&obligation, &mut err);
465465
self.suggest_derive(&obligation, &mut err, leaf_trait_predicate);
466466

@@ -2354,10 +2354,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
23542354

23552355
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
23562356
/// with the same path as `trait_ref`, a help message about
2357-
/// a probable version mismatch is added to `err`
2358-
fn note_version_mismatch(
2357+
/// a probable version mismatch is added to `err`. Otherwise if it implements
2358+
/// another trait with the same name, a note message about a similarly named
2359+
/// trait is added to `err`.
2360+
fn note_different_trait_with_same_name(
23592361
&self,
23602362
err: &mut Diag<'_>,
2363+
obligation: &PredicateObligation<'tcx>,
23612364
trait_pred: ty::PolyTraitPredicate<'tcx>,
23622365
) -> bool {
23632366
let get_trait_impls = |trait_def_id| {
@@ -2384,6 +2387,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
23842387
let traits_with_same_path =
23852388
traits_with_same_path.into_items().into_sorted_stable_ord_by_key(|(p, _)| p);
23862389
let mut suggested = false;
2390+
23872391
for (_, trait_with_same_path) in traits_with_same_path {
23882392
let trait_impls = get_trait_impls(trait_with_same_path);
23892393
if trait_impls.is_empty() {
@@ -2401,6 +2405,72 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
24012405
err.note(crate_msg);
24022406
suggested = true;
24032407
}
2408+
2409+
if suggested {
2410+
return true;
2411+
}
2412+
2413+
let trait_def_id = trait_pred.def_id();
2414+
let trait_name = self.tcx.item_name(trait_def_id);
2415+
let trait_krate_name = self.tcx.crate_name(trait_def_id.krate);
2416+
2417+
// FIXME: If there are multiple different versions of a crate in the dependency graph, there is already
2418+
// a suggestion designed for this purpose, see FnCtxt::detect_and_explain_multiple_crate_versions_of_trait_item.
2419+
// We should merge them all into this one. We should not have 2 subtly different impls of this note.
2420+
if self
2421+
.tcx
2422+
.all_traits_including_private()
2423+
.find(|def_id| {
2424+
def_id.krate != trait_def_id.krate
2425+
&& self.tcx.crate_name(def_id.krate) == trait_krate_name
2426+
&& self.tcx.item_name(def_id) == trait_name
2427+
})
2428+
.is_some()
2429+
{
2430+
return false;
2431+
}
2432+
2433+
let trait_has_same_params = |other_trait_def_id: DefId| -> bool {
2434+
let trait_generics = self.tcx.generics_of(trait_def_id);
2435+
let other_trait_generics = self.tcx.generics_of(other_trait_def_id);
2436+
2437+
if trait_generics.count() != other_trait_generics.count() {
2438+
return false;
2439+
}
2440+
trait_generics.own_params.iter().zip(other_trait_generics.own_params.iter()).all(
2441+
|(a, b)| {
2442+
(matches!(a.kind, ty::GenericParamDefKind::Type { .. })
2443+
&& matches!(b.kind, ty::GenericParamDefKind::Type { .. }))
2444+
|| (matches!(a.kind, ty::GenericParamDefKind::Lifetime,)
2445+
&& matches!(b.kind, ty::GenericParamDefKind::Lifetime))
2446+
|| (matches!(a.kind, ty::GenericParamDefKind::Const { .. })
2447+
&& matches!(b.kind, ty::GenericParamDefKind::Const { .. }))
2448+
},
2449+
)
2450+
};
2451+
let trait_name = self.tcx.item_name(trait_def_id);
2452+
if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| {
2453+
trait_def_id != *def_id
2454+
&& trait_name == self.tcx.item_name(def_id)
2455+
&& trait_has_same_params(*def_id)
2456+
&& self.predicate_must_hold_modulo_regions(&Obligation::new(
2457+
self.tcx,
2458+
obligation.cause.clone(),
2459+
obligation.param_env,
2460+
trait_pred.map_bound(|tr| ty::TraitPredicate {
2461+
trait_ref: ty::TraitRef::new(self.tcx, *def_id, tr.trait_ref.args),
2462+
..tr
2463+
}),
2464+
))
2465+
}) {
2466+
err.note(format!(
2467+
"`{}` implements similarly named `{}`, but not `{}`",
2468+
trait_pred.self_ty(),
2469+
self.tcx.def_path_str(other_trait_def_id),
2470+
trait_pred.print_modifiers_and_trait_path()
2471+
));
2472+
suggested = true;
2473+
}
24042474
suggested
24052475
}
24062476

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