rust-lang / rust-lang/rust-clippy
Warn if same variable is read and written in same expression
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 13.5k
- Forks
- 2.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 32
Description
What it does
TL;DR. Please, warn in code like a.f(g(&mut a)).
Details. Just now I got very unfortunate situation with my production code. Let me describe it. I parse some custom protobuf-based data format. Using my own code (i. e. without using existing protobuf libs).
I have this variable (holding file contents):
let mut uncompressed: &[u8] = ...
Then I read one protobuf varint from it so:
let split_here = read_usize(&mut uncompressed).unwrap();
Now split_here is integer value I have read. And now uncompressed is remaining data.
Now I read split_here-sized data so:
let (archive_info, uncompressed2) = uncompressed.split_at(split_here);
Okay. So far, so good. The code above works correctly.
Now I refactored it so:
let (archive_info, uncompressed2) = uncompressed.split_at(read_usize(&mut uncompressed).unwrap());
(And then forgot about this refactoring.)
And the code stopped to worked correctly! I completely didn't understand what was going on. I debugged the code using gdb. And then after very time-consuming debug I suddenly understood that this refactoring changed meaning of the code.
The new code first reads uncompressed (in uncompressed.split_at...) and then mutates it (in read_usize(&mut uncompressed)). This is not what I want!
So, this is very big footgun. I propose to warn if same variable is read and mutated in same expression. Of course, mutating same variable twice in one expression should warn, too.
(Ideally, rustc should warn on this code, too. Or even error. But this would be dream.)
Advantage
No response
Drawbacks
No response
Example
// This is simplified example from my production code
#![feature(slice_take)]
fn main() {
let uncompressed = vec![0, 0, 0];
let mut uncompressed: &[u8] = &uncompressed;
let (archive_info, uncompressed2) = uncompressed.split_at(usize::from(*<[u8]>::take_first(&mut uncompressed).unwrap()));
}
Could be written as:
#![feature(slice_take)]
fn main() {
let uncompressed = vec![0, 0, 0];
let mut uncompressed: &[u8] = &uncompressed;
let tmp = uncompressed;
let (archive_info, uncompressed2) = tmp.split_at(usize::from(*<[u8]>::take_first(&mut uncompressed).unwrap()));
}
or:
#![feature(slice_take)]
fn main() {
let uncompressed = vec![0, 0, 0];
let mut uncompressed: &[u8] = &uncompressed;
let split_here = usize::from(*<[u8]>::take_first(&mut uncompressed).unwrap());
let (archive_info, uncompressed2) = uncompressed.split_at(split_here);
}
depending on intended meaning
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No source file, test, or entry point is named. Start by reproducing the Rust examples and clarify the intended warning for a read plus mutable read, and for two mutations; done means the chosen behavior is specified for these cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100