about summary refs log tree commit diff
path: root/tests/ui/error-codes
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-04-13 09:07:26 +0000
committerbors <bors@rust-lang.org>2024-04-13 09:07:26 +0000
commit6eaa7fb5762c9cc5e3335d0e5f1bb62ff990c623 (patch)
tree3ef33274f83d1fe8a43ad13209089b11063332ac /tests/ui/error-codes
parentf96442b4482fef6a50b65f2284bcfb8921320183 (diff)
parent4c7213c888174612250f77c29d929c16b331ade5 (diff)
downloadrust-6eaa7fb5762c9cc5e3335d0e5f1bb62ff990c623.tar.gz
rust-6eaa7fb5762c9cc5e3335d0e5f1bb62ff990c623.zip
Auto merge of #122603 - estebank:clone-o-rama, r=lcnr
Detect borrow checker errors where `.clone()` would be an appropriate user action

When a value is moved twice, suggest cloning the earlier move:

```
error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait
  --> $DIR/union-move.rs:49:18
   |
LL |         move_out(x.f1_nocopy);
   |                  ^^^^^^^^^^^
   |                  |
   |                  cannot move out of here
   |                  move occurs because `x.f1_nocopy` has type `ManuallyDrop<RefCell<i32>>`, which does not implement the `Copy` trait
   |
help: consider cloning the value if the performance cost is acceptable
   |
LL |         move_out(x.f1_nocopy.clone());
   |                             ++++++++
```

When a value is borrowed by an `fn` call, consider if cloning the result of the call would be reasonable, and suggest cloning that, instead of the argument:

```
error[E0505]: cannot move out of `a` because it is borrowed
  --> $DIR/variance-issue-20533.rs:53:14
   |
LL |         let a = AffineU32(1);
   |             - binding `a` declared here
LL |         let x = bat(&a);
   |                     -- borrow of `a` occurs here
LL |         drop(a);
   |              ^ move out of `a` occurs here
LL |         drop(x);
   |              - borrow later used here
   |
help: consider cloning the value if the performance cost is acceptable
   |
LL |         let x = bat(&a).clone();
   |                        ++++++++
```

otherwise, suggest cloning the argument:

```
error[E0505]: cannot move out of `a` because it is borrowed
  --> $DIR/variance-issue-20533.rs:59:14
   |
LL |         let a = ClonableAffineU32(1);
   |             - binding `a` declared here
LL |         let x = foo(&a);
   |                     -- borrow of `a` occurs here
LL |         drop(a);
   |              ^ move out of `a` occurs here
LL |         drop(x);
   |              - borrow later used here
   |
help: consider cloning the value if the performance cost is acceptable
   |
LL -         let x = foo(&a);
LL +         let x = foo(a.clone());
   |
```

This suggestion doesn't attempt to square out the types between what's cloned and what the `fn` expects, to allow the user to make a determination on whether to change the `fn` call or `fn` definition themselves.

Special case move errors caused by `FnOnce`:

```
error[E0382]: use of moved value: `blk`
  --> $DIR/once-cant-call-twice-on-heap.rs:8:5
   |
LL | fn foo<F:FnOnce()>(blk: F) {
   |                    --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait
LL |     blk();
   |     ----- `blk` moved due to this call
LL |     blk();
   |     ^^^ value used here after move
   |
note: `FnOnce` closures can only be called once
  --> $DIR/once-cant-call-twice-on-heap.rs:6:10
   |
LL | fn foo<F:FnOnce()>(blk: F) {
   |          ^^^^^^^^ `F` is made to be an `FnOnce` closure here
LL |     blk();
   |     ----- this value implements `FnOnce`, which causes it to be moved when called
```

Account for redundant `.clone()` calls in resulting suggestions:

```
error[E0507]: cannot move out of dereference of `S`
  --> $DIR/needs-clone-through-deref.rs:15:18
   |
LL |         for _ in self.clone().into_iter() {}
   |                  ^^^^^^^^^^^^ ----------- value moved due to this method call
   |                  |
   |                  move occurs because value has type `Vec<usize>`, which does not implement the `Copy` trait
   |
note: `into_iter` takes ownership of the receiver `self`, which moves value
  --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: you can `clone` the value and consume it, but this might not be your desired behavior
   |
LL |         for _ in <Vec<usize> as Clone>::clone(&self).into_iter() {}
   |                  ++++++++++++++++++++++++++++++    ~
```

We use the presence of `&mut` values in a move error as a proxy for the user caring about side effects, so we don't emit a clone suggestion in that case:

