biome style/noUselessElse
labelled safe
Analysis Nemotron 3 Super
Flags `else` blocks following an `if` whose body contains a breaking statement (`return`, `break`, `continue`, or `throw`).
Fix: Removes the `else` keywords and braces, promoting the `else` block's statements to the same indentation level as the enclosing `if` statement.
10 hazards
- Moving a `var`-declared variable from the `else` block to the outer scope may cause it to be hoisted and accessed before initialization if referenced earlier in the function.
- If the `else` block contains a `let` or `const` declaration that shadows an outer variable, promoting it may alter which binding inner functions capture due to changed scope nesting.
- Promoting statements from the `else` block may reorder their execution relative to other statements in the same block if the original `if-else` was not the first or last statement.
- If the `else` block contains a `break` targeting an outer loop, moving it outside the `if` may cause it to execute unconditionally, altering loop termination.
- A `continue` statement promoted from the `else` block may apply to a different loop than intended if loop nesting changes due to the removal of the `else` block's braces.
- A `throw` statement moved from the `else` block may be caught by a different `try`/`catch` if the `if-else` was inside a `try` block and the promotion changes the nesting level.
- Comments attached to the `else` keyword or its braces may be lost or misplaced during the rewrite, as the autofix typically does not preserve syntactic comments on removed tokens.
- If the `else` block is the sole content of a `case` clause in a `switch`, removing its braces may fall through to the next case if the promoted statements do not end with a `break`.
- In a `with` block, promoting `else` statements may change the object used for property resolution if the `if` and `else` blocks previously shared the same `with` context but the promotion alters evaluation order.
- If the `if` statement lacks braces and the autofix incorrectly assumes block structure, it may corrupt syntax by inserting or deleting braces where none existed.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | arguments object mutation | arguments object, function.length, default parameters, rest parameters |
| r1-p002 | invalid | block-scoped function hoisting | function declarations inside blocks, switch cases, labeled statements |
| r1-p003 | fix ok | comment loss on else | Comments attached to the `else` keyword or its braces may be lost or misplaced during the rewrite |
| r1-p004 | invalid | changed try-catch nesting | A `throw` statement moved from the `else` block may be caught by a different `try`/`catch` if the `if-else` was inside a `try` block and the promotion changes the nesting level. |
| r1-p005 | fix ok | async generator yield position | async/await ordering, generators and yield position |
| r1-p006 | fix ok | switch case fallthrough | If the `else` block is the sole content of a `case` clause in a `switch`, removing its braces may fall through to the next case if the promoted statements do not end with a `break`. |
| r1-p007 | invalid | nullish vs falsy | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
| r1-p008 | invalid | regex lastIndex sticky | regular expressions: escapes, unicode flag, lastIndex with the g flag |
| r2-p001 | fix ok | Var declared in else accessed before init | Moving a `var`-declared variable from the `else` block to the outer scope may cause it to be hoisted and accessed before initialization if referenced earlier in the function. |
| r2-p002 | fix ok | Comment preservation hazard | comments attached to the removed or moved code |
| r2-p003 | fix ok | Continue applies to wrong loop | A `continue` statement promoted from the `else` block may apply to a different loop than intended if loop nesting changes due to the removal of the `else` block's braces. |
| r2-p004 | fix ok | Comment on else keyword lost | Comments attached to the `else` keyword or its braces may be lost or misplaced during the rewrite, as the autofix typically does not preserve syntactic comments on removed tokens. |
| r2-p005 | fix ok | Execution reordering relative to siblings | Promoting statements from the `else` block may reorder their execution relative to other statements in the same block if the original `if-else` was not the first or last statement. |
| r2-p006 | fix ok | Var hoisting and redeclaration in loop | var hoisting and re-declaration inside loops or after use |
| r2-p007 | fix ok | Arguments object mutation hazard | arguments object, function.length, default parameters, rest parameters |
| r2-p008 | fix ok | Label/break interaction with nested blocks | label/break/continue control flow, finally blocks, early returns in nested blocks |
Sources
r1-p001 · arguments object mutation
1function f(a=0,b,c,d){if(arguments.length>2){return;}else{g();}}var x=0;function g(){x++};f(1,2);print(x);
--- orig.js 2026-10-17 16:58:19.058146079 -0400 +++ fixed.js 2026-10-17 16:58:19.349149963 -0400 @@ -1 +1 @@ -function f(a=0,b,c,d){if(arguments.length>2){return;}else{g();}}var x=0;function g(){x++};f(1,2);print(x); +function f(a=0,b,c,d){if(arguments.length>2){return;}g();}var x=0;function g(){x++};f(1,2);print(x);
r1-p002 · block-scoped function hoisting (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-0)
1let r='';{function h(){r+='H'}}switch(1){case 1:{if(true){return;}else{h();}}break;}print(r);
r1-p003 · comment loss on else
1let out='';function m(c){if(!c){return /*keep*/;}else/*start*/{out+=/*a*/'x'/*end*/;}/*after*/}m(false);m(true);print(out);
--- orig.js 2026-10-17 16:58:19.566152860 -0400 +++ fixed.js 2026-10-17 16:58:19.835156451 -0400 @@ -1 +1 @@ -let out='';function m(c){if(!c){return /*keep*/;}else/*start*/{out+=/*a*/'x'/*end*/;}/*after*/}m(false);m(true);print(out); +let out='';function m(c){if(!c){return /*keep*/;}/*start*/out+=/*a*/'x'/*end*/;}m(false);m(true);print(out);
r1-p004 · changed try-catch nesting (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-0)
1try{try{if(x){return;}else{throw 'E';}}catch(e){out+='inner'+e;}}catch(e){out+='outer'+e;}var out='',x=0;print(out);
r1-p005 · async generator yield position
1async function* g(){if(1){return;}else{yield 2;}}const it=g();it.next().then(r=>out+=r.value?''+r.value:'');it.next().then(r=>out+=','+(r.done?'done':r.value));var out='';Promise.resolve().then(()=>print(out));
--- orig.js 2026-10-17 16:58:20.045159254 -0400 +++ fixed.js 2026-10-17 16:58:20.330163056 -0400 @@ -1 +1 @@ -async function* g(){if(1){return;}else{yield 2;}}const it=g();it.next().then(r=>out+=r.value?''+r.value:'');it.next().then(r=>out+=','+(r.done?'done':r.value));var out='';Promise.resolve().then(()=>print(out)); +async function* g(){if(1){return;}yield 2;}const it=g();it.next().then(r=>out+=r.value?''+r.value:'');it.next().then(r=>out+=','+(r.done?'done':r.value));var out='';Promise.resolve().then(()=>print(out));
r1-p006 · switch case fallthrough
1let s='';switch(1){case 1:if(0){break;}else{s+='a';}case 2:s+='b';break;}print(s);
--- orig.js 2026-10-17 16:58:20.486165137 -0400 +++ fixed.js 2026-10-17 16:58:20.738168499 -0400 @@ -1 +1 @@ -let s='';switch(1){case 1:if(0){break;}else{s+='a';}case 2:s+='b';break;}print(s); +let s='';switch(1){case 1:if(0){break;}s+='a';case 2:s+='b';break;}print(s);
r1-p007 · nullish vs falsy (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-0)
1let v=null;if(v==null){return;}else{v=1;}print(v===null);
r1-p008 · regex lastIndex sticky (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-0)
1let r=/a/g;r.lastIndex=0;if(r.exec('ba')){return;}else{r.lastIndex=0;out+=r.exec('ba')[0];}var out='';print(out);
r2-p001 · Var declared in else accessed before init
1function f() {
2 console.log(x); // Hazard: x used before init
3 if (false) {
4 return;
5 } else {
6 var x = 1;
7 }
8 return x;
9}
10f();
--- orig.js 2026-10-17 16:58:27.865263378 -0400 +++ fixed.js 2026-10-17 16:58:28.143267071 -0400 @@ -2,9 +2,8 @@ console.log(x); // Hazard: x used before init if (false) { return; - } else { - var x = 1; } + var x = 1; return x; } f();
undefined
r2-p002 · Comment preservation hazard
1function f() {
2 if (true) {
3 return; /* keep */
4 } else {
5 // this comment should stay with g()
6 g();
7 }
8}
9function g() { console.log('g'); }
10f();
--- orig.js 2026-10-17 16:58:28.310269288 -0400 +++ fixed.js 2026-10-17 16:58:28.555272542 -0400 @@ -1,10 +1,9 @@ function f() { if (true) { return; /* keep */ - } else { + } // this comment should stay with g() g(); - } } function g() { console.log('g'); } f();
r2-p003 · Continue applies to wrong loop
1let out = '';
2outer: for (let i = 0; i < 2; i++) {
3 inner: for (let j = 0; j < 2; j++) {
4 if (i === 0) {
5 continue outer; /* else block */
6 } else {
7 out += j;
8 }
9 }
10}
11console.log(out);
--- orig.js 2026-10-17 16:58:28.711274614 -0400 +++ fixed.js 2026-10-17 16:58:28.986278266 -0400 @@ -3,9 +3,8 @@ inner: for (let j = 0; j < 2; j++) { if (i === 0) { continue outer; /* else block */ - } else { - out += j; } + out += j; } } console.log(out);
01
r2-p004 · Comment on else keyword lost
1function f() {
2 if (false) {
3 return;
4 } /* else */ else {
5 console.log('else ran');
6 }
7}
8f();
--- orig.js 2026-10-17 16:58:29.163280616 -0400 +++ fixed.js 2026-10-17 16:58:29.430284160 -0400 @@ -1,8 +1,7 @@ function f() { if (false) { return; - } /* else */ else { - console.log('else ran'); } + console.log('else ran'); } f();
else ran
r2-p005 · Execution reordering relative to siblings
1let log = [];
2function f() {
3 log.push('start');
4 if (false) {
5 return;
6 } else {
7 log.push('else');
8 }
9 log.push('end');
10 return log.join(',');
11}
12console.log(f());
--- orig.js 2026-10-17 16:58:29.593286323 -0400 +++ fixed.js 2026-10-17 16:58:29.860289867 -0400 @@ -3,9 +3,8 @@ log.push('start'); if (false) { return; - } else { - log.push('else'); } + log.push('else'); log.push('end'); return log.join(','); }
start,else,end
r2-p006 · Var hoisting and redeclaration in loop
1function f() {
2 for (let i = 0; i < 2; i++) {
3 if (i === 0) {
4 continue;
5 } else {
6 var x = i;
7 }
8 console.log(x); // Hazard: x from previous iteration
9 }
10}
11f();
--- orig.js 2026-10-17 16:58:30.030292123 -0400 +++ fixed.js 2026-10-17 16:58:30.294295625 -0400 @@ -2,9 +2,8 @@ for (let i = 0; i < 2; i++) { if (i === 0) { continue; - } else { - var x = i; } + var x = i; console.log(x); // Hazard: x from previous iteration } }
1
r2-p007 · Arguments object mutation hazard
1function f(a, b = 0) {
2 if (arguments.length === 0) {
3 return;
4 } else {
5 b = arguments[0];
6 }
7 return b;
8}
9console.log(f(5));
--- orig.js 2026-10-17 16:58:30.467297920 -0400 +++ fixed.js 2026-10-17 16:58:30.737301502 -0400 @@ -1,9 +1,8 @@ function f(a, b = 0) { if (arguments.length === 0) { return; - } else { - b = arguments[0]; } + b = arguments[0]; return b; } console.log(f(5));
5
r2-p008 · Label/break interaction with nested blocks
1let log = '';
2outer: {
3 if (false) {
4 break outer;
5 } else {
6 log += 'a';
7 }
8 log += 'b';
9}
10console.log(log);
--- orig.js 2026-10-17 16:58:30.904303717 -0400 +++ fixed.js 2026-10-17 16:58:31.194307563 -0400 @@ -2,9 +2,8 @@ outer: { if (false) { break outer; - } else { - log += 'a'; } + log += 'a'; log += 'b'; } console.log(log);
ab