On E0308 caused by cloning a reference due to missing bounds, account for derive
On type errors where the difference is expecting an owned type and getting a reference, if the expression is a `.clone()` call and the type is annotated with `#[derive(Clone)]`, we now explain implicit bounds and suggest manually implementing `Clone`.
```
error[E0308]: mismatched types
--> $DIR/derive-implicit-bound-on-clone.rs:10:5
|
LL | fn clone_me<T, K>(x: &ContainsRc<T, K>) -> ContainsRc<T, K> {
| ---------------- expected `ContainsRc<T, K>` because of return type
LL | x.clone()
| ^^^^^^^^^ expected `ContainsRc<T, K>`, found `&ContainsRc<T, K>`
|
= note: expected struct `ContainsRc<_, _>`
found reference `&ContainsRc<_, _>`
note: `ContainsRc<T, K>` does not implement `Clone`, so `&ContainsRc<T, K>` was cloned instead
--> $DIR/derive-implicit-bound-on-clone.rs:10:5
|
LL | x.clone()
| ^
help: `Clone` is not implemented because the some trait bounds could not be satisfied
--> $DIR/derive-implicit-bound-on-clone.rs:5:19
|
LL | #[derive(Clone)]
| ----- in this derive macro expansion
LL | struct ContainsRc<T, K> {
| ^ ^ derive introduces an implicit unsatisfied trait bound `K: Clone`
| |
| derive introduces an implicit unsatisfied trait bound `T: Clone`
= help: consider manually implementing `Clone` to avoid the implict type parameter bounds
```
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 3e4c194..285288e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1932,25 +1932,96 @@ pub(crate) fn note_type_is_not_clone(
None,
);
} else {
+ let mut suggest_derive = true;
if let Some(errors) =
self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
{
+ let manually_impl = "consider manually implementing `Clone` to avoid the \
+ implict type parameter bounds";
match &errors[..] {
[] => {}
[error] => {
- diag.help(format!(
- "`Clone` is not implemented because the trait bound `{}` is \
- not satisfied",
- error.obligation.predicate,
- ));
+ // diag.note("{error:#?}");
+ // diag.note(format!("{:#?} {:#?} {:#?}", error.obligation, error.obligation.cause, error.obligation.cause.code()));
+ let msg = "`Clone` is not implemented because a trait bound is not \
+ satisfied";
+ if let traits::ObligationCauseCode::ImplDerived(data) =
+ error.obligation.cause.code()
+ {
+ let mut span: MultiSpan = data.span.into();
+ if self.tcx.is_automatically_derived(data.impl_or_alias_def_id) {
+ span.push_span_label(
+ data.span,
+ format!(
+ "derive introduces an implicit `{}` bound",
+ error.obligation.predicate
+ ),
+ );
+ }
+ diag.span_help(span, msg);
+ if self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
+ && data.impl_or_alias_def_id.is_local()
+ {
+ diag.help(manually_impl);
+ suggest_derive = false;
+ }
+ } else {
+ diag.help(msg);
+ }
}
_ => {
- diag.help(format!(
- "`Clone` is not implemented because the following trait bounds \
- could not be satisfied: {}",
- listify(&errors, |e| format!("`{}`", e.obligation.predicate))
- .unwrap(),
- ));
+ let unsatisfied_bounds: Vec<_> = errors
+ .iter()
+ .filter_map(|error| match error.obligation.cause.code() {
+ traits::ObligationCauseCode::ImplDerived(data) => {
+ let pre = if self
+ .tcx
+ .is_automatically_derived(data.impl_or_alias_def_id)
+ {
+ "derive introduces an implicit "
+ } else {
+ ""
+ };
+ Some((
+ data.span,
+ format!(
+ "{pre}unsatisfied trait bound `{}`",
+ error.obligation.predicate
+ ),
+ ))
+ }
+ _ => None,
+ })
+ .collect();
+ let msg = "`Clone` is not implemented because the some trait bounds \
+ could not be satisfied";
+ if errors.len() == unsatisfied_bounds.len() {
+ let mut unsatisfied_bounds_spans: MultiSpan = unsatisfied_bounds
+ .iter()
+ .map(|(span, _)| *span)
+ .collect::<Vec<Span>>()
+ .into();
+ for (span, label) in unsatisfied_bounds {
+ unsatisfied_bounds_spans.push_span_label(span, label);
+ }
+ diag.span_help(unsatisfied_bounds_spans, msg);
+ if errors.iter().all(|error| match error.obligation.cause.code() {
+ traits::ObligationCauseCode::ImplDerived(data) => {
+ self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
+ && data.impl_or_alias_def_id.is_local()
+ }
+ _ => false,
+ }) {
+ diag.help(manually_impl);
+ suggest_derive = false;
+ }
+ } else {
+ diag.help(format!(
+ "{msg}: {}",
+ listify(&errors, |e| format!("`{}`", e.obligation.predicate))
+ .unwrap(),
+ ));
+ }
}
}
for error in errors {
@@ -1968,7 +2039,9 @@ pub(crate) fn note_type_is_not_clone(
}
}
}
- self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
+ if suggest_derive {
+ self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
+ }
}
}
}
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.stderr b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.stderr
index 301f3c3..5de99cc 100644
--- a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.stderr
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.stderr
@@ -13,7 +13,8 @@
|
LL | let mut x: HashSet<Day> = v.clone();
| ^
- = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
+help: `Clone` is not implemented because a trait bound is not satisfied
+ --> $SRC_DIR/std/src/collections/hash/set.rs:LL:COL
help: consider annotating `Day` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.stderr b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.stderr
index 301f3c3..5de99cc 100644
--- a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.stderr
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.stderr
@@ -13,7 +13,8 @@
|
LL | let mut x: HashSet<Day> = v.clone();
| ^
- = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
+help: `Clone` is not implemented because a trait bound is not satisfied
+ --> $SRC_DIR/std/src/collections/hash/set.rs:LL:COL
help: consider annotating `Day` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
diff --git a/tests/ui/traits/derive-implicit-bound-on-clone.rs b/tests/ui/traits/derive-implicit-bound-on-clone.rs
new file mode 100644
index 0000000..a4c9e88
--- /dev/null
+++ b/tests/ui/traits/derive-implicit-bound-on-clone.rs
@@ -0,0 +1,24 @@
+// Issue #146515
+use std::rc::Rc;
+
+#[derive(Clone)]
+struct ContainsRc<T, K> { //~ HELP `Clone` is not implemented
+ value: Rc<(T, K)>,
+}
+
+fn clone_me<T, K>(x: &ContainsRc<T, K>) -> ContainsRc<T, K> {
+ x.clone() //~ ERROR E0308
+ //~^ HELP consider manually implementing `Clone`
+}
+
+#[derive(Clone)]
+struct ContainsRcSingle<T> { //~ HELP `Clone` is not implemented
+ value: Rc<T>,
+}
+
+fn clone_me_single<T>(x: &ContainsRcSingle<T>) -> ContainsRcSingle<T> {
+ x.clone() //~ ERROR E0308
+ //~^ HELP consider manually implementing `Clone`
+}
+
+fn main() {}
diff --git a/tests/ui/traits/derive-implicit-bound-on-clone.stderr b/tests/ui/traits/derive-implicit-bound-on-clone.stderr
new file mode 100644
index 0000000..0cec4ef
--- /dev/null
+++ b/tests/ui/traits/derive-implicit-bound-on-clone.stderr
@@ -0,0 +1,53 @@
+error[E0308]: mismatched types
+ --> $DIR/derive-implicit-bound-on-clone.rs:10:5
+ |
+LL | fn clone_me<T, K>(x: &ContainsRc<T, K>) -> ContainsRc<T, K> {
+ | ---------------- expected `ContainsRc<T, K>` because of return type
+LL | x.clone()
+ | ^^^^^^^^^ expected `ContainsRc<T, K>`, found `&ContainsRc<T, K>`
+ |
+ = note: expected struct `ContainsRc<_, _>`
+ found reference `&ContainsRc<_, _>`
+note: `ContainsRc<T, K>` does not implement `Clone`, so `&ContainsRc<T, K>` was cloned instead
+ --> $DIR/derive-implicit-bound-on-clone.rs:10:5
+ |
+LL | x.clone()
+ | ^
+help: `Clone` is not implemented because the some trait bounds could not be satisfied
+ --> $DIR/derive-implicit-bound-on-clone.rs:5:19
+ |
+LL | #[derive(Clone)]
+ | ----- in this derive macro expansion
+LL | struct ContainsRc<T, K> {
+ | ^ ^ derive introduces an implicit unsatisfied trait bound `K: Clone`
+ | |
+ | derive introduces an implicit unsatisfied trait bound `T: Clone`
+ = help: consider manually implementing `Clone` to avoid the implict type parameter bounds
+
+error[E0308]: mismatched types
+ --> $DIR/derive-implicit-bound-on-clone.rs:20:5
+ |
+LL | fn clone_me_single<T>(x: &ContainsRcSingle<T>) -> ContainsRcSingle<T> {
+ | ------------------- expected `ContainsRcSingle<T>` because of return type
+LL | x.clone()
+ | ^^^^^^^^^ expected `ContainsRcSingle<T>`, found `&ContainsRcSingle<T>`
+ |
+ = note: expected struct `ContainsRcSingle<_>`
+ found reference `&ContainsRcSingle<_>`
+note: `ContainsRcSingle<T>` does not implement `Clone`, so `&ContainsRcSingle<T>` was cloned instead
+ --> $DIR/derive-implicit-bound-on-clone.rs:20:5
+ |
+LL | x.clone()
+ | ^
+help: `Clone` is not implemented because a trait bound is not satisfied
+ --> $DIR/derive-implicit-bound-on-clone.rs:15:25
+ |
+LL | #[derive(Clone)]
+ | ----- in this derive macro expansion
+LL | struct ContainsRcSingle<T> {
+ | ^ derive introduces an implicit `T: Clone` bound
+ = help: consider manually implementing `Clone` to avoid the implict type parameter bounds
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.