Description
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