biome style/noUselessElse
labelled safe
Analysis Nemotron 3 Super
Flags else blocks that follow an if block containing a breaking statement (return, break, continue, throw), as they are unreachable and redundant.
Fix: Removes the else block and places its statements directly after the if block, preserving their original order and indentation relative to the function or block scope.
10 hazards
- Moving a break statement out of a switch case alters fall-through behavior, potentially executing unintended cases.
- Relocating a continue statement may cause it to target an outer loop instead of the innermost one, changing iteration behavior.
- Hoisting a function declaration from within the else block could make it available earlier than intended, causing premature invocation.
- Moving a variable declaration with let/const from the else block risks referencing it before initialization if used prior to the if block.
- Extracting a labeled statement from the else block may break label targeting if the label is referenced elsewhere via break/continue.
- Relocating a throw statement could change the stack trace by altering the point of origin, affecting error handling logic.
- Moving a return statement from within a try block may skip execution of a finally block that was meant to run regardless.
- Duplicating a side-effecting expression (e.g., a function call) from the else block into multiple paths due to code duplication during refactoring is not applicable here, but the fix assumes single occurrence—however, if the else block contains multiple statements, their relative order is preserved but their execution context changes.
- If the else block contains a debugger statement, moving it may alter developer tooling behavior if the surrounding scope has different blackboxing settings.
- Comments attached to the else brace or internal to the else block may be lost or misplaced during the rewrite, degrading code readability.
Findings
| program | oracle | label | judge | status | summary |
|---|---|---|---|---|---|
| r1-p007 | fix-changes-behavior | labelled safe | real · high | duplicate | The fix hoists the function declaration out of the else block, changing it from a block-scoped declaration (ES2015+) that only exists when the else runs to a f… |
| r1-p008 | fix-changes-behavior | labelled safe | real · high | duplicate | The fix hoists a function declaration out of an else block into a switch case clause, where function declarations are not allowed in strict mode and have imple… |
| r2-p001 | fix-breaks-syntax | labelled safe | real · high | duplicate | The fix hoists the `let x = 2` declaration out of the `else` block into the same scope where `let x = 1` already exists, causing a duplicate declaration Syntax… |
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Continue targeting wrong loop | Relocating a continue statement may cause it to target an outer loop instead of the innermost one, changing iteration behavior. |
| r1-p002 | fix ok | Class field timing | class fields, static blocks, private names, super calls |
| r1-p003 | fix ok | Label and finally interaction | label/break/continue control flow, finally blocks, early returns in nested blocks |
| r1-p004 | invalid | Labeled statement extraction | Extracting a labeled statement from the else block may break label targeting if the label is referenced elsewhere via break/continue. |
| r1-p005 | fix ok | Comment displacement | Comments attached to the else brace or internal to the else block may be lost or misplaced during the rewrite, degrading code readability. |
| r1-p006 | fix ok | Side effect duplication | order of evaluation when expressions are merged, duplicated or deleted (side effects run twice or never) |
| r1-p007 | fix-changes-behavior | Premature function hoist | Hoisting a function declaration from within the else block could make it available earlier than intended, causing premature invocation. |
| r1-p008 | fix-changes-behavior | Block-scoped function in switch | function declarations inside blocks, switch cases, labeled statements |
| r2-p001 | fix-breaks-syntax | Shadowing after removal of else block | name collisions across scopes after a move (shadowing) |
| r2-p002 | fix ok | Label targeting broken by else block extraction | Extracting a labeled statement from the else block may break label targeting if the label is referenced elsewhere via break/continue. |
| r2-p003 | fix ok | Function declaration hoisted from else block | Hoisting a function declaration from within the else block could make it available earlier than intended, causing premature invocation. |
| r2-p004 | fix ok | Comment displaced when else block removed | comments attached to the removed or moved code |
| r2-p005 | fix ok | Debugger statement moved alters tooling behavior | If the else block contains a debugger statement, moving it may alter developer tooling behavior if the surrounding scope has different blackboxing settings. |
| r2-p006 | fix ok | Function declaration in else block of switch case | function declarations inside blocks, switch cases, labeled statements |
| r2-p007 | fix ok | Order preservation but context change in else block | Duplicating a side-effecting expression [...] their relative order is preserved but their execution context changes. |
| r2-p008 | invalid | Nullish vs falsy in else block after move | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
Sources
r1-p001 · Continue targeting wrong loop
1function main() {
2 let out = '';
3 L1: for (let i = 0; i < 2; i++) {
4 for (let j = 0; j < 2; j++) {
5 if (j === 0) { continue L1; }
6 else { out += i + ',' + j + '|'; }
7 }
8 }
9 console.log(out);
10}
11main();
--- orig.js 2026-10-17 15:42:21.261561278 -0400 +++ fixed.js 2026-10-17 15:42:21.532555670 -0400 @@ -3,7 +3,7 @@ L1: for (let i = 0; i < 2; i++) { for (let j = 0; j < 2; j++) { if (j === 0) { continue L1; } - else { out += i + ',' + j + '|'; } + out += i + ',' + j + '|'; } } console.log(out);
r1-p002 · Class field timing
1class C {
2 static x = 1;
3 #p;
4 constructor() {
5 this.#p = 0;
6 if (false) { return; }
7 else { this.#p = 2; }
8 }
9 get p() { return this.#p; }
10}
11const c = new C();
12console.log(c.p);
--- orig.js 2026-10-17 15:42:21.701552173 -0400 +++ fixed.js 2026-10-17 15:42:21.967546668 -0400 @@ -4,7 +4,7 @@ constructor() { this.#p = 0; if (false) { return; } - else { this.#p = 2; } + this.#p = 2; } get p() { return this.#p; } }
2
r1-p003 · Label and finally interaction
1function main() {
2 let out = '';
3 L: for (let i = 0; i < 2; i++) {
4 try {
5 if (i === 0) { break L; }
6 else { out += i; }
7 } finally { out += 'f'; }
8 }
9 console.log(out);
10}
11main();
--- orig.js 2026-10-17 15:42:22.127543355 -0400 +++ fixed.js 2026-10-17 15:42:22.396537782 -0400 @@ -3,7 +3,7 @@ L: for (let i = 0; i < 2; i++) { try { if (i === 0) { break L; } - else { out += i; } + out += i; } finally { out += 'f'; } } console.log(out);
f
r1-p004 · Labeled statement extraction (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T19-42-1)
1function main() {
2 let out = '';
3 L: for (let i = 0; i < 2; i++) {
4 if (i === 0) { continue; }
5 else { L: out += i; break L; }
6 }
7 console.log(out);
8}
9main();
r1-p005 · Comment displacement
1function main() {
2 let x = 0;
3 if (false) { return; }
4 /* keep-me */
5 else { x = 1; /* inner */ }
6 console.log(x);
7}
8main();
--- orig.js 2026-10-17 15:42:22.618533183 -0400 +++ fixed.js 2026-10-17 15:42:22.899527362 -0400 @@ -2,7 +2,7 @@ let x = 0; if (false) { return; } /* keep-me */ - else { x = 1; /* inner */ } + x = 1; /* inner */ console.log(x); } main();
1
r1-p006 · Side effect duplication
1function main() {
2 let a = 0, b = 0;
3 if (false) { return; }
4 else { a++; b += a; }
5 console.log(a, b);
6}
7main();
--- orig.js 2026-10-17 15:42:23.061524004 -0400 +++ fixed.js 2026-10-17 15:42:23.339518239 -0400 @@ -1,7 +1,7 @@ function main() { let a = 0, b = 0; if (false) { return; } - else { a++; b += a; } + a++; b += a; console.log(a, b); } main();
1 1
r1-p007 · Premature function hoist
1function main() {
2 if (false) { return; }
3 else { function f() { return 1; } }
4 try { console.log(f()); } catch (e) { console.log(e.name); }
5}
6main();
--- orig.js 2026-10-17 15:42:23.507514755 -0400 +++ fixed.js 2026-10-17 15:42:23.777509156 -0400 @@ -1,6 +1,6 @@ function main() { if (false) { return; } - else { function f() { return 1; } } + function f() { return 1; } try { console.log(f()); } catch (e) { console.log(e.name); } } main();
ReferenceError
r1-p008 · Block-scoped function in switch
1function main() {
2 switch (0) {
3 case 0:
4 if (false) { return; }
5 else { function g() { return 2; } }
6 console.log(typeof g);
7 break;
8 }
9}
10main();
--- orig.js 2026-10-17 15:42:23.938505817 -0400 +++ fixed.js 2026-10-17 15:42:24.206500254 -0400 @@ -2,7 +2,7 @@ switch (0) { case 0: if (false) { return; } - else { function g() { return 2; } } + function g() { return 2; } console.log(typeof g); break; }
undefined
r2-p001 · Shadowing after removal of else block
1function f() {
2 let x = 1;
3 if (true) { return x; }
4 else {
5 let x = 2;
6 console.log(x);
7 }
8}
9f();
--- orig.js 2026-10-17 15:42:28.435412284 -0400 +++ fixed.js 2026-10-17 15:42:28.696406842 -0400 @@ -1,9 +1,8 @@ function f() { let x = 1; if (true) { return x; } - else { + let x = 2; console.log(x); - } } f();
r2-p002 · Label targeting broken by else block extraction
1outer: for (let i = 0; i < 2; i++) {
2 if (i === 0) { continue outer; }
3 else {
4 console.log('reached');
5 break outer;
6 }
7}
--- orig.js 2026-10-17 15:42:28.746405799 -0400 +++ fixed.js 2026-10-17 15:42:29.010400295 -0400 @@ -1,7 +1,6 @@ outer: for (let i = 0; i < 2; i++) { if (i === 0) { continue outer; } - else { + console.log('reached'); break outer; - } }
reached
r2-p003 · Function declaration hoisted from else block
1function f() {
2 if (false) {
3 return g();
4 } else {
5 function g() { return 'early'; }
6 return g();
7 }
8}
9console.log(f());
--- orig.js 2026-10-17 15:42:29.177396809 -0400 +++ fixed.js 2026-10-17 15:42:29.432391487 -0400 @@ -1,9 +1,8 @@ function f() { if (false) { return g(); - } else { + } function g() { return 'early'; } return g(); - } } console.log(f());
early
r2-p004 · Comment displaced when else block removed
1function f() {
2 if (true) { return 1; }
3 // This comment should stay
4 else {
5 // This comment may be lost
6 console.log(2);
7 }
8}
9f();
--- orig.js 2026-10-17 15:42:29.599388001 -0400 +++ fixed.js 2026-10-17 15:42:29.839382992 -0400 @@ -1,9 +1,8 @@ function f() { if (true) { return 1; } // This comment should stay - else { + // This comment may be lost console.log(2); - } } f();
r2-p005 · Debugger statement moved alters tooling behavior
1function f() {
2 if (true) { return; }
3 else {
4 debugger;
5 console.log('unreachable');
6 }
7}
8f();
--- orig.js 2026-10-17 15:42:29.990379840 -0400 +++ fixed.js 2026-10-17 15:42:30.237374680 -0400 @@ -1,8 +1,7 @@ function f() { if (true) { return; } - else { + debugger; console.log('unreachable'); - } } f();
r2-p006 · Function declaration in else block of switch case
1function f() {
2 switch (0) {
3 case 0:
4 if (true) { break; }
5 else {
6 function g() { return 1; }
7 return g();
8 }
9 }
10}
11console.log(f());
--- orig.js 2026-10-17 15:42:30.382371650 -0400 +++ fixed.js 2026-10-17 15:42:30.664365758 -0400 @@ -2,10 +2,9 @@ switch (0) { case 0: if (true) { break; } - else { + function g() { return 1; } return g(); - } } } console.log(f());
undefined
r2-p007 · Order preservation but context change in else block
1let log = [];
2function f() {
3 if (true) { return; }
4 else {
5 log.push(1);
6 log.push(2);
7 }
8}
9f();
10console.log(log);
--- orig.js 2026-10-17 15:42:30.830362290 -0400 +++ fixed.js 2026-10-17 15:42:31.095356751 -0400 @@ -1,10 +1,9 @@ let log = []; function f() { if (true) { return; } - else { + log.push(1); log.push(2); - } } f(); console.log(log);
[]
r2-p008 · Nullish vs falsy in else block after move (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T19-42-1)
1let obj = { foo: undefined };
2if (obj?.foo ?? false) {
3 return;
4} else {
5 console.log(obj?.foo ?? 'default');
6}