Skip to content

Commit 532c561

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 5771665 commit 532c561

File tree

8 files changed

+145
-0
lines changed

8 files changed

+145
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
498498
);
499499
}
500500

501+
self.suggest_impl_similarly_named_trait(&mut err, &obligation, leaf_trait_predicate);
502+
501503
self.try_to_add_help_message(
502504
&root_obligation,
503505
&obligation,

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5127,6 +5127,73 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
51275127
_ => {}
51285128
}
51295129
}
5130+
pub(crate) fn suggest_impl_similarly_named_trait(
5131+
&self,
5132+
err: &mut Diag<'_>,
5133+
obligation: &PredicateObligation<'tcx>,
5134+
trait_predicate: ty::PolyTraitPredicate<'tcx>,
5135+
) {
5136+
let trait_def_id = trait_predicate.def_id();
5137+
let trait_name = self.tcx.item_name(trait_def_id);
5138+
5139+
let trait_has_same_params = |other_trait_def_id: DefId| -> bool {
5140+
let trait_generics = self.tcx.generics_of(trait_def_id);
5141+
let other_trait_generics = self.tcx.generics_of(other_trait_def_id);
5142+
5143+
if trait_generics.count() != other_trait_generics.count() {
5144+
return false;
5145+
}
5146+
trait_generics.own_params.iter().zip(other_trait_generics.own_params.iter()).all(
5147+
|(a, b)| {
5148+
a.name == b.name
5149+
&& a.index == b.index
5150+
&& ((matches!(a.kind, ty::GenericParamDefKind::Type { .. })
5151+
&& matches!(b.kind, ty::GenericParamDefKind::Type { .. }))
5152+
|| (matches!(a.kind, ty::GenericParamDefKind::Lifetime,)
5153+
&& matches!(b.kind, ty::GenericParamDefKind::Lifetime))
5154+
|| (matches!(a.kind, ty::GenericParamDefKind::Const { .. })
5155+
&& matches!(b.kind, ty::GenericParamDefKind::Const { .. })))
5156+
},
5157+
)
5158+
};
5159+
5160+
if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| {
5161+
trait_def_id != *def_id
5162+
&& trait_name == self.tcx.item_name(def_id)
5163+
&& trait_has_same_params(*def_id)
5164+
&& self
5165+
.tcx
5166+
.predicates_of(*def_id)
5167+
.instantiate(self.tcx, trait_predicate.skip_binder().trait_ref.args)
5168+
.predicates
5169+
.iter()
5170+
.find(|clause| {
5171+
if clause
5172+
.as_trait_clause()
5173+
.map_or(false, |trait_clause| trait_clause.def_id() == *def_id)
5174+
== false
5175+
{
5176+
return false;
5177+
}
5178+
let pred = clause.as_trait_clause().unwrap();
5179+
self.predicate_must_hold_modulo_regions(&Obligation::new(
5180+
self.tcx,
5181+
obligation.cause.clone(),
5182+
obligation.param_env,
5183+
pred,
5184+
))
5185+
})
5186+
.is_some()
5187+
}) {
5188+
err.note(format!(
5189+
"`{}` implements similarly named `{}`, but not `{}`",
5190+
trait_predicate.self_ty(),
5191+
self.tcx.def_path_str(other_trait_def_id),
5192+
trait_predicate.print_modifiers_and_trait_path()
5193+
));
5194+
}
5195+
()
5196+
}
51305197
}
51315198

51325199
/// Add a hint to add a missing borrow or remove an unnecessary one.

tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `foo::Struct: Trait` is not satisfied
44
13 | check_trait::<foo::Struct>();
55
| ^^^^^^^^^^^ the trait `Trait` is not implemented for `foo::Struct`
66
|
7+
= note: `foo::Struct` implements similarly named `foo::Trait`, but not `Trait`
78
note: there are multiple different versions of crate `foo` in the dependency graph
89
--> foo-current.rs:7:1
910
|

tests/run-make/crate-loading/multiple-dep-versions.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ LL | do_something(Type);
66
| |
77
| required by a bound introduced by this call
88
|
9+
= note: `dep_2_reexport::Type` implements similarly named `dependency::Trait`, but not `Trait`
910
note: there are multiple different versions of crate `dependency` in the dependency graph
1011
--> replaced
1112
|
@@ -92,6 +93,7 @@ LL | do_something(OtherType);
9293
| |
9394
| required by a bound introduced by this call
9495
|
96+
= note: `OtherType` implements similarly named `dependency::Trait`, but not `Trait`
9597
note: there are multiple different versions of crate `dependency` in the dependency graph
9698
--> replaced
9799
|

tests/ui/traits/bound/same-crate-name.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ help: trait impl with same name found
1212
LL | impl Bar for Foo {}
1313
| ^^^^^^^^^^^^^^^^
1414
= note: perhaps two different versions of crate `crate_a2` are being used?
15+
= note: `Foo` implements similarly named `main::a::Bar`, but not `main::a::Bar`
1516
= help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
1617
note: required by a bound in `try_foo`
1718
--> $DIR/auxiliary/crate_a1.rs:3:24
@@ -48,6 +49,7 @@ help: trait impl with same name found
4849
LL | impl Bar for ImplementsWrongTraitConditionally<isize> {}
4950
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5051
= note: perhaps two different versions of crate `crate_a2` are being used?
52+
= note: `ImplementsWrongTraitConditionally<isize>` implements similarly named `main::a::Bar`, but not `main::a::Bar`
5153
= help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
5254
note: required by a bound in `try_foo`
5355
--> $DIR/auxiliary/crate_a1.rs:3:24
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)