rust-lang / rust-lang/rust-clippy

Lint using blocks in compound expression operators

Open
#4,756 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint L-correctness
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Consider the following code:

#[derive(Copy, Clone)]
struct MyAdd;

impl std::ops::AddAssign<MyAdd> for MyAdd {
    fn add_assign(&mut self, other: MyAdd) {}
}

fn main() {
    let lhs = &mut MyAdd;
    let rhs = MyAdd;
    *{println!("LHS"); lhs} += {println!("RHS"); rhs};
}

This code uses a block express as both the LHS and RHS of a compound assignment operator (+=). This code is not only difficult to read - its execution order depends on the type of the expression produced by the block (see https://github.com/rust-lang/rust/issues/28160 and https://github.com/rust-lang/rust/pull/61572).

As written, this code prints:

LHS
RHS

However, this nearly identical code:

fn main() {
    let lhs = &mut 25;
    let rhs = 30;
    *{println!("LHS"); lhs} += {println!("RHS"); rhs};
}

prints:

RHS
LHS

That is, the evaluation order of the LHS and RHS is dependent on whether or not the type used is a primitive or not.

Using a block expression with a compound assignment operator is a bad idea in and of itself - it's very difficult to read, and makes it unclear what is actually happening. However, the inconsistent evaluation order makes this even worse - a seemly unrelated change (e.g. changing the type of a variable earlier up in a function) can result in the execution order of other expressions changing.

It would be useful if Clippy were to lint the use of anything other than a variable, literal, or (chain of) method calls with a compound assignment operator.

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

Start by reproducing the two Rust examples in the issue and compare their evaluation order for primitive and user-defined types. Use the examples to define the lint's scope and verify that it flags non-simple operands of compound assignment while permitting variables, literals, and method-call chains.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.