Skip to content

Avoid redundant clone suggestions in borrowck diagnostics#154197

Merged
rust-bors[bot] merged 2 commits intorust-lang:mainfrom
yuk1ty:fix-redundant-clone-error2
Apr 25, 2026
Merged

Avoid redundant clone suggestions in borrowck diagnostics#154197
rust-bors[bot] merged 2 commits intorust-lang:mainfrom
yuk1ty:fix-redundant-clone-error2

Conversation

@yuk1ty
Copy link
Copy Markdown
Contributor

@yuk1ty yuk1ty commented Mar 22, 2026

View all comments

Fixes #153886

Removed redundant .clone() suggestions.

I found that there are two patterns to handle this issue while I was implementing:

  • Should suggest only UFCS
  • Should suggest only simple .clone()

For the target issue, we can just remove the UFCS (<Option<String> as Clone>::clone(&selection.1)) side.

However, for the BorrowedContentSource::OverloadedDeref pattern like Rc<Vec<i32>>, for instance the borrowck-move-out-of-overloaded-auto-deref.rs test case, I think we need to employ the UFCS way. The actual test case is:

//@ run-rustfix
use std::rc::Rc;

pub fn main() {
    let _x = Rc::new(vec![1, 2]).into_iter();
    //~^ ERROR [E0507]
}

And another error will be shown if we simply use the simple .clone() pattern. Like:

use std::rc::Rc;

pub fn main() {
    let _x = Rc::new(vec![1, 2]).clone().into_iter();
}

then we will get

error[E0507]: cannot move out of an `Rc`
   --> src/main.rs:5:14
    |
  5 |     let _x = Rc::new(vec![1, 2]).clone().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ----------- value moved due to this method call
    |              |
    |              move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
    |
note: `into_iter` takes ownership of the receiver `self`, which moves value
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:310:18
    |
310 |     fn into_iter(self) -> Self::IntoIter;
    |                  ^^^^
help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
  5 -     let _x = Rc::new(vec![1, 2]).clone().into_iter();
  5 +     let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
    |

For more information about this error, try `rustc --explain E0507`.

Rust Playground

In this case, Rc::clone only increments the reference count and returns a new Rc<Vec<i32>>; it does not grant ownership of the inner Vec<i32>. As a result, calling into_iter() attempts to move the Vec<i32>, leading to the same E0507 error again.

On the other hand, in UFCS form:

<Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter()

This explicitly calls <Vec<i32> as Clone>::clone, and the argument &Rc<Vec<i32>> is treated as &Vec<i32> via Rc’s Deref implementation. As a result, the Vec<i32> itself is cloned, yielding an owned Vec<i32>, which allows into_iter() to succeed, if my understanding is correct.

I addressed the issue as far as I could find the edge cases but please advice me if I'm overlooking something.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 22, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 22, 2026

r? @adwinwhite

rustbot has assigned @adwinwhite.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: borrowck, compiler
  • borrowck, compiler expanded to 69 candidates
  • Random selection from 13 candidates

@rust-log-analyzer

This comment has been minimized.

@yuk1ty
Copy link
Copy Markdown
Contributor Author

yuk1ty commented Mar 22, 2026

The failed tests tell me another case of the issue. Let me mark PR as draft.

