Else-If Chain Without Final Else
ID |
swift.else_if_without_else |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Code Smell |
Language |
Swift |
Tags |
code-style, conditional, control-flow |
Description
Reports an if / else if chain that does not end with a plain else. Only chains carrying at
least one else if are reported: a lone if, and a plain if / else, are out of scope. Each
chain yields at most one finding, reported at its outermost if.
The rule ships disabled by default. Whether every chain must state its remaining case is a house-style judgement, and imposing it on an existing codebase produces more findings than most teams want to act on. Enable it per project, or through a stricter policy, when the convention is one the team has adopted.
Rationale
if status == 0 {
markPending()
} else if status == 1 {
markActive()
}
// status == 2 arrives here having changed nothing
Once a condition is spelled out more than once, the chain reads as a decision table, and a reader
takes the absence of a final else to mean the listed conditions cover every input. When they do
not, control leaves the chain with whatever state it already had. The failure surfaces later, on
the one input nobody wrote a test for, and the code gives no hint that the case was ever
considered.
A final else block that is empty counts as a missing one, unless it carries a comment: an else
with real statements, or with only a comment saying why nothing is done, both close the chain.
A chain whose every branch returns is still reported. Swift’s early exit is guard, which leaves
no dangling case behind, so a returning chain with no else has exactly the same unstated case as
any other.
Swift’s if expression is out of scope — the compiler requires it to be exhaustive, so a
missing final else there is a compile error rather than a smell.
Remediation
State the remaining case. Handle it, fail loudly, or write the comment that says why there is nothing to handle — all three tell the next reader that the case was considered.
if status == 0 {
markPending()
} else if status == 1 {
markActive()
} else {
markUnknown(status)
}
When the subject is a finite set of values, a switch is usually the better shape: the compiler
then checks exhaustiveness instead of the reader.
switch status {
case .pending:
markPending()
case .active:
markActive()
case .archived:
markArchived()
}