```
error[E0505]: cannot move out of `s` because it is borrowed
  --> $DIR/borrowck-overloaded-index-move-index.rs:53:7
   |
LL |     let mut s = "hello".to_string();
   |         ----- binding `s` declared here
LL |     let rs = &mut s;
   |              ------ borrow of `s` occurs here
...
LL |     f[s] = 10;
   |       ^ move out of `s` occurs here
...
LL |     use_mut(rs);
   |             -- borrow later used here
```

We properly account for `foo += foo;` errors where we *don't* suggest `foo.clone() += foo;`, instead suggesting `foo += foo.clone();`.

---

Each commit can be reviewed in isolation. There are some "cleanup" commits, but kept them separate in order to show *why* specific changes were being made, and their effect on tests' output.

Fix #49693, CC #64167.
Diffstat (limited to 'tests/ui/error-codes')
-rw-r--r--tests/ui/error-codes/E0504.stderr6
-rw-r--r--tests/ui/error-codes/E0505.stderr6
-rw-r--r--tests/ui/error-codes/E0507.stderr5
-rw-r--r--tests/ui/error-codes/E0508-fail.stderr5
-rw-r--r--tests/ui/error-codes/E0508.stderr5
-rw-r--r--tests/ui/error-codes/E0509.stderr5
6 files changed, 32 insertions, 0 deletions
diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr
index c8a48961cb3..900cb706bd9 100644
--- a/tests/ui/error-codes/E0504.stderr
+++ b/tests/ui/error-codes/E0504.stderr
@@ -13,6 +13,12 @@ LL |         println!("child function: {}", fancy_num.num);
 ...
 LL |     println!("main function: {}", fancy_ref.num);
    |                                   ------------- borrow later used here
+   |
+note: if `FancyNum` implemented `Clone`, you could clone the value
+  --> $DIR/E0504.rs:1:1
+   |
+LL | struct FancyNum {
+   | ^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr
index 250680d2c1c..ce01298a70d 100644
--- a/tests/ui/error-codes/E0505.stderr
+++ b/tests/ui/error-codes/E0505.stderr
@@ -10,6 +10,12 @@ LL |         eat(x);
    |             ^ move out of `x` occurs here
 LL |         _ref_to_val.use_ref();
    |         ----------- borrow later used here
+   |
+note: if `Value` implemented `Clone`, you could clone the value
+  --> $DIR/E0505.rs:1:1
+   |
+LL | struct Value {}
+   | ^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0507.stderr b/tests/ui/error-codes/E0507.stderr
index 767fedfccbf..60a4daa9d38 100644
--- a/tests/ui/error-codes/E0507.stderr
+++ b/tests/ui/error-codes/E0507.stderr
@@ -11,6 +11,11 @@ note: `TheDarkKnight::nothing_is_true` takes ownership of the receiver `self`, w
    |
 LL |     fn nothing_is_true(self) {}
    |                        ^^^^
+note: if `TheDarkKnight` implemented `Clone`, you could clone the value
+  --> $DIR/E0507.rs:3:1
+   |
+LL | struct TheDarkKnight;
+   | ^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0508-fail.stderr b/tests/ui/error-codes/E0508-fail.stderr
index 1153b1d09c7..96d3bcb67a5 100644
--- a/tests/ui/error-codes/E0508-fail.stderr
+++ b/tests/ui/error-codes/E0508-fail.stderr
@@ -7,6 +7,11 @@ LL |     let _value = array[0];
    |                  cannot move out of here
    |                  move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait
    |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/E0508-fail.rs:1:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let _value = &array[0];
diff --git a/tests/ui/error-codes/E0508.stderr b/tests/ui/error-codes/E0508.stderr
index 4c864e24144..c1b622e2432 100644
--- a/tests/ui/error-codes/E0508.stderr
+++ b/tests/ui/error-codes/E0508.stderr
@@ -7,6 +7,11 @@ LL |     let _value = array[0];
    |                  cannot move out of here
    |                  move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait
    |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/E0508.rs:1:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let _value = &array[0];
diff --git a/tests/ui/error-codes/E0509.stderr b/tests/ui/error-codes/E0509.stderr
index 59843a5491a..75c372d0440 100644
--- a/tests/ui/error-codes/E0509.stderr
+++ b/tests/ui/error-codes/E0509.stderr
@@ -7,6 +7,11 @@ LL |     let fancy_field = drop_struct.fancy;
    |                       cannot move out of here
    |                       move occurs because `drop_struct.fancy` has type `FancyNum`, which does not implement the `Copy` trait
    |
+note: if `FancyNum` implemented `Clone`, you could clone the value
+  --> $DIR/E0509.rs:1:1
+   |
+LL | struct FancyNum {
+   | ^^^^^^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let fancy_field = &drop_struct.fancy;