Skip to content

arithmetic_side_effects doesn't fire in non-const function located inside of const definition #13845

Open
@u59149403

Description

@u59149403

Summary

arithmetic_side_effects doesn't fire in non-const function located inside of const definition. (See below for why it is important for me.)

Lint Name

arithmetic_side_effects

Reproducer

I tried this code:

// Add alloy 0.8.1 as a dependency

#![deny(clippy::arithmetic_side_effects)]

const _: () = {
    fn f() {
        let _ = alloy::primitives::U256::from(1) + alloy::primitives::U256::from(2);
    }
};

I expected to see this happen: arithmetic_side_effects should fire.

Instead, this happened: nothing fired.

Note: it is documented that arithmetic_side_effects will not fire in const contexts. But this is not const context.

Okay, now let me describe, why this is important and how this actually breaks my workflow.

Okay, so I use crate alloy for managing my money. alloy provides type U256 for dealing with monetary values. U256 is borrowed from another crate ruint. I don't want silent overflows, I want panics on overflow, so I filled bug to ruint, hoping they will add panics-on-overflow: recmo/uint#408 . But a developer said that they don't want to change the behavior, so I have to resort to some hacks on my side. So I decided to use clippy's lint arithmetic_side_effects to eliminate arithmetic operations with U256 in my code and use checked_add and similar instead.

But I'm not sure that arithmetic_side_effects will always work. It is possible that it will change its behavior in some next version of clippy. Notably, arithmetic_side_effects currently doesn't fire on Wrapping. It is possible that following same logic authors of clippy will disable this lint for U256, too, because U256 has wrapping behavior, too! So, I need some way to be sure that arithmetic_side_effects will always fire in the future. So, I decided to use #[expect].

So, I put this function to my code:

#![deny(clippy::arithmetic_side_effects)]

// ...

fn f() {
  #[expect(clippy::arithmetic_side_effects)]
  {
    let _ = alloy::primitives::U256::from(1) + alloy::primitives::U256::from(2);
  }
}

Okay, this works, but has some problems. First, f is not used, so it fires warning about dead code. Second, I need to invent new name for every such function, despite I don't plan to call them. And finally, such functions pollute global name space.

It is possible to put this function to some module, but then the problem reappears again, because now I have to invent some name for this module, despite I don't plan to use it.

So, I decided to use const _: () trick:

#![deny(clippy::arithmetic_side_effects)]

#[expect(clippy::arithmetic_side_effects)]
const _: () = { alloy::primitives::U256::from(1) + alloy::primitives::U256::from(2); };

But this is const context. And arithmetic_side_effects is documented not to fire in the const contexts. Moreover, this code doesn't even compile. So, I did this:

#![deny(clippy::arithmetic_side_effects)]

const _: () = {
    fn f() {
        #[expect(clippy::arithmetic_side_effects)]
        {
            let _ = alloy::primitives::U256::from(1) + alloy::primitives::U256::from(2);
        }
    }
};

Unfortunately, this code gives message this lint expectation is unfulfilled. It seems this non-const context is treated as const by clippy. This is a bug, and thus I reported it

Version

rustc 1.85.0-nightly (a224f3807 2024-12-09)
binary: rustc
commit-hash: a224f3807e58afc9353510f1d556c607d367545d
commit-date: 2024-12-09
host: x86_64-unknown-linux-gnu
release: 1.85.0-nightly
LLVM version: 19.1.5

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-negativeIssue: The lint should have been triggered on code, but wasn't

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions