Skip to content

avoid_redundant_else

v0.8.0 Warning Fix Control Flow

Flags an else whose matching if branch always exits — via return, throw, break, or continue. Reaching the code after the if already implies the condition was false, so the else only adds a level of indentation.

String describe(int value) {
if (value < 0) {
return 'negative';
} else {
return 'non-negative';
}
}
String describe(int value) {
if (value < 0) {
return 'negative';
}
return 'non-negative';
}

A validation guard is the shape this rule pays for itself on — the whole rest of the method loses a level:

// Don't
double average(List<int> values) {
if (values.isEmpty) {
throw ArgumentError('values must not be empty');
} else {
var total = 0;
for (final value in values) {
total += value;
}
return total / values.length;
}
}
// Do
double average(List<int> values) {
if (values.isEmpty) {
throw ArgumentError('values must not be empty');
}
var total = 0;
for (final value in values) {
total += value;
}
return total / values.length;
}

The same applies to a loop body, where the else wraps everything that follows:

// Don't
void report(List<String> lines) {
for (final line in lines) {
if (line.isEmpty) {
continue;
} else {
print(line.trim());
}
}
}
// Do
void report(List<String> lines) {
for (final line in lines) {
if (line.isEmpty) {
continue;
}
print(line.trim());
}
}

else if chains are never reported. They read as a single decision, and splitting them into sequential if statements usually reads worse than the chain.

The exit check is syntactic: a branch counts as exiting when its last statement is a return, throw, break, or continue. Two consequences follow:

  • A branch that exits through a helper is not recognised, so if (bad) { _fail(); } else { ... } is not reported even when _fail() returns Never.
  • A branch whose last statement is a switch or if where every path returns is not recognised either — only the last statement itself is examined.

The quick fix declines to hoist an else body that declares a variable, since the name could collide in the enclosing scope. Those cases report without an automatic fix; unindent them by hand, renaming if needed.

This rule is in the opinionated preset, so it is on with preset: opinionated, or by name:

many_lints.yaml
rules:
avoid_redundant_else: true

To turn it off again:

many_lints.yaml
rules:
avoid_redundant_else: false

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