Skip to content

Conversation

ada4a
Copy link
Contributor

@ada4a ada4a commented Sep 21, 2025

Fixes #15722

changelog: [unnecessary_mut_passed]: retain parens around the arguments

The lint was probably renamed at some point, but the files weren't. This
made it annoying to search for the lint.
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Sep 21, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 21, 2025

r? @Alexendoo

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

@ada4a ada4a force-pushed the needless_mut_passed branch from b6ec641 to 3d5b2ac Compare September 21, 2025 21:37
@ada4a ada4a marked this pull request as draft September 22, 2025 20:03
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Sep 22, 2025
@ada4a ada4a force-pushed the needless_mut_passed branch from 3d5b2ac to cb01a29 Compare September 23, 2025 11:09
@ada4a ada4a marked this pull request as ready for review September 23, 2025 11:09
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Sep 23, 2025
@ada4a ada4a changed the title fix(unnecessary_mut_passed): retain receiver parens in (&mut t).method() fix(unnecessary_mut_passed): retain parens around the arguments Sep 23, 2025
@ada4a ada4a force-pushed the needless_mut_passed branch from cb01a29 to 1ddb6fe Compare September 23, 2025 11:10
Comment on lines 93 to 96
let mut sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr();
if argument.span.check_source_text(cx, |src| has_enclosing_paren(src)) {
sugg = sugg.maybe_paren();
}
Copy link
Member

Choose a reason for hiding this comment

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

Instead of building up a suggestion string as it does currently it would be nice to span only the mut part of &mut and replace it with "" to improve the diagnostic

I'm happy to merge the PR as is though, just let me know either way

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did try doing that:

Implementation

let span_to_remove = {
    let span_until_arg = argument.span.until(arg.span);
    let Some(ref_pos) =
        span_until_arg.with_source_text(cx, |src| src.find('&').expect("a reference must contain `&`"))
    else {
        return;
    };
    let lo = ref_pos + '&'.len_utf8();
    span_until_arg.with_lo(BytePos::from_usize(lo))
};

span_lint_and_then(
    cx,
    UNNECESSARY_MUT_PASSED,
    argument.span,
    format!("the {fn_kind} `{name}` doesn't need a mutable reference"),
    |diag| {
        diag.span_suggestion(span_to_remove, "remove this `mut`", String::new(), applicability);
    },
);

But:

  • The diagnostic ended up looking just a bit too crowded imo?
    error: the method `takes_ref` doesn't need a mutable reference
      --> tests/ui/unnecessary_mut_passed.rs:188:25
       |
    LL |     my_struct.takes_ref((&mut 42));
       |                         ^^----^^^
       |                           |
       |                           help: remove this `mut`
    
  • More importantly, diving into the source is inherently brittle I think. For example, if we add handling of &raw references in the future, the implementation above would start eating the raw part as well, which would of course be no good. That said, building the expression from scratch is not hugely better -- it will, in theory, destroy all the custom formatting and comments -- though to be fair one wouldn't really expect to see either of these in this context.

Given all that, I'm kind of undecided, maybe leaning a bit towards the "cut out the mut" approach -- WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

If you ever see a crowded diagnostic swap it out for a span_suggestion_verbose, long term that's intended to be the default (rust-lang/rust#141973)

Yeah it's unfortunate it can't be done solely with spans, but has_enclosing_paren also requires examing the source I guess. You could do .find("&mut") to guarantee you've found it

I wouldn't worry about a future addition of &raw handling as they'd have to adapt it either way

Copy link
Contributor Author

@ada4a ada4a Sep 24, 2025

Choose a reason for hiding this comment

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

long term that's intended to be the default (rust-lang/rust#141973)

Good to know!

You could do .find("&mut") to guarantee you've found it

I'm a bit worried that that would make us miss weirdly formatted things. I could add a debug assertion that the part after & does actually contain a mut, but I wish we could issue some kind of a warning about that even in release builds.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh no, apparently the implementation above completely wrecks ui_test and/or rustfix:

