Skip to content

avoid_constant_conditions

v0.4.0 Warning Control Flow

Flags a comparison (==, !=, <, >, <=, >=) whose operands are both compile-time constants. The answer is fixed before the program runs, so one branch is dead and the other always taken.

The usual shape is a constant that used to be a variable, or a constant compared against the value it was defined as:

abstract final class Config {
static const channel = 'stable';
static const maxRetries = 3;
}
void configure() {
// Always true — `channel` is `'stable'` by definition.
if (Config.channel == 'stable') {
enableCrashReporting();
}
}
void enableCrashReporting() {}

Compare the constant against something that varies — a parameter, a field, a value read at runtime:

abstract final class Config {
static const channel = 'stable';
static const maxRetries = 3;
}
void configure(String buildChannel) {
if (buildChannel == Config.channel) {
enableCrashReporting();
}
}
void enableCrashReporting() {}

Comparing a variable against a named constant is the whole point of naming it, and is never reported:

void retry(int attempt) {
const maxRetries = 3;
if (attempt < maxRetries) {
// Only `maxRetries` is constant, so nothing is reported.
}
}

Hard-coding both sides to force a branch is the fastest way to test one, and the easiest thing to forget to undo:

void render() {
// Don't — left over from "let me see the empty state"
if (1 == 1) {
showEmptyState();
}
}
void showEmptyState() {}

Naming both sides does not make the comparison meaningful — it is still decided at compile time:

void check() {
const limit = 4;
const ceiling = 4;
// Don't — `4 != 4`, written in two steps
if (limit != ceiling) {
print('never');
}
}

Only the six comparison operators are checked. &&, || and arithmetic are out of scope — see avoid_contradictory_expressions for conjunctions that can never be true.

Constness is read syntactically: literals, const variables and static const fields, const collections and const constructor calls, plus prefix operators over those. A plain final local is not treated as constant, so final limit = 4; if (limit != 4) is not reported. Neither is a function call, so f() == 3 stays silent even when f is trivially constant.

There is no quick fix. Which operand should have been a variable is a question only the author can answer.

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_constant_conditions: false

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