Skip to content

avoid_unnecessary_negations

v0.8.0 Warning Fix Control Flow

This rule flags a negation that can be removed without changing the meaning. Four shapes are reported.

A negation over a negation. Usually the result of inverting a condition and leaving the inner expression as it was:

void start() {}
void toggle(bool isEnabled) {
if (!!isEnabled) {
start();
}
}

A negation over !=. The ! and the != cancel:

enum Status { active, archived }
void activate() {}
void refresh(Status status) {
if (!(status != Status.active)) {
activate();
}
}

A negation over a boolean literal. !true is just false written the long way — most often a feature flag that was flipped in place:

void renderLegacyBanner() {}
void render() {
if (!true) {
renderLegacyBanner();
}
}

A negation on both sides of a comparison. Negating both operands leaves the answer unchanged:

void sync() {}
void check(bool isReady, bool isLoaded) {
if (!isReady == !isLoaded) {
sync();
}
}

The quick fix removes the redundant operators, one diagnostic at a time:

enum Status { active, archived }
void start() {}
void activate() {}
void renderLegacyBanner() {}
void sync() {}
void examples(bool isEnabled, Status status, bool isReady, bool isLoaded) {
if (isEnabled) {
start();
}
if (status == Status.active) {
activate();
}
if (false) {
renderLegacyBanner();
}
if (isReady == isLoaded) {
sync();
}
}

Four shapes are reported: ! applied to a ! expression, ! applied to a != comparison, ! applied to a boolean literal, and ==/!= with a negation on both sides. Parentheses are unwrapped first, so !(!flag) is caught.

A single negation in a comparison (!a == b) is left alone — removing it would change the result. A negated == (!(a == b)) is also deliberately excluded: it is a single negation, and rewriting it to != is a style preference rather than a redundancy. Negated relational comparisons are handled by avoid_inverted_boolean_checks instead, so the same code is never reported twice.

This rule is in the core preset, so it is on with preset: core, preset: recommended or preset: opinionated.

To turn it off:

many_lints.yaml
rules:
avoid_unnecessary_negations: false

To keep the rule on but skip certain paths, use per-rule exclude.