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()
}

© 2026 Xygeni. All rights reserved