Skip to content
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

Suggest deriving traits if possible #86943

Merged
merged 1 commit into from
Sep 8, 2021
Merged
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
81 changes: 81 additions & 0 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.note(&format!(
"the following trait bounds were not satisfied:\n{bound_list}"
));
self.suggest_derive(&mut err, &unsatisfied_predicates);

unsatisfied_bounds = true;
}
}
Expand Down Expand Up @@ -971,6 +973,85 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None
}

fn suggest_derive(
&self,
err: &mut DiagnosticBuilder<'_>,
unsatisfied_predicates: &Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
) {
let derivables = [
sym::Eq,
sym::PartialEq,
sym::Ord,
sym::PartialOrd,
sym::Clone,
sym::Copy,
sym::Hash,
sym::Default,
sym::debug_trait,
];
let mut derives = unsatisfied_predicates
.iter()
.filter_map(|(pred, _)| {
let trait_pred =
if let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() {
trait_pred
} else {
return None;
};
let trait_ref = trait_pred.trait_ref;
let adt_def = if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() {
adt_def
} else {
return None;
};
if adt_def.did.is_local() {
let diagnostic_items = self.tcx.diagnostic_items(trait_ref.def_id.krate);
return derivables.iter().find_map(|trait_derivable| {
let item_def_id =
if let Some(item_def_id) = diagnostic_items.get(trait_derivable) {
item_def_id
} else {
return None;
};
if item_def_id == &trait_pred.trait_ref.def_id
&& !(adt_def.is_enum() && *trait_derivable == sym::Default)
{
return Some((
format!("{}", trait_ref.self_ty()),
self.tcx.def_span(adt_def.did),
format!("{}", trait_ref.print_only_trait_path()),
));
}
None
});
}
None
})
.collect::<Vec<(String, Span, String)>>();
derives.sort();
let derives_grouped = derives.into_iter().fold(
Vec::<(String, Span, String)>::new(),
|mut acc, (self_name, self_span, trait_name)| {
if let Some((acc_self_name, _, ref mut traits)) = acc.last_mut() {
if acc_self_name == &self_name {
traits.push_str(format!(", {}", trait_name).as_str());
return acc;
}
}
acc.push((self_name, self_span, trait_name));
acc
},
);
for (self_name, self_span, traits) in &derives_grouped {
err.span_suggestion_verbose(
self_span.shrink_to_lo(),
&format!("consider annotating `{}` with `#[derive({})]`", self_name, traits),
format!("#[derive({})]\n", traits),
Applicability::MaybeIncorrect,
);
}
}

