Skip to content

Conversation

ada4a
Copy link
Contributor

@ada4a ada4a commented Aug 5, 2025

This PR:

  • adds structured suggestions to all 3 lint scenarios
  • adds more examples of linted patterns, and recommended fixes
  • moves some test cases to _unfixable.rs, to allow running rustfix on the main file
  • stops the lint from firing multiple times on types like &mut &mut &mut T

Open questions:

  • I'd like to add a note explaining why chained &mut are useless.. but can't really come up with something consice. I very much don't like the one in the docs -- it's a bit too condescending imo.
  • see comments in the diff for more

I do realize that the PR ended up being quite wide-scoped, but that's primarily because once I added structured suggestions to one of the lint cases, ui_test started complaining about warnings remaining after the rustfix run, which of course had to with the fact that the other two lint cases didn't actually fix the code they linted, only warned. I guess ui_test could be smarter about this, by understanding that if a warning was created without a suggestion, then that warning will still remain in the fixed file. But oh well.

changelog: [mut_mut]: add structured suggestions, improve docs

fixes #13021

@rustbot
Copy link
Collaborator

rustbot commented Aug 5, 2025

r? @samueltardieu

rustbot has assigned @samueltardieu.
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

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Aug 5, 2025
//~| mut_mut
}

let mut z = inline!(&mut $(&mut 3u32));
Copy link
Contributor Author

@ada4a ada4a Aug 5, 2025

Choose a reason for hiding this comment

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

I stopped the lint from firing when inside macros, because the $(&mut 3u32) thing caused a bad suggestion:

&mut 3u32mut $(&mut u32)

should I try bringing the suggestion back, and if so, how?

"this expression mutably borrows a mutable reference. Consider reborrowing",
"this expression mutably borrows a mutable reference",
|diag| {
diag.span_suggestion(expr.span, "reborrow instead", sugg, applicability);
Copy link
Contributor Author

@ada4a ada4a Aug 5, 2025

Choose a reason for hiding this comment

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

From my experience, not many people know about reborrowing -- maybe the message for the case that recommends it should link to some page that explains it?

Admittedly this is less necessary now that we've got a structured suggestion; people can just search for the term separately if they're interested

Copy link
Member

Choose a reason for hiding this comment

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

An important property of Clippy is that we have an unique position and people learn from Clippy's output. So, they (ideally) come to Clippy with a learner mindset.

With the suggestion, they'll assume that adding &* is called "reborrowing"

expr.hir_id,
expr.span,
"generally you want to avoid `&mut &mut _` if possible",
"an expression of form `&mut &mut _`",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

is this message helpful enough?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I'd say that its helpful enough. What do you have in mind?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well it now just states the fact, not really explaining why having double &muts is bad. But I also can't come up with a concise way to explain why it's bad, probably because I can't really imagine how one would end up with such code – unlike the "&mut expr where expr itself has a type &mut T" case, which can happen because you forget the type of expr

Copy link

github-actions bot commented Aug 5, 2025

Lintcheck changes for dc534c4

Lint Added Removed Changed
clippy::mut_mut 0 0 11

This comment will be updated if you push new changes

@ada4a ada4a force-pushed the mut-mut branch 2 times, most recently from a81c9a6 to c26eb82 Compare August 5, 2025 12:57
@ada4a
Copy link
Contributor Author

ada4a commented Aug 5, 2025

@cmrschwarz let me know what you think! You can see the change in the diagnostic messages in the .stderr files

@cmrschwarz
Copy link

@cmrschwarz let me know what you think! You can see the change in the diagnostic messages in the .stderr files

Just looking at the structured suggestions, this looks exactly like what I hoped for.
Thank you for working on this!

@ada4a
Copy link
Contributor Author

ada4a commented Aug 12, 2025

Reviewer unavailable, so rerolling

r? clippy

@ada4a
Copy link
Contributor Author

ada4a commented Sep 7, 2025

@blyxyas friendly ping in case this flew under the radar^^

@blyxyas
Copy link
Member

blyxyas commented Sep 7, 2025

No, this hasn't flown through the radar Currently reviewing this.

Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

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

Great starting patch!

View changes since this review

Comment on lines +134 to +142
loop {
if !e.span.eq_ctxt(e2.span) {
return;
}
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, next) = e2.kind {
(e, e2) = (e2, next);
many_muts = true;
} else {
break;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Could you extract these two blocks into a function? The !e.span.eq_ctxt(.. call can be extracted into a closure passed to the function, being that we checking for context changes in types is not really useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean in order to share the peeling logic with the types case? But wouldn't that be impossible, given that we peel ExprKinds here, but TyKinds there?

let (mut e, mut e2) = (e, e2);
let mut many_muts = false;
loop {
if !e.span.eq_ctxt(e2.span) {
Copy link
Member

Choose a reason for hiding this comment

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

(Not a problem, just record-keeping)

One might think that it's an improvement to use SpanData instead of Span, so we don't get the SpanData twice per each span (one for e2 being the argument, and once for it being the object that the method is called on).

But being that there aren't almost any occurences of /&mut &mut (&mut)+/, it's probably better to avoid the cost of getting the two initial spans and pay the price at higher-mut sites (which aren't very frequent).

expr.hir_id,
expr.span,
"generally you want to avoid `&mut &mut _` if possible",
"an expression of form `&mut &mut _`",
Copy link
Member

Choose a reason for hiding this comment

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

Yes, I'd say that its helpful enough. What do you have in mind?

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Sep 11, 2025
@rustbot

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Sep 28, 2025

This PR was rebased onto a different master 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.

Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

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

LGTM, thanks! ❤️

View changes since this review

"this expression mutably borrows a mutable reference. Consider reborrowing",
"this expression mutably borrows a mutable reference",
|diag| {
diag.span_suggestion(expr.span, "reborrow instead", sugg, applicability);
Copy link
Member

Choose a reason for hiding this comment

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

An important property of Clippy is that we have an unique position and people learn from Clippy's output. So, they (ideally) come to Clippy with a learner mindset.

With the suggestion, they'll assume that adding &* is called "reborrowing"

@blyxyas blyxyas added this pull request to the merge queue Sep 29, 2025
Merged via the queue into rust-lang:master with commit 98a92bf Sep 29, 2025
11 checks passed
@ada4a ada4a deleted the mut-mut branch September 29, 2025 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mut_mut complains when borrowing from a slice of mutable references
5 participants