BUG002-switch-fallthrough — Switch case falls through
| Property | Value |
|---|---|
| Dimension | Maintainability |
| Severity | Medium |
| Confidence | High |
| CWE | CWE-484 |
| Languages | JavaScript, TypeScript |
What it detects
A case (or default:) clause inside a switch statement that:
- Is not the last clause in its
switch, and - Has at least one statement in its body, and
- Does not end with a terminating statement (
break,return,throw,continue), and - Has no ESLint-style fallthrough comment immediately before the next case label.
The control falls through to the next clause silently — almost always a typo
for a missing break.
Carve-outs
Empty case grouping
Empty consequents are the idiomatic way to apply the same body to multiple case values and are not flagged:
// NOT flagged — intentional grouping
switch (x) {
case 1:
case 2:
case 3:
doMulti();
break;
}
// falls through comment
Following ESLint no-fallthrough, a comment matching
/falls?\s*through/i on the line immediately before the next case
label silences the finding. Both line comments and block comments work:
switch (x) {
case 1:
doA();
// falls through
case 2:
doB();
break;
}
switch (x) {
case 1:
doA();
/* fallthrough */
case 2:
doB();
break;
}
Why it matters
CWE-484 (Omitted Break Statement in Switch) is a textbook source of bugs.
The omitted break is invisible and the code still compiles, but the
following case body runs anyway and silently changes behavior.
// Bug: forgot break — `doB()` always runs when x === 1
switch (x) {
case 1:
doA();
case 2:
doB();
break;
}
Examples — flagged
// Plain fallthrough
switch (x) {
case 1:
doA();
case 2:
doB();
break;
}
// Multiple cascading falls
switch (x) {
case 1:
doA();
case 2:
doB();
case 3:
doC();
break;
}
// `default:` in the middle, falling through
switch (x) {
case 1:
doA();
break;
default:
doDefault();
case 2:
doB();
break;
}
// Block body without terminator
switch (x) {
case 1: {
doA();
}
case 2:
doB();
break;
}
Examples — not flagged
// Terminators stop fallthrough
switch (x) {
case 1:
doA();
break;
case 2:
return doB();
case 3:
throw new Error('c');
}
// Last case can't fall through
switch (x) {
case 1:
doA();
break;
case 2:
doB(); // last case
}
// Empty grouping
switch (x) {
case 1:
case 2:
doMulti();
break;
}
// Comment carve-out
switch (x) {
case 1:
doA();
// falls through
case 2:
doB();
break;
}
Fix guidance
Add a terminating statement at the end of the case body, or — if the
fallthrough is intentional — add a // falls through comment immediately
before the next case label.
// Before (bug):
switch (x) {
case 1:
doA();
case 2:
doB();
break;
}
// After (fix):
switch (x) {
case 1:
doA();
break;
case 2:
doB();
break;
}
Implementation
crates/zuit-lang-js/src/analyzers/switch_fallthrough.rs- Pre-extraction in
crates/zuit-lang-js/src/parse.rs