@yuk1ty yuk1ty marked this pull request as draft March 22, 2026 05:04
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 22, 2026
@yuk1ty yuk1ty force-pushed the fix-redundant-clone-error2 branch from e986806 to 9020069 Compare March 22, 2026 05:59
@yuk1ty yuk1ty marked this pull request as ready for review March 22, 2026 08:40
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Mar 22, 2026
/// Tracks which suggestions were emitted by [`MirBorrowckCtxt::explain_captures`],
/// so callers can avoid emitting redundant suggestions downstream.
struct ExplainCapturesResult {
clone_suggestion: CloneSuggestion,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's fine to use bool here instead of enum

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yuk1ty yuk1ty force-pushed the fix-redundant-clone-error2 branch from 9020069 to 1c123db Compare March 22, 2026 14:16
@yuk1ty yuk1ty requested a review from Kivooeo March 23, 2026 00:36
@Kivooeo
Copy link
Copy Markdown
Member

Kivooeo commented Mar 23, 2026

I'll possibly take a look today or tomorrow. I'm rather busy at the moment, so I wouldn't expect this to be that quick, to be honest.

@adwinwhite
Copy link
Copy Markdown
Contributor

It seems that @Kivooeo is quite busy. I'll have a look.

@adwinwhite
Copy link
Copy Markdown
Contributor

Some nits, otherwise it's a very nice improvement. Thank you for working on this!

@adwinwhite
Copy link
Copy Markdown
Contributor

adwinwhite commented Mar 31, 2026

This PR also improves the diag for the case below, I think.
Could you add this as a test if it's confirmed to be fixed?

use std::ops::Deref;

struct MyBox<T>(T);

impl<T> Deref for MyBox<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

pub fn main() {
    let _x = MyBox(vec![1, 2]).into_iter();
    // currently we have a simple clone help which is wrong. it's better to have UFCS.
}

@adwinwhite
Copy link
Copy Markdown
Contributor

@rustbot author

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 31, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 31, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Mar 31, 2026
@yuk1ty
Copy link
Copy Markdown
Contributor Author

yuk1ty commented Apr 2, 2026

Thank you! Let me try 👍

Copy link
Copy Markdown
Contributor

@adwinwhite adwinwhite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, forgot to submit inline reviews.

View changes since this review

self.explain_captures(
// The return value indicates whether a clone suggestion was emitted;
// redundancy is already handled via the `FnSelfUse` check below.
let _ = self.explain_captures(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a real result. I think the wildcard binding is unnecessary.

moved_place: Place<'tcx>,
msg_opt: CapturedMessageOpt,
) {
) -> ExplainCapturesResult {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to use a simple enum as return type? For readability and less indirect wrapper.

span: Span,
use_spans: Option<UseSpans<'tcx>>,
) -> Diag<'infcx> {
) -> (Diag<'infcx>, bool) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't really get what the bool means from the fn signature.

@yuk1ty
Copy link
Copy Markdown
Contributor Author

yuk1ty commented Apr 12, 2026

Oh, thank you for the comments! Let me address your comments 👍

@adwinwhite
Copy link
Copy Markdown
Contributor

Final nits: Could you bless and add a .fixed file for the redundant-clone-suggection case?
And rename it to something like ufcs-for-deref-inner-clone? It didn't ever have redundant suggestions.

After squashing all commits, r=me

@adwinwhite
Copy link
Copy Markdown
Contributor

@bors delegate+

@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented Apr 20, 2026

✌️ @yuk1ty, you can now approve this pull request!

If @adwinwhite told you to "r=me" after making some further change, then please make that change and post @bors r=adwinwhite.

@yuk1ty yuk1ty force-pushed the fix-redundant-clone-error2 branch from 5451a7a to 958cd36 Compare April 24, 2026 13:59
@rustbot

This comment has been minimized.

@yuk1ty yuk1ty force-pushed the fix-redundant-clone-error2 branch from 958cd36 to 5409599 Compare April 24, 2026 14:04
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 24, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rust-log-analyzer

This comment has been minimized.

@yuk1ty yuk1ty force-pushed the fix-redundant-clone-error2 branch from 5409599 to 61ff157 Compare April 24, 2026 23:46
@yuk1ty
Copy link
Copy Markdown
Contributor Author

yuk1ty commented Apr 25, 2026

@bors r=me

@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented Apr 25, 2026

📌 Commit 61ff157 has been approved by me

It is now in the queue for this repository.

⚠️ The following reviewer(s) could not be found: me

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 25, 2026
@adwinwhite
Copy link
Copy Markdown
Contributor

@bors r-

@rust-bors rust-bors Bot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Apr 25, 2026
@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented Apr 25, 2026

This pull request was unapproved.

View changes since this unapproval

@rust-bors rust-bors Bot removed the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Apr 25, 2026
@adwinwhite
Copy link
Copy Markdown
Contributor

@yuk1ty the name should adwinwhite rather than literally me.

@bors r+ rollup

@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented Apr 25, 2026

📌 Commit 61ff157 has been approved by adwinwhite

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 25, 2026
jhpratt added a commit to jhpratt/rust that referenced this pull request Apr 25, 2026
… r=adwinwhite

Avoid redundant clone suggestions in borrowck diagnostics

Fixes rust-lang#153886

Removed redundant `.clone()` suggestions.

I found that there are two patterns to handle this issue while I was implementing:

- Should suggest only UFCS
- Should suggest only simple `.clone()`

For the target issue, we can just remove the UFCS (`<Option<String> as Clone>::clone(&selection.1)`) side.

However, for the `BorrowedContentSource::OverloadedDeref` pattern like `Rc<Vec<i32>>`, for instance the `borrowck-move-out-of-overloaded-auto-deref.rs` test case, I think we need to employ the UFCS way. The actual test case is:

```rust
//@ run-rustfix
use std::rc::Rc;

pub fn main() {
    let _x = Rc::new(vec![1, 2]).into_iter();
    //~^ ERROR [E0507]
}
```

And another error will be shown if we simply use the simple `.clone()` pattern. Like:

```rust
use std::rc::Rc;

pub fn main() {
    let _x = Rc::new(vec![1, 2]).clone().into_iter();
}
```

then we will get

```
error[E0507]: cannot move out of an `Rc`
   --> src/main.rs:5:14
    |
  5 |     let _x = Rc::new(vec![1, 2]).clone().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ----------- value moved due to this method call
    |              |
    |              move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
    |
note: `into_iter` takes ownership of the receiver `self`, which moves value
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:310:18
    |
310 |     fn into_iter(self) -> Self::IntoIter;
    |                  ^^^^
help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
  5 -     let _x = Rc::new(vec![1, 2]).clone().into_iter();
  5 +     let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
    |

For more information about this error, try `rustc --explain E0507`.
```

[Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=7e767bed3f1c573c03642f20f454ed03)

In this case, `Rc::clone` only increments the reference count and returns a new `Rc<Vec<i32>>`; it does not grant ownership of the inner `Vec<i32>`. As a result, calling into_iter() attempts to move the `Vec<i32>`, leading to the same E0507 error again.

On the other hand, in UFCS form:

```
<Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter()
```

This explicitly calls `<Vec<i32> as Clone>::clone`, and the argument `&Rc<Vec<i32>>` is treated as `&Vec<i32>` via Rc’s `Deref` implementation. As a result, the `Vec<i32>` itself is cloned, yielding an owned `Vec<i32>`, which allows `into_iter()` to succeed, if my understanding is correct.

I addressed the issue as far as I could find the edge cases but please advice me if I'm overlooking something.
rust-bors Bot pushed a commit that referenced this pull request Apr 25, 2026
Rollup of 7 pull requests

Successful merges:

 - #155643 (Improve suggestion for $-prefixed fragment specifiers)
 - #154197 (Avoid redundant clone suggestions in borrowck diagnostics)
 - #154372 (Exposing Float Masks)
 - #155680 (Handle index projections in call destinations in DSE)
 - #155732 (bootstrap: Don't clone submodules unconditionally in dry-run)
 - #155737 (Account for `GetSyntheticValue` failures)
 - #155738 (Pass fields to `is_tuple_fields` instead of `SBValue` object)
@rust-bors rust-bors Bot merged commit 0af72af into rust-lang:main Apr 25, 2026
11 checks passed
@rustbot rustbot added this to the 1.97.0 milestone Apr 25, 2026
rust-timer added a commit that referenced this pull request Apr 25, 2026
Rollup merge of #154197 - yuk1ty:fix-redundant-clone-error2, r=adwinwhite

Avoid redundant clone suggestions in borrowck diagnostics

Fixes #153886

Removed redundant `.clone()` suggestions.

I found that there are two patterns to handle this issue while I was implementing:

- Should suggest only UFCS
- Should suggest only simple `.clone()`

For the target issue, we can just remove the UFCS (`<Option<String> as Clone>::clone(&selection.1)`) side.

However, for the `BorrowedContentSource::OverloadedDeref` pattern like `Rc<Vec<i32>>`, for instance the `borrowck-move-out-of-overloaded-auto-deref.rs` test case, I think we need to employ the UFCS way. The actual test case is:

```rust
//@ run-rustfix
use std::rc::Rc;

pub fn main() {
    let _x = Rc::new(vec![1, 2]).into_iter();
    //~^ ERROR [E0507]
}
```

And another error will be shown if we simply use the simple `.clone()` pattern. Like:

```rust
use std::rc::Rc;

pub fn main() {
    let _x = Rc::new(vec![1, 2]).clone().into_iter();
}
```

then we will get

```
error[E0507]: cannot move out of an `Rc`
   --> src/main.rs:5:14
    |
  5 |     let _x = Rc::new(vec![1, 2]).clone().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ----------- value moved due to this method call
    |              |
    |              move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
    |
note: `into_iter` takes ownership of the receiver `self`, which moves value
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:310:18
    |
310 |     fn into_iter(self) -> Self::IntoIter;
    |                  ^^^^
help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
  5 -     let _x = Rc::new(vec![1, 2]).clone().into_iter();
  5 +     let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
    |

For more information about this error, try `rustc --explain E0507`.
```

[Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=7e767bed3f1c573c03642f20f454ed03)

In this case, `Rc::clone` only increments the reference count and returns a new `Rc<Vec<i32>>`; it does not grant ownership of the inner `Vec<i32>`. As a result, calling into_iter() attempts to move the `Vec<i32>`, leading to the same E0507 error again.

On the other hand, in UFCS form:

```
<Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter()
```

This explicitly calls `<Vec<i32> as Clone>::clone`, and the argument `&Rc<Vec<i32>>` is treated as `&Vec<i32>` via Rc’s `Deref` implementation. As a result, the `Vec<i32>` itself is cloned, yielding an owned `Vec<i32>`, which allows `into_iter()` to succeed, if my understanding is correct.

I addressed the issue as far as I could find the edge cases but please advice me if I'm overlooking something.
@yuk1ty
Copy link
Copy Markdown
Contributor Author

yuk1ty commented Apr 25, 2026

@adwinwhite I'm sorry I misunderstood. Thank you for your support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redundant clone suggestion in move error

5 participants