biome style/noUselessElse
labelled safe
Analysis Nemotron 3 Super
Flags else blocks after if statements containing breaking statements (return, break, continue, throw), as they are unreachable.
Fix: Removes the else block and places its statements directly after the if block, dedenting them by one level while preserving their original order.
10 hazards
- Moving a break statement out of a switch case into the enclosing loop changes which loop it breaks, altering control flow.
- Lifting a continue statement from inside a nested loop to an outer scope may cause it to break the wrong loop or become invalid if no enclosing loop exists.
- Hoisting a throw statement before a variable declaration in the else block could reference a variable that hasn't been initialized yet due to TDZ.
- Extracting a return statement from an else block inside a function may skip cleanup logic in a finally block of a surrounding try statement.
- Moving a break from an else block inside a labeled statement to an outer context may break the wrong label or become syntactically invalid.
- If the else block contains a function declaration, moving it to the outer scope may alter hoisting behavior and create a naming conflict.
- Comments attached to the else block or its braces may be lost or misplaced during the rewrite, affecting readability but not semantics.
- If the if block contains a labeled break and the else block is lifted, the label may no longer be in scope for the lifted statement.
- Lifting a statement with side effects (e.g., i++) from an else block changes the timing of execution relative to prior code in the same block.
- In a try-finally construct, moving a return from an else block before the finally block may prevent the finally block from executing if the return was previously guarded.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Continue lifted from inner loop breaks wrong outer loop | Lifting a continue statement from inside a nested loop to an outer scope may cause it to break the wrong loop or become invalid if no enclosing loop exists. |
| r1-p002 | fix ok | Class field initialization skipped due to removed else block | class fields, static blocks, private names, super calls |
| r1-p003 | fix ok | Break moved from else in labeled block breaks outer label incorrectly | label/break/continue control flow, finally blocks, early returns in nested blocks |
| r1-p004 | fix ok | Break from else inside labeled statement moves to wrong label scope | Moving a break from an else block inside a labeled statement to an outer context may break the wrong label or become syntactically invalid. |
| r1-p005 | invalid | Return moved before finally skips cleanup action | In a try-finally construct, moving a return from an else block before the finally block may prevent the finally block from executing if the return was previously guarded. |
| r1-p006 | silent prediction off | Function call in else condition runs twice after fix | order of evaluation when expressions are merged, duplicated or deleted (side effects run twice or never) |
| r1-p007 | invalid | Throw hoisted before let declaration causes TDZ error | Hoisting a throw statement before a variable declaration in the else block could reference a variable that hasn't been initialized yet due to TDZ. |
| r1-p008 | silent prediction off | Function declaration in else block becomes hoisted and alters control flow | function declarations inside blocks, switch cases, labeled statements |
| r2-p001 | invalid | Shadowing after else removal | name collisions across scopes after a move (shadowing) |
| r2-p002 | fix ok | Break to wrong label after lift | Moving a break from an else block inside a labeled statement to an outer context may break the wrong label or become syntactically invalid. |
| r2-p003 | silent prediction off | Throw before let TDZ | Hoisting a throw statement before a variable declaration in the else block could reference a variable that hasn't been initialized yet due to TDZ. |
| r2-p004 | invalid | Comments lost on move | comments attached to the removed or moved code |
| r2-p005 | silent prediction off | Side effect timing changed | Lifting a statement with side effects (e.g., i++) from an else block changes the timing of execution relative to prior code in the same block. |
| r2-p006 | invalid | Function declaration hoisted in else | function declarations inside blocks, switch cases, labeled statements |
| r2-p007 | fix ok | Labeled break loses label scope | If the if block contains a labeled break and the else block is lifted, the label may no longer be in scope for the lifted statement. |
| r2-p008 | silent prediction off | Nullish vs falsy after lift | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
Sources
r1-p001 · Continue lifted from inner loop breaks wrong outer loop
1let out = [];
2outer: for (let i = 0; i < 2; i++) {
3 inner: for (let j = 0; j < 2; j++) {
4 if (i === 0) { continue outer; }
5 else { out.push(i + ',' + j); }
6 }
7}
8console.log(out.join('|'));
--- orig.js 2026-10-17 15:57:36.172011573 -0400 +++ fixed.js 2026-10-17 15:57:36.448021434 -0400 @@ -2,7 +2,7 @@ outer: for (let i = 0; i < 2; i++) { inner: for (let j = 0; j < 2; j++) { if (i === 0) { continue outer; } - else { out.push(i + ',' + j); } + out.push(i + ',' + j); } } console.log(out.join('|'));
1,0|1,1
r1-p002 · Class field initialization skipped due to removed else block
1class C {
2 static x = (() => { if (false) { return 1; } else { return 2; } })();
3}
4console.log(C.x);
--- orig.js 2026-10-17 15:57:36.612027294 -0400 +++ fixed.js 2026-10-17 15:57:36.873036619 -0400 @@ -1,4 +1,4 @@ class C { - static x = (() => { if (false) { return 1; } else { return 2; } })(); + static x = (() => { if (false) { return 1; }return 2; })(); } console.log(C.x);
2
r1-p003 · Break moved from else in labeled block breaks outer label incorrectly
1let result = '';
2loop: {
3 if (true) { break loop; }
4 else { result += 'b'; }
5}
6result += 'a';
7console.log(result);
--- orig.js 2026-10-17 15:57:37.034042369 -0400 +++ fixed.js 2026-10-17 15:57:37.296051707 -0400 @@ -1,7 +1,7 @@ let result = ''; loop: { if (true) { break loop; } - else { result += 'b'; } + result += 'b'; } result += 'a'; console.log(result);
a
r1-p004 · Break from else inside labeled statement moves to wrong label scope
1let log = '';
2outer: {
3 inner: {
4 if (false) { break inner; }
5 else { break outer; }
6 }
7 log += 'after inner';
8}
9log += 'end';
10console.log(log);
--- orig.js 2026-10-17 15:57:37.461057587 -0400 +++ fixed.js 2026-10-17 15:57:37.728067103 -0400 @@ -2,7 +2,7 @@ outer: { inner: { if (false) { break inner; } - else { break outer; } + break outer; } log += 'after inner'; }
end
r1-p005 · Return moved before finally skips cleanup action (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T19-57-2)
1let finallyRan = false;
2try {
3 if (false) { return 'skipped'; }
4 else { return 'not skipped'; }
5} finally {
6 finallyRan = true;
7}
8console.log('finally ran:', finallyRan);
r1-p006 · Function call in else condition runs twice after fix
1let callCount = 0;
2function f() { return ++callCount; }
3if (f() > 0) { console.log('yes'); }
4else { console.log('no', f()); }
yes
r1-p007 · Throw hoisted before let declaration causes TDZ error (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T19-57-2)
1try {
2 if (false) { return; }
3 else { throw x; }
4} catch (e) {
5 console.log(e.name);
6}
7let x = 42;
r1-p008 · Function declaration in else block becomes hoisted and alters control flow
1let fCalled = false;
2if (false) {
3 function f() { fCalled = true; }
4} else {
5 f();
6}
7console.log(fCalled);
r2-p001 · Shadowing after else removal (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T19-57-2)
1let x = 1;
2if (true) { return; }
3else {
4 const x = 2;
5 console.log(x);
6}
7console.log('after');
r2-p002 · Break to wrong label after lift
1outer: {
2 if (false) { break outer; }
3 else {
4 console.log('in else');
5 break outer;
6 }
7}
8console.log('reached');
--- orig.js 2026-10-17 15:57:41.802211505 -0400 +++ fixed.js 2026-10-17 15:57:42.055220429 -0400 @@ -1,8 +1,7 @@ outer: { if (false) { break outer; } - else { + console.log('in else'); break outer; - } } console.log('reached');
in else reached
r2-p003 · Throw before let TDZ
1if (false) {}
2else {
3 throw new Error(x);
4 let x = 1;
5}
6console.log('not reached');
r2-p004 · Comments lost on move (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T19-57-2)
1if (true) { return; }
2else {
3 // important side effect
4 console.log(42);
5}
6console.log('done');
r2-p005 · Side effect timing changed
1let i = 0;
2if (false) { i++ }
3else {
4 console.log(i); // should print 0
5 i++;
6}
7console.log(i); // should print 1 if not lifted, 2 if lifted before log
0 1
r2-p006 · Function declaration hoisted in else (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T19-57-2)
1if (true) { return; }
2else {
3 function f() { return 1; }
4 console.log(f());
5}
6// After fix, f is hoisted and callable before
7console.log(typeof f);
r2-p007 · Labeled break loses label scope
1outer: {
2 if (false) { break outer; }
3 else {
4 console.log('in else');
5 break outer; // label 'outer' must still be in scope
6 }
7}
8console.log('after label');
--- orig.js 2026-10-17 15:57:42.783246061 -0400 +++ fixed.js 2026-10-17 15:57:43.054255598 -0400 @@ -1,8 +1,7 @@ outer: { if (false) { break outer; } - else { + console.log('in else'); break outer; // label 'outer' must still be in scope - } } console.log('after label');
in else after label
r2-p008 · Nullish vs falsy after lift
1let obj = null;
2if (false) {}
3else {
4 console.log(obj?.prop ?? 'default');
5}
6// After fix, if obj is reassigned later, timing matters
7obj = { prop: 'present' };
default