/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
Expand Down
4 changes: 4 additions & 0 deletions library/core/src/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ use self::Ordering::*;
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} == {Rhs}`"
)]
#[rustc_diagnostic_item = "PartialEq"]
pub trait PartialEq<Rhs: ?Sized = Self> {
/// This method tests for `self` and `other` values to be equal, and is used
/// by `==`.
Expand Down Expand Up @@ -269,6 +270,7 @@ pub macro PartialEq($item:item) {
#[doc(alias = "==")]
#[doc(alias = "!=")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "Eq"]
pub trait Eq: PartialEq<Self> {
// this method is used solely by #[deriving] to assert
// that every component of a type implements #[deriving]
Expand Down Expand Up @@ -728,6 +730,7 @@ impl<T: Clone> Clone for Reverse<T> {
#[doc(alias = "<=")]
#[doc(alias = ">=")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "Ord"]
pub trait Ord: Eq + PartialOrd<Self> {
/// This method returns an [`Ordering`] between `self` and `other`.
///
Expand Down Expand Up @@ -984,6 +987,7 @@ impl PartialOrd for Ordering {
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`"
)]
#[rustc_diagnostic_item = "PartialOrd"]
pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
/// This method returns an ordering between `self` and `other` values if one exists.
///
Expand Down
1 change: 1 addition & 0 deletions library/core/src/hash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ mod sip;
/// [`HashSet`]: ../../std/collections/struct.HashSet.html
/// [`hash`]: Hash::hash
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "Hash"]
pub trait Hash {
/// Feeds this value into the given [`Hasher`].
///
Expand Down
1 change: 1 addition & 0 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ pub trait StructuralEq {
// existing specializations on `Copy` that already exist in the standard
// library, and there's no way to safely have this behavior right now.
#[rustc_unsafe_specialization_marker]
#[rustc_diagnostic_item = "Copy"]
pub trait Copy: Clone {
// Empty.
}
Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/derives/derive-assoc-type-not-impl.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ LL | Bar::<NotClone> { x: 1 }.clone();
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `NotClone` with `#[derive(Clone)]`
|
LL | #[derive(Clone)]
|

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ LL | a.unwrap();
|
= note: the following trait bounds were not satisfied:
`Foo: Debug`
help: consider annotating `Foo` with `#[derive(Debug)]`
|
LL | #[derive(Debug)]
|

error: aborting due to previous error

Expand Down
44 changes: 44 additions & 0 deletions src/test/ui/suggestions/derive-trait-for-method-call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::time::Instant;

enum Enum {
First
}

#[derive(Clone)]
enum CloneEnum {
First
}

struct Struct {
}

#[derive(Clone)]
struct CloneStruct {
}

struct Foo<X, Y> (X, Y);
impl<X: Clone + Default + , Y: Clone + Default> Foo<X, Y> {
fn test(&self) -> (X, Y) {
(self.0, self.1)
}
}

fn test1() {
let x = Foo(Enum::First, CloneEnum::First);
let y = x.test();
//~^the method `test` exists for struct `Foo<Enum, CloneEnum>`, but its trait bounds were not satisfied [E0599]
}

fn test2() {
let x = Foo(Struct{}, CloneStruct{});
let y = x.test();
//~^the method `test` exists for struct `Foo<Struct, CloneStruct>`, but its trait bounds were not satisfied [E0599]
}

fn test3() {
let x = Foo(Vec::<Enum>::new(), Instant::now());
let y = x.test();
//~^the method `test` exists for struct `Foo<Vec<Enum>, Instant>`, but its trait bounds were not satisfied [E0599]
}

fn main() {}
84 changes: 84 additions & 0 deletions src/test/ui/suggestions/derive-trait-for-method-call.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
error[E0599]: the method `test` exists for struct `Foo<Enum, CloneEnum>`, but its trait bounds were not satisfied
--> $DIR/derive-trait-for-method-call.rs:28:15
|
LL | enum Enum {
| ---------
| |
| doesn't satisfy `Enum: Clone`
| doesn't satisfy `Enum: Default`
...
LL | enum CloneEnum {
| -------------- doesn't satisfy `CloneEnum: Default`
...
LL | struct Foo<X, Y> (X, Y);
| ------------------------ method `test` not found for this
...
LL | let y = x.test();
| ^^^^ method cannot be called on `Foo<Enum, CloneEnum>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Enum: Clone`
`Enum: Default`
`CloneEnum: Default`
help: consider annotating `Enum` with `#[derive(Clone)]`
|
LL | #[derive(Clone)]
|

error[E0599]: the method `test` exists for struct `Foo<Struct, CloneStruct>`, but its trait bounds were not satisfied
--> $DIR/derive-trait-for-method-call.rs:34:15
|
LL | struct Struct {
| -------------
| |
| doesn't satisfy `Struct: Clone`
| doesn't satisfy `Struct: Default`
...
LL | struct CloneStruct {
| ------------------ doesn't satisfy `CloneStruct: Default`
...
LL | struct Foo<X, Y> (X, Y);
| ------------------------ method `test` not found for this
...
LL | let y = x.test();
| ^^^^ method cannot be called on `Foo<Struct, CloneStruct>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Struct: Clone`
`Struct: Default`
`CloneStruct: Default`
help: consider annotating `CloneStruct` with `#[derive(Default)]`
|
LL | #[derive(Default)]
|
help: consider annotating `Struct` with `#[derive(Clone, Default)]`
|
LL | #[derive(Clone, Default)]
|

error[E0599]: the method `test` exists for struct `Foo<Vec<Enum>, Instant>`, but its trait bounds were not satisfied
--> $DIR/derive-trait-for-method-call.rs:40:15
|
LL | struct Foo<X, Y> (X, Y);
| ------------------------ method `test` not found for this
...
LL | let y = x.test();
| ^^^^ method cannot be called on `Foo<Vec<Enum>, Instant>` due to unsatisfied trait bounds
|
::: $SRC_DIR/std/src/time.rs:LL:COL
|
LL | pub struct Instant(time::Instant);
| ---------------------------------- doesn't satisfy `Instant: Default`
|
::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
|
LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
| ------------------------------------------------------------------------------------------------ doesn't satisfy `Vec<Enum>: Clone`
|
= note: the following trait bounds were not satisfied:
`Vec<Enum>: Clone`
`Instant: Default`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0599`.
4 changes: 4 additions & 0 deletions src/test/ui/union/union-derive-clone.mirunsafeck.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ LL | let w = u.clone();
= note: the following trait bounds were not satisfied:
`CloneNoCopy: Copy`
which is required by `U5<CloneNoCopy>: Clone`
help: consider annotating `CloneNoCopy` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
|

error[E0277]: the trait bound `U1: Copy` is not satisfied
--> $DIR/union-derive-clone.rs:6:10
Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/union/union-derive-clone.thirunsafeck.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ LL | let w = u.clone();
= note: the following trait bounds were not satisfied:
`CloneNoCopy: Copy`
which is required by `U5<CloneNoCopy>: Clone`
help: consider annotating `CloneNoCopy` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
|

error[E0277]: the trait bound `U1: Copy` is not satisfied
--> $DIR/union-derive-clone.rs:6:10
Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/unique-pinned-nocopy.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ LL | | >(Unique<T>, A);
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `R` with `#[derive(Clone)]`
|
LL | #[derive(Clone)]
|

error: aborting due to previous error

Expand Down