Skip to content

Commit 434cb43

Browse files
committed
Auto merge of #86943 - ptrojahn:suggest_derive, r=estebank
Suggest deriving traits if possible This only applies to builtin derives as I don't think there is a clean way to get the available derives in typeck. Closes #85851
2 parents 72969f6 + 50e5f90 commit 434cb43

11 files changed

+235
-0
lines changed

compiler/rustc_typeck/src/check/method/suggest.rs

+81
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
829829
err.note(&format!(
830830
"the following trait bounds were not satisfied:\n{bound_list}"
831831
));
832+
self.suggest_derive(&mut err, &unsatisfied_predicates);
833+
832834
unsatisfied_bounds = true;
833835
}
834836
}
@@ -971,6 +973,85 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
971973
None
972974
}
973975

976+
fn suggest_derive(
977+
&self,
978+
err: &mut DiagnosticBuilder<'_>,
979+
unsatisfied_predicates: &Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
980+
) {
981+
let derivables = [
982+
sym::Eq,
983+
sym::PartialEq,
984+
sym::Ord,
985+
sym::PartialOrd,
986+
sym::Clone,
987+
sym::Copy,
988+
sym::Hash,
989+
sym::Default,
990+
sym::debug_trait,
991+
];
992+
let mut derives = unsatisfied_predicates
993+
.iter()
994+
.filter_map(|(pred, _)| {
995+
let trait_pred =
996+
if let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() {
997+
trait_pred
998+
} else {
999+
return None;
1000+
};
1001+
let trait_ref = trait_pred.trait_ref;
1002+
let adt_def = if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() {
1003+
adt_def
1004+
} else {
1005+
return None;
1006+
};
1007+
if adt_def.did.is_local() {
1008+
let diagnostic_items = self.tcx.diagnostic_items(trait_ref.def_id.krate);
1009+
return derivables.iter().find_map(|trait_derivable| {
1010+
let item_def_id =
1011+
if let Some(item_def_id) = diagnostic_items.get(trait_derivable) {
1012+
item_def_id
1013+
} else {
1014+
return None;
1015+
};
1016+
if item_def_id == &trait_pred.trait_ref.def_id
1017+
&& !(adt_def.is_enum() && *trait_derivable == sym::Default)
1018+
{
1019+
return Some((
1020+
format!("{}", trait_ref.self_ty()),
1021+
self.tcx.def_span(adt_def.did),
1022+
format!("{}", trait_ref.print_only_trait_path()),
1023+
));
1024+
}
1025+
None
1026+
});
1027+
}
1028+
None
1029+
})
1030+
.collect::<Vec<(String, Span, String)>>();
1031+
derives.sort();
1032+
let derives_grouped = derives.into_iter().fold(
1033+
Vec::<(String, Span, String)>::new(),
1034+
|mut acc, (self_name, self_span, trait_name)| {
1035+
if let Some((acc_self_name, _, ref mut traits)) = acc.last_mut() {
1036+
if acc_self_name == &self_name {
1037+
traits.push_str(format!(", {}", trait_name).as_str());
1038+
return acc;
1039+
}
1040+
}
1041+
acc.push((self_name, self_span, trait_name));
1042+
acc
1043+
},
1044+
);
1045+
for (self_name, self_span, traits) in &derives_grouped {
1046+
err.span_suggestion_verbose(
1047+
self_span.shrink_to_lo(),
1048+
&format!("consider annotating `{}` with `#[derive({})]`", self_name, traits),
1049+
format!("#[derive({})]\n", traits),
1050+
Applicability::MaybeIncorrect,
1051+
);
1052+
}
1053+
}
1054+
9741055
/// Print out the type for use in value namespace.
9751056
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
9761057
match ty.kind() {

library/core/src/cmp.rs

+4
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ use self::Ordering::*;
203203
message = "can't compare `{Self}` with `{Rhs}`",
204204
label = "no implementation for `{Self} == {Rhs}`"
205205
)]
206+
#[rustc_diagnostic_item = "PartialEq"]
206207
pub trait PartialEq<Rhs: ?Sized = Self> {
207208
/// This method tests for `self` and `other` values to be equal, and is used
208209
/// by `==`.
@@ -269,6 +270,7 @@ pub macro PartialEq($item:item) {
269270
#[doc(alias = "==")]
270271
#[doc(alias = "!=")]
271272
#[stable(feature = "rust1", since = "1.0.0")]
273+
#[rustc_diagnostic_item = "Eq"]
272274
pub trait Eq: PartialEq<Self> {
273275
// this method is used solely by #[deriving] to assert
274276
// that every component of a type implements #[deriving]
@@ -728,6 +730,7 @@ impl<T: Clone> Clone for Reverse<T> {
728730
#[doc(alias = "<=")]
729731
#[doc(alias = ">=")]
730732
#[stable(feature = "rust1", since = "1.0.0")]
733+
#[rustc_diagnostic_item = "Ord"]
731734
pub trait Ord: Eq + PartialOrd<Self> {
732735
/// This method returns an [`Ordering`] between `self` and `other`.
733736
///
@@ -984,6 +987,7 @@ impl PartialOrd for Ordering {
984987
message = "can't compare `{Self}` with `{Rhs}`",
985988
label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`"
986989
)]
990+
#[rustc_diagnostic_item = "PartialOrd"]
987991
pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
988992
/// This method returns an ordering between `self` and `other` values if one exists.
989993
///

library/core/src/hash/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ mod sip;
157157
/// [`HashSet`]: ../../std/collections/struct.HashSet.html
158158
/// [`hash`]: Hash::hash
159159
#[stable(feature = "rust1", since = "1.0.0")]
160+
#[rustc_diagnostic_item = "Hash"]
160161
pub trait Hash {
161162
/// Feeds this value into the given [`Hasher`].
162163
///

library/core/src/marker.rs

+1
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ pub trait StructuralEq {
382382
// existing specializations on `Copy` that already exist in the standard
383383
// library, and there's no way to safely have this behavior right now.
384384
#[rustc_unsafe_specialization_marker]
385+
#[rustc_diagnostic_item = "Copy"]
385386
pub trait Copy: Clone {
386387
// Empty.
387388
}

src/test/ui/derives/derive-assoc-type-not-impl.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ LL | Bar::<NotClone> { x: 1 }.clone();
1919
= help: items from traits can only be used if the trait is implemented and in scope
2020
= note: the following trait defines an item `clone`, perhaps you need to implement it:
2121
candidate #1: `Clone`
22+
help: consider annotating `NotClone` with `#[derive(Clone)]`
23+
|
24+
LL | #[derive(Clone)]
25+
|
2226

2327
error: aborting due to previous error
2428

src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ LL | a.unwrap();
99
|
1010
= note: the following trait bounds were not satisfied:
1111
`Foo: Debug`
12+
help: consider annotating `Foo` with `#[derive(Debug)]`
13+
|
14+
LL | #[derive(Debug)]
15+
|
1216

1317
error: aborting due to previous error
1418

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use std::time::Instant;
2+
3+
enum Enum {
4+
First
5+
}
6+
7+
#[derive(Clone)]
8+
enum CloneEnum {
9+
First
10+
}
11+
12+
struct Struct {
13+
}
14+
15+
#[derive(Clone)]
16+
struct CloneStruct {
17+
}
18+
19+
struct Foo<X, Y> (X, Y);
20+
impl<X: Clone + Default + , Y: Clone + Default> Foo<X, Y> {
21+
fn test(&self) -> (X, Y) {
22+
(self.0, self.1)
23+
}
24+
}
25+
26+
fn test1() {
27+
let x = Foo(Enum::First, CloneEnum::First);
28+
let y = x.test();
29+
//~^the method `test` exists for struct `Foo<Enum, CloneEnum>`, but its trait bounds were not satisfied [E0599]
30+
}
31+
32+
fn test2() {
33+
let x = Foo(Struct{}, CloneStruct{});
34+
let y = x.test();
35+
//~^the method `test` exists for struct `Foo<Struct, CloneStruct>`, but its trait bounds were not satisfied [E0599]
36+
}
37+
38+
fn test3() {
39+
let x = Foo(Vec::<Enum>::new(), Instant::now());
40+
let y = x.test();
41+
//~^the method `test` exists for struct `Foo<Vec<Enum>, Instant>`, but its trait bounds were not satisfied [E0599]
42+
}
43+
44+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
error[E0599]: the method `test` exists for struct `Foo<Enum, CloneEnum>`, but its trait bounds were not satisfied
2+
--> $DIR/derive-trait-for-method-call.rs:28:15
3+
|
4+
LL | enum Enum {
5+
| ---------
6+
| |
7+
| doesn't satisfy `Enum: Clone`
8+
| doesn't satisfy `Enum: Default`
9+
...
10+
LL | enum CloneEnum {
11+
| -------------- doesn't satisfy `CloneEnum: Default`
12+
...
13+
LL | struct Foo<X, Y> (X, Y);
14+
| ------------------------ method `test` not found for this
15+
...
16+
LL | let y = x.test();
17+
| ^^^^ method cannot be called on `Foo<Enum, CloneEnum>` due to unsatisfied trait bounds
18+
|
19+
= note: the following trait bounds were not satisfied:
20+
`Enum: Clone`
21+
`Enum: Default`
22+
`CloneEnum: Default`
23+
help: consider annotating `Enum` with `#[derive(Clone)]`
24+
|
25+
LL | #[derive(Clone)]
26+
|
27+
28+
error[E0599]: the method `test` exists for struct `Foo<Struct, CloneStruct>`, but its trait bounds were not satisfied
29+
--> $DIR/derive-trait-for-method-call.rs:34:15
30+
|
31+
LL | struct Struct {
32+
| -------------
33+
| |
34+
| doesn't satisfy `Struct: Clone`
35+
| doesn't satisfy `Struct: Default`
36+
...
37+
LL | struct CloneStruct {
38+
| ------------------ doesn't satisfy `CloneStruct: Default`
39+
...
40+
LL | struct Foo<X, Y> (X, Y);
41+
| ------------------------ method `test` not found for this
42+
...
43+
LL | let y = x.test();
44+
| ^^^^ method cannot be called on `Foo<Struct, CloneStruct>` due to unsatisfied trait bounds
45+
|
46+
= note: the following trait bounds were not satisfied:
47+
`Struct: Clone`
48+
`Struct: Default`
49+
`CloneStruct: Default`
50+
help: consider annotating `CloneStruct` with `#[derive(Default)]`
51+
|
52+
LL | #[derive(Default)]
53+
|
54+
help: consider annotating `Struct` with `#[derive(Clone, Default)]`
55+
|
56+
LL | #[derive(Clone, Default)]
57+
|
58+
59+
error[E0599]: the method `test` exists for struct `Foo<Vec<Enum>, Instant>`, but its trait bounds were not satisfied
60+
--> $DIR/derive-trait-for-method-call.rs:40:15
61+
|
62+
LL | struct Foo<X, Y> (X, Y);
63+
| ------------------------ method `test` not found for this
64+
...
65+
LL | let y = x.test();
66+
| ^^^^ method cannot be called on `Foo<Vec<Enum>, Instant>` due to unsatisfied trait bounds
67+
|
68+
::: $SRC_DIR/std/src/time.rs:LL:COL
69+
|
70+
LL | pub struct Instant(time::Instant);
71+
| ---------------------------------- doesn't satisfy `Instant: Default`
72+
|
73+
::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
74+
|
75+
LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
76+
| ------------------------------------------------------------------------------------------------ doesn't satisfy `Vec<Enum>: Clone`
77+
|
78+
= note: the following trait bounds were not satisfied:
79+
`Vec<Enum>: Clone`
80+
`Instant: Default`
81+
82+
error: aborting due to 3 previous errors
83+
84+
For more information about this error, try `rustc --explain E0599`.

src/test/ui/union/union-derive-clone.mirunsafeck.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ LL | let w = u.clone();
1616
= note: the following trait bounds were not satisfied:
1717
`CloneNoCopy: Copy`
1818
which is required by `U5<CloneNoCopy>: Clone`
19+
help: consider annotating `CloneNoCopy` with `#[derive(Copy)]`
20+
|
21+
LL | #[derive(Copy)]
22+
|
1923

2024
error[E0277]: the trait bound `U1: Copy` is not satisfied
2125
--> $DIR/union-derive-clone.rs:6:10

src/test/ui/union/union-derive-clone.thirunsafeck.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ LL | let w = u.clone();
1616
= note: the following trait bounds were not satisfied:
1717
`CloneNoCopy: Copy`
1818
which is required by `U5<CloneNoCopy>: Clone`
19+
help: consider annotating `CloneNoCopy` with `#[derive(Copy)]`
20+
|
21+
LL | #[derive(Copy)]
22+
|
1923

2024
error[E0277]: the trait bound `U1: Copy` is not satisfied
2125
--> $DIR/union-derive-clone.rs:6:10

src/test/ui/unique-pinned-nocopy.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ LL | | >(Unique<T>, A);
2121
= help: items from traits can only be used if the trait is implemented and in scope
2222
= note: the following trait defines an item `clone`, perhaps you need to implement it:
2323
candidate #1: `Clone`
24+
help: consider annotating `R` with `#[derive(Clone)]`
25+
|
26+
LL | #[derive(Clone)]
27+
|
2428

2529
error: aborting due to previous error
2630

0 commit comments

Comments
 (0)