-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[WIP] - new lint: using let mut a = a
at start of function
#5138
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
Conversation
This looks a little broader than I was imagining ... For e.g. I would like this to be linted: fn f(a: usize) {
let b = 1;
let mut a = a;
} but not this: fn f(a: usize) {
let b = a;
loop {
let mut a = a;
a += 5;
} |
But maybe this is better, I'm not sure. |
My understanding was if we're defining a local mutable variable pointing to a function parameter, then that local mutable variable is not needed and instead the function parameter's mutability could instead be changed. The loop example you provided wouldn't trigger this lint cause it doesn't check inside of the From what I've learnt over the past week, this line here loops through each statement in that function. The function has only two statements, the edit: grammar |
/// // rest of code | ||
/// } | ||
/// ``` | ||
pub FN_PARAM_REDEF_AS_MUTABLE, |
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.
bikeshedding: I think we usually call them variable "bindings" rather than "definitions". So, this can be named FN_PARAM_REBIND_AS_MUT
.
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.
Sweet, I'll rename it cause that sounds a lot better.
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.
error: a parameter was redefined as mutable, can be removed
--> clippy_lints/src/literal_representation.rs:198:5
|
198 | / fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) {
199 | | let mut integer = digits;
200 | | let mut fraction = None;
201 | | let mut exponent = None;
... |
224 | | (integer, fraction, exponent)
225 | | }
| |_____^
|
help: consider making this param `mut`
--> clippy_lints/src/literal_representation.rs:198:26
|
198 | fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) {
| ^^^^^^^^^^^^^
help: consider removing this local variable
--> clippy_lints/src/literal_representation.rs:199:9
|
199 | let mut integer = digits;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
This is a bug. The digits
arg is binded to integer
, but then gets used afterwards. If we'd declare the arg as mut
, we would modify the semantics of the function.
To test if the arg is used afterwards, you can use:
rust-clippy/clippy_lints/src/utils/usage.rs
Lines 79 to 82 in a7b3b9f
pub struct UsedVisitor { | |
pub var: ast::Name, // var to look for | |
pub used: bool, // has the var been used otherwise? | |
} |
Don't worry about the dogfood tests now. We can fix them, once this lint is implemented and tested by ui tests completely
if let BindingMode::ByValue(mutability) = mode; | ||
if let Mutability::Mut = mutability; |
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.
if let BindingMode::ByValue(mutability) = mode; | |
if let Mutability::Mut = mutability; | |
if let BindingMode::ByValue(Mutability::Mut) = mode; |
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.
Sweet, learning more Rust as I go!
if let Some(ref expr) = local.init; | ||
if let ExprKind::Path(_, ref path) = expr.kind; | ||
if let Some(ref segment) = path.segments.last(); | ||
if let name = segment.ident.name; |
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.
if let name = segment.ident.name; | |
let name = segment.ident.name; |
if let BindingMode::ByValue(param_mut) = param_mode; | ||
if let Mutability::Not = param_mut; |
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.
if let BindingMode::ByValue(param_mut) = param_mode; | |
if let Mutability::Not = param_mut; | |
if let BindingMode::ByValue(Mutability::Not) = param_mode; |
db.span_help(param.span, "consider making this param `mut`"); | ||
db.span_help(stmt.span, "consider removing this local variable"); | ||
|
||
multispan_sugg(db, "...".to_string(), vec![]); |
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.
What does this do?
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.
I'm going to remove this, initially I was going to use this to do what line 78~79 was doing.
cx, | ||
FN_PARAM_REDEF_AS_MUTABLE, | ||
fn_span, | ||
"a parameter was redefined as mutable, can be removed", |
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.
NIT:
"a parameter was redefined as mutable, can be removed", | |
"a parameter was redefined as mutable and can be removed", |
@@ -0,0 +1,8 @@ | |||
#![warn(clippy::fn_param_redef_as_mutable)] | |||
|
|||
fn foobar(a: Vec<bool>) { |
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.
We'll need more tests:
- more than one statement (lint)
- binding is not in the top level block (no lint)
- arg is used after binding (no lint)
- name of rebind is the same as name of arg (lint)
- name of rebind is the same as name of arg and arg is used afterwards. (lint)
- This test: (no lint)
fn f(x: usize) {
let y = x * 3;
let x = 42;
for i in 0..x {
println!("{}", i);
}
let mut z = x; // Should not get linted
z /= 2;
println!("x={}, y={}, z={}", x, y, z);
}
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.
I noticed that you used arg instead of param, is there a preference to the wording here that I should use in the change?
Also just one thing that I think would be good for discussion; What's the intention behind not catching a rebind if they're not the same name of the arg?
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.
What's the intention behind not catching a rebind if they're not the same name of the arg?
Oh, I should have specified, what of the above test cases should get linted and what not. I added it in my comment above
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.
I noticed that you used arg instead of param
arg is shorter to write 😄 I don't know, what they are called in rust, honestly. I think, I heard both.
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.
Looking at the rustc doc, they are called param: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/struct.Param.html
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.
I've heard arguments are the values that get passed to a function while parameters are the formal variables in the type signature. So in fn f(a: usize)
, a is a parameter, but in Vec::from("b")
, "b" is an argument.
☔ The latest upstream changes (presumably #5125) made this pull request unmergeable. Please resolve the merge conflicts. |
- Added in the right checks - Added better help hints
☔ The latest upstream changes (presumably #5202) made this pull request unmergeable. Please resolve the merge conflicts. |
/// **Why is this bad?** reduces the complexity of the code by removing a redundant local | ||
/// mutable variable. |
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.
Making this mistake is bad because you add unnecessary complexity, not reduce it ;)
Maybe something like
/// **Why is this bad?** reduces the complexity of the code by removing a redundant local | |
/// mutable variable. | |
/// **Why is this bad?** Introduces a local variables that increases complexity unnecessarily. |
Thanks for contributing to Clippy! Sadly this PR was not updated in quite some time. If you waited on input from a reviewer, we're sorry that this fell under the radar. If you want to continue to work on this, just reopen the PR and/or ping a reviewer. |
Lint { | ||
name: "fn_param_redef_as_mutable", | ||
group: "complexity", | ||
desc: "default lint description", |
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.
Update with real description
fixes #5102
changelog: none