error: failed to apply suggestions for tests/ui/unnecessary_mut_passed.rs with rustfix
could not replace range 1..1584, maybe parts of it were already replaced?
Add //@no-rustfix to the test file to ignore rustfix suggestions

full stderr:
<JSON representation of the error, for some bizarre reason>

At first I thought that that was due to the test cases with multiple suggestions on the same line, like (&mut my_struct).takes_ref(&mut 42);, but even removing that doesn't help.

Any ideas?...

Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit worried that that would make us miss weirdly formatted things

I thought &mut was one token but yeah sure you could look for mut instead since & mut is valid apparently

I could add a debug assertion that the part after & does actually contain a mut, but I wish we could issue some kind of a warning about that even in release builds.

We don't want to panic when the source doesn't seem to make sense because it often doesn't in the presence of proc macros that use set_span, checking for the actual mut token does ensure that we don't lint in those cases also

1..1584 sounds like a pretty large range, I would guess something went wrong with a span calculation

Copy link
Contributor Author

@ada4a ada4a Sep 24, 2025

Choose a reason for hiding this comment

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

We don't want to panic when the source doesn't seem to make sense because it often doesn't in the presence of proc macros that use set_span, checking for the actual mut token does ensure that we don't lint in those cases also

Makes sense

1..1584 sounds like a pretty large range, I would guess something went wrong with a span calculation

Not only is the span wrong, but it isn't even on the right file?... Here's the JSON representation of the very first suggestion: (EDIT2: figured it out, see below)

{
  "message": "remove this `mut`",
  "code": null,
  "level": "help",
  "spans": [
    {
      "file_name": "/home/ada4a/dev/minor/clippy/tests/clippy.toml",
      "byte_start": 1,
      "byte_end": 1524,
      "line_start": 1,
      "line_end": 57,
      "column_start": 2,
      "column_end": 20,
      "is_primary": true,
      "text": [],
      "label": null,
      "suggested_replacement": "",
      "suggestion_applicability": "MachineApplicable",
      "expansion": null
    }
  ],
  "children": [],
  "rendered": null
}

Huh?...

EDIT: the diagnostic span is at least in the right file though:

{
  "$message_type": "diagnostic",
  "message": "the function `takes_ref` doesn't need a mutable reference",
  "code": {
    "code": "clippy::unnecessary_mut_passed",
    "explanation": null
  },
  "level": "error",
  "spans": [
    {
      "file_name": "tests/ui/unnecessary_mut_passed.rs",
      "byte_start": 1420,
      "byte_end": 1427,
      "line_start": 57,
      "line_end": 57,
      "column_start": 15,
      "column_end": 22,
      "is_primary": true,
      "text": [
        {
          "text": " takes_ref(&mut 42);",
          "highlight_start": 15,
          "highlight_end": 22
        }
      ],
      "label": null,
      "suggested_replacement": null,
      "suggestion_applicability": null,
      "expansion": null
    }
  ],
  // snip

Copy link
Contributor Author

@ada4a ada4a Sep 24, 2025

Choose a reason for hiding this comment

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

So the reason for the bug was that I was using span.with_lo(1), thinking that 1 is the relative position in the span, but it of course was the absolute position (..in another file -- this part I still don't understand) -- the solution was to use span.split_at(1).1 instead

(Sorry for all the spam, just writing this down so that I hopefully remember it for the future debugging)

@ada4a ada4a force-pushed the needless_mut_passed branch from 1ddb6fe to 9cb82d7 Compare September 24, 2025 17:34
@github-actions
Copy link

Lintcheck changes for 9cb82d7

Lint Added Removed Changed
clippy::unnecessary_mut_passed 0 0 1

This comment will be updated if you push new changes

Copy link
Member

@Alexendoo Alexendoo left a comment

Choose a reason for hiding this comment

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

@Alexendoo Alexendoo added this pull request to the merge queue Sep 27, 2025
Merged via the queue into rust-lang:master with commit 3e218d1 Sep 27, 2025
11 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Sep 27, 2025
This was referenced Sep 27, 2025
@ada4a ada4a deleted the needless_mut_passed branch September 28, 2025 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

suggestion for unnecessary_mut_passed doesn't add surrounding parens

3 participants