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
This commit is contained in:
commit
434cb437b5
11 changed files with 235 additions and 0 deletions
|
@ -829,6 +829,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
err.note(&format!(
|
err.note(&format!(
|
||||||
"the following trait bounds were not satisfied:\n{bound_list}"
|
"the following trait bounds were not satisfied:\n{bound_list}"
|
||||||
));
|
));
|
||||||
|
self.suggest_derive(&mut err, &unsatisfied_predicates);
|
||||||
|
|
||||||
unsatisfied_bounds = true;
|
unsatisfied_bounds = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -971,6 +973,85 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
None
|
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.
|
/// Print out the type for use in value namespace.
|
||||||
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
|
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
|
|
|
@ -203,6 +203,7 @@ use self::Ordering::*;
|
||||||
message = "can't compare `{Self}` with `{Rhs}`",
|
message = "can't compare `{Self}` with `{Rhs}`",
|
||||||
label = "no implementation for `{Self} == {Rhs}`"
|
label = "no implementation for `{Self} == {Rhs}`"
|
||||||
)]
|
)]
|
||||||
|
#[rustc_diagnostic_item = "PartialEq"]
|
||||||
pub trait PartialEq<Rhs: ?Sized = Self> {
|
pub trait PartialEq<Rhs: ?Sized = Self> {
|
||||||
/// This method tests for `self` and `other` values to be equal, and is used
|
/// This method tests for `self` and `other` values to be equal, and is used
|
||||||
/// by `==`.
|
/// by `==`.
|
||||||
|
@ -269,6 +270,7 @@ pub macro PartialEq($item:item) {
|
||||||
#[doc(alias = "==")]
|
#[doc(alias = "==")]
|
||||||
#[doc(alias = "!=")]
|
#[doc(alias = "!=")]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[rustc_diagnostic_item = "Eq"]
|
||||||
pub trait Eq: PartialEq<Self> {
|
pub trait Eq: PartialEq<Self> {
|
||||||
// this method is used solely by #[deriving] to assert
|
// this method is used solely by #[deriving] to assert
|
||||||
// that every component of a type implements #[deriving]
|
// that every component of a type implements #[deriving]
|
||||||
|
@ -728,6 +730,7 @@ impl<T: Clone> Clone for Reverse<T> {
|
||||||
#[doc(alias = "<=")]
|
#[doc(alias = "<=")]
|
||||||
#[doc(alias = ">=")]
|
#[doc(alias = ">=")]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[rustc_diagnostic_item = "Ord"]
|
||||||
pub trait Ord: Eq + PartialOrd<Self> {
|
pub trait Ord: Eq + PartialOrd<Self> {
|
||||||
/// This method returns an [`Ordering`] between `self` and `other`.
|
/// This method returns an [`Ordering`] between `self` and `other`.
|
||||||
///
|
///
|
||||||
|
@ -984,6 +987,7 @@ impl PartialOrd for Ordering {
|
||||||
message = "can't compare `{Self}` with `{Rhs}`",
|
message = "can't compare `{Self}` with `{Rhs}`",
|
||||||
label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`"
|
label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`"
|
||||||
)]
|
)]
|
||||||
|
#[rustc_diagnostic_item = "PartialOrd"]
|
||||||
pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
|
pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
|
||||||
/// This method returns an ordering between `self` and `other` values if one exists.
|
/// This method returns an ordering between `self` and `other` values if one exists.
|
||||||
///
|
///
|
||||||
|
|
|
@ -157,6 +157,7 @@ mod sip;
|
||||||
/// [`HashSet`]: ../../std/collections/struct.HashSet.html
|
/// [`HashSet`]: ../../std/collections/struct.HashSet.html
|
||||||
/// [`hash`]: Hash::hash
|
/// [`hash`]: Hash::hash
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[rustc_diagnostic_item = "Hash"]
|
||||||
pub trait Hash {
|
pub trait Hash {
|
||||||
/// Feeds this value into the given [`Hasher`].
|
/// Feeds this value into the given [`Hasher`].
|
||||||
///
|
///
|
||||||
|
|
|
@ -382,6 +382,7 @@ pub trait StructuralEq {
|
||||||
// existing specializations on `Copy` that already exist in the standard
|
// existing specializations on `Copy` that already exist in the standard
|
||||||
// library, and there's no way to safely have this behavior right now.
|
// library, and there's no way to safely have this behavior right now.
|
||||||
#[rustc_unsafe_specialization_marker]
|
#[rustc_unsafe_specialization_marker]
|
||||||
|
#[rustc_diagnostic_item = "Copy"]
|
||||||
pub trait Copy: Clone {
|
pub trait Copy: Clone {
|
||||||
// Empty.
|
// Empty.
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
= 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:
|
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||||
candidate #1: `Clone`
|
candidate #1: `Clone`
|
||||||
|
help: consider annotating `NotClone` with `#[derive(Clone)]`
|
||||||
|
|
|
||||||
|
LL | #[derive(Clone)]
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,10 @@ LL | a.unwrap();
|
||||||
|
|
|
|
||||||
= note: the following trait bounds were not satisfied:
|
= note: the following trait bounds were not satisfied:
|
||||||
`Foo: Debug`
|
`Foo: Debug`
|
||||||
|
help: consider annotating `Foo` with `#[derive(Debug)]`
|
||||||
|
|
|
||||||
|
LL | #[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
44
src/test/ui/suggestions/derive-trait-for-method-call.rs
Normal file
44
src/test/ui/suggestions/derive-trait-for-method-call.rs
Normal file
|
@ -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
src/test/ui/suggestions/derive-trait-for-method-call.stderr
Normal file
84
src/test/ui/suggestions/derive-trait-for-method-call.stderr
Normal file
|
@ -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`.
|
|
@ -16,6 +16,10 @@ LL | let w = u.clone();
|
||||||
= note: the following trait bounds were not satisfied:
|
= note: the following trait bounds were not satisfied:
|
||||||
`CloneNoCopy: Copy`
|
`CloneNoCopy: Copy`
|
||||||
which is required by `U5<CloneNoCopy>: Clone`
|
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
|
error[E0277]: the trait bound `U1: Copy` is not satisfied
|
||||||
--> $DIR/union-derive-clone.rs:6:10
|
--> $DIR/union-derive-clone.rs:6:10
|
||||||
|
|
|
@ -16,6 +16,10 @@ LL | let w = u.clone();
|
||||||
= note: the following trait bounds were not satisfied:
|
= note: the following trait bounds were not satisfied:
|
||||||
`CloneNoCopy: Copy`
|
`CloneNoCopy: Copy`
|
||||||
which is required by `U5<CloneNoCopy>: Clone`
|
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
|
error[E0277]: the trait bound `U1: Copy` is not satisfied
|
||||||
--> $DIR/union-derive-clone.rs:6:10
|
--> $DIR/union-derive-clone.rs:6:10
|
||||||
|
|
|
@ -21,6 +21,10 @@ LL | | >(Unique<T>, A);
|
||||||
= help: items from traits can only be used if the trait is implemented and in scope
|
= 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:
|
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||||
candidate #1: `Clone`
|
candidate #1: `Clone`
|
||||||
|
help: consider annotating `R` with `#[derive(Clone)]`
|
||||||
|
|
|
||||||
|
LL | #[derive(Clone)]
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue