-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detect borrow checker errors where .clone()
would be an appropriate user action
#122603
Conversation
This comment has been minimized.
This comment has been minimized.
☔ The latest upstream changes (presumably #121652) made this pull request unmergeable. Please resolve the merge conflicts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
went through this PR and it mostly looks good.
I ended up only scimming over some of the functions as if let chains with 10 components are difficult to read.
nits, then r=me
@@ -977,6 +977,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | |||
can_suggest_clone | |||
} | |||
|
|||
pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option<Span> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add a doc comment, I don't fully understand this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's poinit is to detect x.clone()
calls where the type of x
matches the return type which is caused by an auto-ref?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, correct.
&& let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) | ||
&& let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) | ||
&& rcvr_ty == expr_ty | ||
&& segment.ident.name == sym::clone |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is clone
not a diagnostics item?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, but the res in the method call is Err
for these cases :-/
@@ -3,10 +3,10 @@ | |||
// fn body, causing this (invalid) code to be accepted. | |||
|
|||
pub trait Foo<'a> { | |||
type Bar; | |||
type Bar: Clone; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that the suggestion denormalise(&x).clone()
would actually come up, otherwise it would suggest denormalise(x.clone())
, and I wanted to verify that logic worked with associated types too (checking that the return type of denormalise
could be cloned and side step the longer borrow of x
).
``` error[E0507]: cannot move out of `*x` which is behind a shared reference --> $DIR/borrowck-fn-in-const-a.rs:6:16 | LL | return *x | ^^ move occurs because `*x` has type `String`, which does not implement the `Copy` trait | help: consider cloning the value if the performance cost is acceptable | LL - return *x LL + return x.clone() | ```
``` error[E0507]: cannot move out of `val`, a captured variable in an `FnMut` closure --> $DIR/issue-87456-point-to-closure.rs:10:28 | LL | let val = String::new(); | --- captured outer variable LL | LL | take_mut(|| { | -- captured by this `FnMut` closure LL | LL | let _foo: String = val; | ^^^ move occurs because `val` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here | LL | let _foo: String = &val; | + help: consider cloning the value if the performance cost is acceptable | LL | let _foo: String = val.clone(); | ++++++++ ```
…ument ``` error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:28:14 | LL | let a = AffineU32(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).clone(); | ++++++++ ```
Explicitly look for `expr += other_expr;` and avoid suggesting `expr.clone() += other_expr;`, instead suggesting `expr += other_expr.clone();`.
``` 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 ```
We attempt to suggest an appropriate clone for move errors on expressions like `S { ..s }` where a field isn't `Copy`. If we can't suggest, we still don't emit the incorrect suggestion of `S { ..s }.clone()`. ``` error[E0509]: cannot move out of type `S<K>`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:28:19 | LL | let _s2 = S { a: 2, ..s0 }; | ^^^^^^^^^^^^^^^^ | | | cannot move out of here | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait | help: clone the value from the field instead of using the spread operator syntax | LL | let _s2 = S { a: 2, c: s0.c.clone(), ..s0 }; | +++++++++++++++++ ``` ``` error[E0509]: cannot move out of type `S<()>`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:20:19 | LL | let _s2 = S { a: 2, ..s0 }; | ^^^^^^^^^^^^^^^^ | | | cannot move out of here | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait | note: `B` doesn't implement `Copy` or `Clone` --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 | LL | struct B; | ^^^^^^^^ help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax | LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; | +++++++++++++++++ ```
Added comments and reworded messages
@bors r=lcnr Addressed the comments, approving as per #122603 (review) |
☀️ Test successful - checks-actions |
1 similar comment
☀️ Test successful - checks-actions |
Finished benchmarking commit (6eaa7fb): comparison URL. Overall result: ✅ improvements - no action needed@rustbot label: -perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)This benchmark run did not return any relevant results for this metric. CyclesThis benchmark run did not return any relevant results for this metric. Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 676.802s -> 675.908s (-0.13%) |
When a value is moved twice, suggest cloning the earlier move:
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:otherwise, suggest cloning the argument:
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 thefn
call orfn
definition themselves.Special case move errors caused by
FnOnce
:Account for redundant
.clone()
calls in resulting suggestions: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:We properly account for
foo += foo;
errors where we don't suggestfoo.clone() += foo;
, instead suggestingfoo += 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.