diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 9ed6d0c6ece..a746ad7ad4b 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -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; } } @@ -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>)>, + ) { + 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::>(); + 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() { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 4e82b655394..5ac9fdec0cf 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -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 { /// This method tests for `self` and `other` values to be equal, and is used /// by `==`. @@ -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 { // this method is used solely by #[deriving] to assert // that every component of a type implements #[deriving] @@ -728,6 +730,7 @@ impl Clone for Reverse { #[doc(alias = "<=")] #[doc(alias = ">=")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Ord"] pub trait Ord: Eq + PartialOrd { /// This method returns an [`Ordering`] between `self` and `other`. /// @@ -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: PartialEq { /// This method returns an ordering between `self` and `other` values if one exists. /// diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 77161e961e7..da3f20d18e5 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -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`]. /// diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 333f81ce4cf..82962a1fe13 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -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. } diff --git a/src/test/ui/derives/derive-assoc-type-not-impl.stderr b/src/test/ui/derives/derive-assoc-type-not-impl.stderr index fd993d0f9d8..1080f947732 100644 --- a/src/test/ui/derives/derive-assoc-type-not-impl.stderr +++ b/src/test/ui/derives/derive-assoc-type-not-impl.stderr @@ -19,6 +19,10 @@ LL | Bar:: { 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 diff --git a/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr b/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr index 1030061b2d1..596b7bfe79c 100644 --- a/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr +++ b/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr @@ -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 diff --git a/src/test/ui/suggestions/derive-trait-for-method-call.rs b/src/test/ui/suggestions/derive-trait-for-method-call.rs new file mode 100644 index 00000000000..25043da52aa --- /dev/null +++ b/src/test/ui/suggestions/derive-trait-for-method-call.rs @@ -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); +impl Foo { + 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`, 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`, but its trait bounds were not satisfied [E0599] +} + +fn test3() { + let x = Foo(Vec::::new(), Instant::now()); + let y = x.test(); + //~^the method `test` exists for struct `Foo, Instant>`, but its trait bounds were not satisfied [E0599] +} + +fn main() {} diff --git a/src/test/ui/suggestions/derive-trait-for-method-call.stderr b/src/test/ui/suggestions/derive-trait-for-method-call.stderr new file mode 100644 index 00000000000..97fc1134a94 --- /dev/null +++ b/src/test/ui/suggestions/derive-trait-for-method-call.stderr @@ -0,0 +1,84 @@ +error[E0599]: the method `test` exists for struct `Foo`, 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); + | ------------------------ method `test` not found for this +... +LL | let y = x.test(); + | ^^^^ method cannot be called on `Foo` 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`, 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); + | ------------------------ method `test` not found for this +... +LL | let y = x.test(); + | ^^^^ method cannot be called on `Foo` 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, Instant>`, but its trait bounds were not satisfied + --> $DIR/derive-trait-for-method-call.rs:40:15 + | +LL | struct Foo (X, Y); + | ------------------------ method `test` not found for this +... +LL | let y = x.test(); + | ^^^^ method cannot be called on `Foo, 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 { + | ------------------------------------------------------------------------------------------------ doesn't satisfy `Vec: Clone` + | + = note: the following trait bounds were not satisfied: + `Vec: Clone` + `Instant: Default` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/union/union-derive-clone.mirunsafeck.stderr b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr index b52117cd19c..146a627bcde 100644 --- a/src/test/ui/union/union-derive-clone.mirunsafeck.stderr +++ b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr @@ -16,6 +16,10 @@ LL | let w = u.clone(); = note: the following trait bounds were not satisfied: `CloneNoCopy: Copy` which is required by `U5: 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 diff --git a/src/test/ui/union/union-derive-clone.thirunsafeck.stderr b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr index b52117cd19c..146a627bcde 100644 --- a/src/test/ui/union/union-derive-clone.thirunsafeck.stderr +++ b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr @@ -16,6 +16,10 @@ LL | let w = u.clone(); = note: the following trait bounds were not satisfied: `CloneNoCopy: Copy` which is required by `U5: 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 diff --git a/src/test/ui/unique-pinned-nocopy.stderr b/src/test/ui/unique-pinned-nocopy.stderr index 8e0804ebf9b..02ce371c8d5 100644 --- a/src/test/ui/unique-pinned-nocopy.stderr +++ b/src/test/ui/unique-pinned-nocopy.stderr @@ -21,6 +21,10 @@ LL | | >(Unique, 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