Skip to content
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

New Lint: detect unused &mut references in function parameters #5546

Closed
leonardo-m opened this issue Apr 29, 2020 · 9 comments
Closed

New Lint: detect unused &mut references in function parameters #5546

leonardo-m opened this issue Apr 29, 2020 · 9 comments
Labels
A-lint Area: New lints E-hard Call for participation: This a hard problem and requires more experience or effort to work on L-suggestion Lint: Improving, adding or fixing lint suggestions

Comments

@leonardo-m
Copy link

Hi! Clippy in the Rust Playground spots this problem:

fn foo(x: &u32) {
    let _y = x;
}
fn main() {
    foo(&mut 10);
}


warning: The function/method `foo` doesn't need a mutable reference
note: `#[warn(clippy::unnecessary_mut_passed)]` on by default

But it doesn't find the inverted problem here:

fn foo(x: &mut u32) { // This doesn't need to take a mut
    let _y = x;
}
fn main() {
    foo(&mut 10);
}

While this is caught by Rustc too:

fn foo(mut x: u32) {
    let _y = x;
}
fn main() {
    foo(10);
}

warning: variable does not need to be mutable
@flip1995
Copy link
Member

This would be an entirely new lint. Detecting if a mutable reference is actually necessary is way harder, than just detecting if a mutable reference is given to a function, that only takes a shared reference.

@flip1995 flip1995 changed the title Unnecessary mut ref arguments New Lint: detect unused &mut references in function parameters Apr 29, 2020
@flip1995 flip1995 added L-suggestion Lint: Improving, adding or fixing lint suggestions E-hard Call for participation: This a hard problem and requires more experience or effort to work on A-lint Area: New lints labels Apr 29, 2020
@Logarithmus
Copy link

Logarithmus commented Jul 8, 2022

@flip1995 can't clippy just temporary replace &mut with &, test if it still compiles, and if it is, then suggest a lint?

@flip1995
Copy link
Member

flip1995 commented Jul 9, 2022

Sadly not.

@Logarithmus
Copy link

@flip1995 why? Sorry, I'm not familiar with clippy's internals.

@flip1995
Copy link
Member

Maybe a relevant info before the explanation: Clippy doesn't do the compilation. Clippy just calls rustc programmatically and uses the rustc queries to do the analysis. But all the heavy lifting, like parsing, type checking, ... is done by rustc

Clippy can only run one compiler pass. Doing something like this would mean that Clippy has to modify a file on disk and then run the compiler again, and after that revert the modification of the file. Perf concerns aside (we'd probably have to do this multiple times for multiple lints), this would be unexpected behavior of a linter from the users perspective. At least I wouldn't expect that my static analyzer modifies files on disk. rustfix and rustfmt sure, they have to modify files; but the linter... that would be a surprise.

Another option might be to only re-compile the function. But that wouldn't work because we'd had to manually rerun every rustc pass on that function. And rustc is not designed for this. Also specifically in this case, there might also be global concerns, because a function signature is changed.

Don't be sorry when asking questions. It's the best way to learn :)

@Logarithmus
Copy link

Logarithmus commented Jul 11, 2022

@flip1995 thanks for a detailed explanation. You are concerned that modifying source code files might confuse the user. What prevents clippy from copying them to a temp directory or loading into RAM, modifying the copies and then passing to rustc?

@flip1995
Copy link
Member

Clippy would still create files on disk (in /tmp) or would use more RAM. You can't really compile a single file in Rust, like you do in C++. In Rust the smallest compilation unit is a crate (in simplified terms). So you'd have to copy the whole crate. And then you still need the context of the dependencies. At this point we'd almost be rebuilding cargo into Clippy.

I like to be strict here and keep the behavior that Clippy doesn't do anything regarding the file system. (Except for the target/ dir, which is also technically created by cargo/rustc and not by Clippy)

@y21
Copy link
Member

y21 commented May 20, 2023

In unsafe code, &mut self is often necessary even if it would compile with just &self because that would otherwise make it unsound, so I think we should be careful with how the lint interacts with unsafe code and perhaps not lint at all if the body contains any unsafe {}.

For example:

struct LeakyBox<T>(*mut T);
impl<T> LeakyBox<T> {
  pub fn new(v: T) -> Self {
    Self(Box::into_raw(Box::new(v)))
  }

  pub fn get_mut(&self) -> &mut T {
    unsafe { &mut *self.0 }
  }
}

This code compiles fine and LeakyBox::get_mut doesn't require &mut self to compile, but it's unsound, because this one doesn't assert uniqueness over the LeakyBox and lets you obtain two aliasing &muts. So this really should take &mut self.

@y21
Copy link
Member

y21 commented Mar 6, 2024

This lint eixsts now: https://rust-lang.github.io/rust-clippy/master/index.html#/needless_pass_by_ref_mut , implemented in #10900

Output for the reproducer above:

warning: this argument is a mutable reference, but not used mutably
 --> src/main.rs:3:11
  |
3 | fn foo(x: &mut u32) { // This doesn't need to take a mut
  |           ^^^^^^^^ help: consider changing to: `&u32`
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut

@y21 y21 closed this as completed Mar 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints E-hard Call for participation: This a hard problem and requires more experience or effort to work on L-suggestion Lint: Improving, adding or fixing lint suggestions
Projects
None yet
Development

No branches or pull requests

4 participants