Rollup merge of #144201 - estebank:suggest-clone, r=SparrowLii

Mention type that could be `Clone` but isn't in more cases

When encountering a moved value of a type that isn't `Clone` because of unmet obligations, but where all the unmet predicates reference crate-local types, mention them and suggest cloning, as we do in other cases already:

```
error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure
  --> f111.rs:14:25
   |
13 | fn do_stuff(foo: Option<Foo>) {
   |             --- captured outer variable
14 |     require_fn_trait(|| async {
   |                      -- ^^^^^ `foo` is moved here
   |                      |
   |                      captured by this `Fn` closure
15 |         if foo.map_or(false, |f| f.foo()) {
   |            ---
   |            |
   |            variable moved due to use in coroutine
   |            move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait
   |
note: if `Foo` implemented `Clone`, you could clone the value
  --> f111.rs:4:1
   |
4  | struct Foo;
   | ^^^^^^^^^^ consider implementing `Clone` for this type
...
15 |         if foo.map_or(false, |f| f.foo()) {
   |            --- you could clone this value
```

CC rust-lang/rust#68119.
This commit is contained in:
Trevor Gross
2025-07-26 01:15:05 -05:00
committed by GitHub
18 changed files with 364 additions and 0 deletions

View File

@@ -1290,6 +1290,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
span,
format!("if `{ty}` implemented `Clone`, you could clone the value"),
);
} else if let ty::Adt(_, _) = ty.kind()
&& let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
{
// For cases like `Option<NonClone>`, where `Option<T>: Clone` if `T: Clone`, we point
// at the types that should be `Clone`.
let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);
let cause = ObligationCause::misc(expr.span, self.mir_def_id());
ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait);
let errors = ocx.select_all_or_error();
if errors.iter().all(|error| {
match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) {
Some(clause) => match clause.self_ty().skip_binder().kind() {
ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait,
_ => false,
},
None => false,
}
}) {
let mut type_spans = vec![];
let mut types = FxIndexSet::default();
for clause in errors
.iter()
.filter_map(|e| e.obligation.predicate.as_clause())
.filter_map(|c| c.as_trait_clause())
{
let ty::Adt(def, _) = clause.self_ty().skip_binder().kind() else { continue };
type_spans.push(self.infcx.tcx.def_span(def.did()));
types.insert(
self.infcx
.tcx
.short_string(clause.self_ty().skip_binder(), &mut err.long_ty_path()),
);
}
let mut span: MultiSpan = type_spans.clone().into();
for sp in type_spans {
span.push_span_label(sp, "consider implementing `Clone` for this type");
}
span.push_span_label(expr.span, "you could clone this value");
let types: Vec<_> = types.into_iter().collect();
let msg = match &types[..] {
[only] => format!("`{only}`"),
[head @ .., last] => format!(
"{} and `{last}`",
head.iter().map(|t| format!("`{t}`")).collect::<Vec<_>>().join(", ")
),
[] => unreachable!(),
};
err.span_note(
span,
format!("if {msg} implemented `Clone`, you could clone the value"),
);
}
}
}

View File

