rust-lang / rust-lang/rust-clippy

Warn if same variable is read and written in same expression

Open
#10,880 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.