about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2023-11-21 01:09:11 +0000
committerEsteban Küber <esteban@kuber.com.ar>2023-12-04 21:54:33 +0000
commita754fcaa43e02472ff9e1971e0e7085f58a6f041 (patch)
tree9b1123473b7ac9f96844ef2f35b1618300f98d5a
parent1c69c6ab0890a8a4df947e41102dc667d668718e (diff)
downloadrust-a754fcaa43e02472ff9e1971e0e7085f58a6f041.tar.gz
rust-a754fcaa43e02472ff9e1971e0e7085f58a6f041.zip
On "this .clone() is on the reference", provide more info
When encountering a case where `let x: T = (val: &T).clone();` and
`T: !Clone`, already mention that the reference is being cloned. We now
also suggest `#[derive(Clone)]` not only on `T` but also on type
parameters to satisfy blanket implementations.

```
error[E0308]: mismatched types
  --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:17:39
   |
LL |             let mut x: HashSet<Day> = v.clone();
   |                        ------------   ^^^^^^^^^ expected `HashSet<Day>`, found `&HashSet<Day>`
   |                        |
   |                        expected due to this
   |
   = note: expected struct `HashSet<Day>`
           found reference `&HashSet<Day>`
note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned instead
  --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:17:39
   |
LL |             let mut x: HashSet<Day> = v.clone();
   |                                       ^
   = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
help: consider annotating `Day` with `#[derive(Clone)]`
   |
LL + #[derive(Clone)]
LL | enum Day {
   |
```

Case taken from # #41825.
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs75
-rw-r--r--tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed30
-rw-r--r--tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs29
-rw-r--r--tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr25
4 files changed, 158 insertions, 1 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index a904b419a94..b61b029813c 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -21,7 +21,7 @@ use rustc_hir::{
     StmtKind, TyKind, WherePredicate,
 };
 use rustc_hir_analysis::astconv::AstConv;
-use rustc_infer::traits::{self, StatementAsExpression};
+use rustc_infer::traits::{self, StatementAsExpression, TraitEngineExt};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -34,6 +34,7 @@ use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::solve::FulfillmentCtxt;
 use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
 use rustc_trait_selection::traits::error_reporting::DefIdOrName;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1619,6 +1620,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     None,
                 );
             } else {
+                self.infcx.probe(|_snapshot| {
+                    if let ty::Adt(def, args) = expected_ty.kind()
+                        && let Some((def_id, _imp)) = self
+                            .tcx
+                            .all_impls(clone_trait_did)
+                            .filter_map(|def_id| {
+                                self.tcx.impl_trait_ref(def_id).map(|r| (def_id, r))
+                            })
+                            .map(|(def_id, imp)| (def_id, imp.skip_binder()))
+                            .filter(|(_, imp)| match imp.self_ty().peel_refs().kind() {
+                                ty::Adt(i_def, _) if i_def.did() == def.did() => true,
+                                _ => false,
+                            })
+                            .next()
+                    {
+                        let mut fulfill_cx = FulfillmentCtxt::new(&self.infcx);
+                        // We get all obligations from the impl to talk about specific
+                        // trait bounds.
+                        let obligations = self
+                            .tcx
+                            .predicates_of(def_id)
+                            .instantiate(self.tcx, args)
+                            .into_iter()
+                            .map(|(clause, span)| {
+                                traits::Obligation::new(
+                                    self.tcx,
+                                    traits::ObligationCause::misc(span, self.body_id),
+                                    self.param_env,
+                                    clause,
+                                )
+                            })
+                            .collect::<Vec<_>>();
+                        fulfill_cx.register_predicate_obligations(&self.infcx, obligations);
+                        let errors = fulfill_cx.select_all_or_error(&self.infcx);
+                        match &errors[..] {
+                            [] => {}
+                            [error] => {
+                                diag.help(format!(
+                                    "`Clone` is not implemented because the trait bound `{}` is \
+                                     not satisfied",
+                                    error.obligation.predicate,
+                                ));
+                            }
+                            [errors @ .., last] => {
+                                diag.help(format!(
+                                    "`Clone` is not implemented because the following trait bounds \
+                                     could not be satisfied: {} and `{}`",
+                                    errors
+                                        .iter()
+                                        .map(|e| format!("`{}`", e.obligation.predicate))
+                                        .collect::<Vec<_>>()
+                                        .join(", "),
+                                    last.obligation.predicate,
+                                ));
+                            }
+                        }
+                        for error in errors {
+                            if let traits::FulfillmentErrorCode::CodeSelectionError(
+                                traits::SelectionError::Unimplemented,
+                            ) = error.code
+                                && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+                                    error.obligation.predicate.kind().skip_binder()
+                            {
+                                self.infcx.err_ctxt().suggest_derive(
+                                    &error.obligation,
+                                    diag,
+                                    error.obligation.predicate.kind().rebind(pred),
+                                );
+                            }
+                        }
+                    }
+                });
                 self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
             }
         }
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed
new file mode 100644
index 00000000000..e88ca6079ec
--- /dev/null
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+#![allow(unused_variables, dead_code)]
+use std::collections::BTreeMap;
+use std::collections::HashSet;
+
+#[derive(Debug,Eq,PartialEq,Hash)]
+#[derive(Clone)]
+enum Day {
+    Mon,
+}
+
+struct Class {
+    days: BTreeMap<u32, HashSet<Day>>
+}
+
+impl Class {
+    fn do_stuff(&self) {
+        for (_, v) in &self.days {
+            let mut x: HashSet<Day> = v.clone(); //~ ERROR
+            let y: Vec<Day> = x.drain().collect();
+            println!("{:?}", x);
+        }
+    }
+}
+
+fn fail() {
+    let c = Class { days: BTreeMap::new() };
+    c.do_stuff();
+}
+fn main() {}
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs
new file mode 100644
index 00000000000..ba277c4a9c4
--- /dev/null
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+#![allow(unused_variables, dead_code)]
+use std::collections::BTreeMap;
+use std::collections::HashSet;
+
+#[derive(Debug,Eq,PartialEq,Hash)]
+enum Day {
+    Mon,
+}
+
+struct Class {
+    days: BTreeMap<u32, HashSet<Day>>
+}
+
+impl Class {
+    fn do_stuff(&self) {
+        for (_, v) in &self.days {
+            let mut x: HashSet<Day> = v.clone(); //~ ERROR
+            let y: Vec<Day> = x.drain().collect();
+            println!("{:?}", x);
+        }
+    }
+}
+
+fn fail() {
+    let c = Class { days: BTreeMap::new() };
+    c.do_stuff();
+}
+fn main() {}
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr
new file mode 100644
index 00000000000..3ca4bffd624
--- /dev/null
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr
@@ -0,0 +1,25 @@
+error[E0308]: mismatched types
+  --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
+   |
+LL |             let mut x: HashSet<Day> = v.clone();
+   |                        ------------   ^^^^^^^^^ expected `HashSet<Day>`, found `&HashSet<Day>`
+   |                        |
+   |                        expected due to this
+   |
+   = note: expected struct `HashSet<Day>`
+           found reference `&HashSet<Day>`
+note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned instead
+  --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
+   |
+LL |             let mut x: HashSet<Day> = v.clone();
+   |                                       ^
+   = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
+help: consider annotating `Day` with `#[derive(Clone)]`
+   |
+LL + #[derive(Clone)]
+LL | enum Day {
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.