@@ -8,6 +8,15 @@ LL | drop(t);
| - value moved here
LL | t.b = Some(u);
| ^^^ value assigned here after move
|
note: if `Test2` implemented `Clone`, you could clone the value
--> $DIR/borrowck-partial-reinit-1.rs:3:1
|
LL | struct Test2 {
| ^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(t);
| - you could clone this value
error[E0382]: assign of moved value: `t`
--> $DIR/borrowck-partial-reinit-1.rs:33:5
@@ -19,6 +28,15 @@ LL | drop(t);
| - value moved here
LL | t.0 = Some(u);
| ^^^ value assigned here after move
|
note: if `Test3` implemented `Clone`, you could clone the value
--> $DIR/borrowck-partial-reinit-1.rs:7:1
|
LL | struct Test3(Option<Test>);
| ^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(t);
| - you could clone this value
error: aborting due to 2 previous errors

View File

@@ -7,6 +7,15 @@ LL | let mut u = Test { a: 2, b: Some(Box::new(t))};
| - value moved here
LL | t.b = Some(Box::new(u));
| ^^^ value assigned here after move
|
note: if `Test` implemented `Clone`, you could clone the value
--> $DIR/borrowck-partial-reinit-2.rs:1:1
|
LL | struct Test {
| ^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let mut u = Test { a: 2, b: Some(Box::new(t))};
| - you could clone this value
error: aborting due to 1 previous error

View File

@@ -7,6 +7,15 @@ LL | let a = u.a;
| --- value moved here
LL | let a = u.a;
| ^^^ value used here after move
|
note: if `U` implemented `Clone`, you could clone the value
--> $DIR/borrowck-union-move-assign.rs:7:1
|
LL | union U {
| ^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.a;
| --- you could clone this value
error: aborting due to 1 previous error

View File

@@ -7,6 +7,15 @@ LL | let a = u.n1;
| ---- value moved here
LL | let a = u.n1;
| ^^^^ value used here after move
|
note: if `Unn` implemented `Clone`, you could clone the value
--> $DIR/borrowck-union-move.rs:7:1
|
LL | union Unn {
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.n1;
| ---- you could clone this value
error[E0382]: use of moved value: `u`
--> $DIR/borrowck-union-move.rs:31:21
@@ -17,6 +26,15 @@ LL | let a = u.n1;
| ---- value moved here
LL | let a = u;
| ^ value used here after move
|
note: if `Unn` implemented `Clone`, you could clone the value
--> $DIR/borrowck-union-move.rs:7:1
|
LL | union Unn {
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.n1;
| ---- you could clone this value
error[E0382]: use of moved value: `u`
--> $DIR/borrowck-union-move.rs:36:21
@@ -27,6 +45,15 @@ LL | let a = u.n1;
| ---- value moved here
LL | let a = u.n2;
| ^^^^ value used here after move
|
note: if `Unn` implemented `Clone`, you could clone the value
--> $DIR/borrowck-union-move.rs:7:1
|
LL | union Unn {
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.n1;
| ---- you could clone this value
error[E0382]: use of moved value: `u`
--> $DIR/borrowck-union-move.rs:63:21
@@ -37,6 +64,15 @@ LL | let a = u.n;
| --- value moved here
LL | let a = u.n;
| ^^^ value used here after move
|
note: if `Ucn` implemented `Clone`, you could clone the value
--> $DIR/borrowck-union-move.rs:15:1
|
LL | union Ucn {
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.n;
| --- you could clone this value
error[E0382]: use of moved value: `u`
--> $DIR/borrowck-union-move.rs:68:21
@@ -47,6 +83,15 @@ LL | let a = u.n;
| --- value moved here
LL | let a = u.c;
| ^^^ value used here after move
|
note: if `Ucn` implemented `Clone`, you could clone the value
--> $DIR/borrowck-union-move.rs:15:1
|
LL | union Ucn {
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.n;
| --- you could clone this value
error[E0382]: use of moved value: `u`
--> $DIR/borrowck-union-move.rs:83:21
@@ -57,6 +102,15 @@ LL | let a = u.n;
| --- value moved here
LL | let a = u;
| ^ value used here after move
|
note: if `Ucn` implemented `Clone`, you could clone the value
--> $DIR/borrowck-union-move.rs:15:1
|
LL | union Ucn {
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.n;
| --- you could clone this value
error: aborting due to 6 previous errors

View File

@@ -17,6 +17,15 @@ LL | drop(u);
| - value moved here
LL | u.0 = S(1);
| ^^^^^^^^^^ value partially assigned here after move
|
note: if `Tpair` implemented `Clone`, you could clone the value
--> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:6:1
|
LL | struct Tpair(S, i32);
| ^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(u);
| - you could clone this value
error[E0382]: assign to part of moved value: `v`
--> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:31:9
@@ -27,6 +36,15 @@ LL | drop(v);
| - value moved here
LL | v.x = S(1);
| ^^^^^^^^^^ value partially assigned here after move
|
note: if `Spair` implemented `Clone`, you could clone the value
--> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:7:1
|
LL | struct Spair { x: S, y: i32 }
| ^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(v);
| - you could clone this value
error: aborting due to 3 previous errors

View File

@@ -50,6 +50,15 @@ LL | drop(u);
| - value moved here
LL | u.0 = S(1);
| ^^^^^^^^^^ value partially assigned here after move
|
note: if `Tpair` implemented `Clone`, you could clone the value
--> $DIR/issue-54499-field-mutation-of-moved-out.rs:6:1
|
LL | struct Tpair(S, i32);
| ^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(u);
| - you could clone this value
error[E0594]: cannot assign to `u.1`, as `u` is not declared as mutable
--> $DIR/issue-54499-field-mutation-of-moved-out.rs:27:9
@@ -82,6 +91,15 @@ LL | drop(v);
| - value moved here
LL | v.x = S(1);
| ^^^^^^^^^^ value partially assigned here after move
|
note: if `Spair` implemented `Clone`, you could clone the value
--> $DIR/issue-54499-field-mutation-of-moved-out.rs:7:1
|
LL | struct Spair { x: S, y: i32 }
| ^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(v);
| - you could clone this value
error[E0594]: cannot assign to `v.y`, as `v` is not declared as mutable
--> $DIR/issue-54499-field-mutation-of-moved-out.rs:38:9

View File

@@ -4,6 +4,14 @@ error[E0507]: cannot move out of `*array` which is behind a shared reference
LL | *array
| ^^^^^^ move occurs because `*array` has type `Vec<Value>`, which does not implement the `Copy` trait
|
note: if `Value` implemented `Clone`, you could clone the value
--> $DIR/issue-54597-reject-move-out-of-borrow-via-pat.rs:4:1
|
LL | struct Value;
| ^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | *array
| ------ you could clone this value
help: consider removing the dereference here
|
LL - *array

View File

@@ -26,6 +26,15 @@ LL | E::Number(_) if let E::String(s) = *value => { }
...
LL | let x = value;
| ^^^^^ value used here after move
|
note: if `E` implemented `Clone`, you could clone the value
--> $DIR/if-let-guards-errors.rs:32:1
|
LL | E::Number(_) if let E::String(s) = *value => { }
| ------ you could clone this value
...
LL | enum E {
| ^^^^^^ consider implementing `Clone` for this type
error: aborting due to 2 previous errors

View File

@@ -26,6 +26,15 @@ LL | E::Number(_) if let E::String(s) = *value => { }
...
LL | let x = value;
| ^^^^^ value used here after move
|
note: if `E` implemented `Clone`, you could clone the value
--> $DIR/if-let-guards-errors.rs:32:1
|
LL | E::Number(_) if let E::String(s) = *value => { }
| ------ you could clone this value
...
LL | enum E {
| ^^^^^^ consider implementing `Clone` for this type
error: aborting due to 2 previous errors

View File

@@ -8,6 +8,14 @@ LL | let mut copy: Vec<U> = map.clone().into_values().collect();
|
note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`, which moves value
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
note: if `Hash128_1` implemented `Clone`, you could clone the value
--> $DIR/suggest-clone-when-some-obligation-is-unmet.rs:8:1
|
LL | pub struct Hash128_1;
| ^^^^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let mut copy: Vec<U> = map.clone().into_values().collect();
| ----------- you could clone this value
help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied
|
LL - let mut copy: Vec<U> = map.clone().into_values().collect();

View File

@@ -27,6 +27,15 @@ LL | drop(d);
| - value moved here
LL | d.x = 10;
| ^^^^^^^^ value assigned here after move
|
note: if `D` implemented `Clone`, you could clone the value
--> $DIR/issue-21232-partial-init-and-erroneous-use.rs:11:1
|
LL | struct D {
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(d);
| - you could clone this value
error[E0381]: partially assigned binding `d` isn't fully initialized
--> $DIR/issue-21232-partial-init-and-erroneous-use.rs:45:5
@@ -57,6 +66,15 @@ LL | drop(d);
| - value moved here
LL | d.s.y = 20;
| ^^^^^^^^^^ value partially assigned here after move
|
note: if `D` implemented `Clone`, you could clone the value
--> $DIR/issue-21232-partial-init-and-erroneous-use.rs:11:1
|
LL | struct D {
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | drop(d);
| - you could clone this value
error: aborting due to 6 previous errors

View File

@@ -146,6 +146,15 @@ LL | m!((ref mut borrow, mov) = tup0);
...
LL | drop(&tup0);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!((ref mut borrow, mov) = tup0);
| ---- you could clone this value
error[E0382]: borrow of moved value: `tup1`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:76:10
@@ -161,6 +170,15 @@ LL | m!((mov, _, ref mut borrow) = tup1);
...
LL | drop(&tup1);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!((mov, _, ref mut borrow) = tup1);
| ---- you could clone this value
error[E0382]: borrow of moved value: `tup2`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:77:10
@@ -176,6 +194,15 @@ LL | m!((ref borrow, mov) = tup2);
...
LL | drop(&tup2);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!((ref borrow, mov) = tup2);
| ---- you could clone this value
error[E0382]: borrow of moved value: `tup3`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:78:10
@@ -191,6 +218,15 @@ LL | m!((mov, _, ref borrow) = tup3);
...
LL | drop(&tup3);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!((mov, _, ref borrow) = tup3);
| ---- you could clone this value
error[E0382]: borrow of moved value: `tup4`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:79:21
@@ -206,6 +242,15 @@ LL | m!((ref borrow, mov) = tup4);
...
LL | m!((ref x, _) = &tup4);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!((ref borrow, mov) = tup4);
| ---- you could clone this value
error[E0382]: borrow of moved value: `arr0`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:80:10
@@ -221,6 +266,15 @@ LL | m!([mov @ .., ref borrow] = arr0);
...
LL | drop(&arr0);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!([mov @ .., ref borrow] = arr0);
| ---- you could clone this value
error[E0382]: borrow of moved value: `arr1`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:81:35
@@ -236,6 +290,15 @@ LL | m!([_, ref mut borrow @ .., _, mov] = arr1);
...
LL | m!([_, mov1, mov2, mov3, _] = &arr1);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!([_, ref mut borrow @ .., _, mov] = arr1);
| ---- you could clone this value
error[E0382]: borrow of moved value: `arr2`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:82:10
@@ -251,6 +314,15 @@ LL | m!([mov @ .., ref borrow] = arr2);
...
LL | drop(&arr2);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!([mov @ .., ref borrow] = arr2);
| ---- you could clone this value
error[E0382]: borrow of moved value: `arr3`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:83:35
@@ -265,6 +337,15 @@ LL | m!([_, ref borrow @ .., _, mov] = arr3);
...
LL | m!([_, mov1, mov2, mov3, _] = &arr3);
| ^^^^^ value borrowed here after move
|
note: if `S` implemented `Clone`, you could clone the value
--> $DIR/move-ref-patterns-closure-captures-inside.rs:2:5
|
LL | struct S; // Not `Copy`.
| ^^^^^^^^ consider implementing `Clone` for this type
...
LL | m!([_, ref borrow @ .., _, mov] = arr3);
| ---- you could clone this value
error[E0382]: borrow of moved value: `tup0`
--> $DIR/move-ref-patterns-closure-captures-inside.rs:111:10

View File

@@ -3,6 +3,15 @@ error[E0507]: cannot move out of static item `BAR`
|
LL | test(BAR);
| ^^^ move occurs because `BAR` has type `Foo`, which does not implement the `Copy` trait
|
note: if `Foo` implemented `Clone`, you could clone the value
--> $DIR/static-items-cant-move.rs:5:1
|
LL | struct Foo {
| ^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | test(BAR);
| --- you could clone this value
error: aborting due to 1 previous error

View File

@@ -13,6 +13,15 @@ LL | move || {
LL |
LL | var = Some(NotCopyable);
| --- variable moved due to use in closure
|
note: if `NotCopyable` implemented `Clone`, you could clone the value
--> $DIR/option-content-move2.rs:1:1
|
LL | struct NotCopyable;
| ^^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | var = Some(NotCopyable);
| --- you could clone this value
error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closure
--> $DIR/option-content-move2.rs:21:9

View File

@@ -31,6 +31,15 @@ LL | let a = u.x;
| --- value moved here
LL | let b = u.y;
| ^^^ value used here after move
|
note: if `U` implemented `Clone`, you could clone the value
--> $DIR/union-borrow-move-parent-sibling.rs:43:1
|
LL | union U {
| ^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.x;
| --- you could clone this value
error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x`)
--> $DIR/union-borrow-move-parent-sibling.rs:67:13
@@ -73,6 +82,15 @@ LL | let a = u.x;
| --- value moved here
LL | let b = u.y;
| ^^^ value used here after move
|
note: if `U` implemented `Clone`, you could clone the value
--> $DIR/union-borrow-move-parent-sibling.rs:43:1
|
LL | union U {
| ^^^^^^^ consider implementing `Clone` for this type
...
LL | let a = u.x;
| --- you could clone this value
error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `u.y`)
--> $DIR/union-borrow-move-parent-sibling.rs:81:13

View File

@@ -16,6 +16,15 @@ LL | let binder: unsafe<> NotCopy = wrap_binder!(base);
| ---- value moved here
LL | drop(base);
| ^^^^ value used here after move
|
note: if `NotCopyInner` implemented `Clone`, you could clone the value
--> $DIR/moves.rs:8:1
|
LL | struct NotCopyInner;
| ^^^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let binder: unsafe<> NotCopy = wrap_binder!(base);
| ---- you could clone this value
error[E0382]: use of moved value: `binder`
--> $DIR/moves.rs:24:14

View File

@@ -17,6 +17,14 @@ LL | fn push(&mut self, n: Box<dyn ToString + 'static>) {
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value
| |
| in this method
note: if `Number` implemented `Clone`, you could clone the value
--> $DIR/use-after-move-implicity-coerced-object.rs:3:1
|
LL | struct Number {
| ^^^^^^^^^^^^^ consider implementing `Clone` for this type
...
LL | l.push(n);
| - you could clone this value
error: aborting due to 1 previous error