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';}throw counts as exiting
Section titled “throw counts as exiting”A validation guard is the shape this rule pays for itself on — the whole rest of the method loses a level:
// Don'tdouble 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; }}// Dodouble 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;}continue and break inside a loop
Section titled “continue and break inside a loop”The same applies to a loop body, where the else wraps everything that follows:
// Don'tvoid report(List<String> lines) { for (final line in lines) { if (line.isEmpty) { continue; } else { print(line.trim()); } }}// Dovoid report(List<String> lines) { for (final line in lines) { if (line.isEmpty) { continue; } print(line.trim()); }}Known limitations
Section titled “Known limitations”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()returnsNever. - A branch whose last statement is a
switchorifwhere 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.
Configuration
Section titled “Configuration”This rule is in the opinionated preset, so it is on with
preset: opinionated, or by name:
rules: avoid_redundant_else: trueTo turn it off again:
rules: avoid_redundant_else: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_collapsible_if— Merge nested if statements with &&.prefer_early_return— Replace a body-wrapping if with an early-return guard.prefer_immediate_return— Return an expression directly instead of via a throwaway variable.no_equal_then_else— Both branches of a condition are identical.