about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-09-08 07:27:41 +0000
committerbors <bors@rust-lang.org>2021-09-08 07:27:41 +0000
commit434cb437b55d61bcb54a01921de7ac752e6dee13 (patch)
tree845babdaf6bd27957d622788e08029592ca833fe
parent72969f6526b6a3ff9f2bbfe73e26249d977794e7 (diff)
parent50e5f90c925b411bac7de0ec3ec239d7d321b5a2 (diff)
downloadrust-434cb437b55d61bcb54a01921de7ac752e6dee13.tar.gz
rust-434cb437b55d61bcb54a01921de7ac752e6dee13.zip
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
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs81
-rw-r--r--library/core/src/cmp.rs4
-rw-r--r--library/core/src/hash/mod.rs1
-rw-r--r--library/core/src/marker.rs1
-rw-r--r--src/test/ui/derives/derive-assoc-type-not-impl.stderr4
-rw-r--r--src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr4
-rw-r--r--src/test/ui/suggestions/derive-trait-for-method-call.rs44
-rw-r--r--src/test/ui/suggestions/derive-trait-for-method-call.stderr84
-rw-r--r--src/test/ui/union/union-derive-clone.mirunsafeck.stderr4
-rw-r--r--src/test/ui/union/union-derive-clone.thirunsafeck.stderr4
-rw-r--r--src/test/ui/unique-pinned-nocopy.stderr4
11 files changed, 235 insertions, 0 deletions
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<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() {
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<Rhs: ?Sized = Self> {
     /// 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<Self> {
     // this method is used solely by #[deriving] to assert
     // that every component of a type implements #[deriving]
@@ -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`.
     ///
@@ -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.
     ///
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::<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
 
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> (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() {}
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<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`.
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<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
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<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
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<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