fixhunt

biome style/useCollapsedElseIf

labelled safe
Runs / 2026-10-17T20-59-21-536Z-iv32-biome-style_useCollapsedElseIfbiome 2.5.152026-10-17 20:59 UTC8 programs × 2 round(s)16 programs, 7 fired, 5 fixed, 0 findings, 2 rejectedmodel cost $0.0032

Analysis Nemotron 3 Super

Flags when an else block contains exactly one if statement as its sole content.

Fix: Replaces 'else { if(condition) {...} }' with 'else if(condition) {...}', preserving inner block structure and comments.

8 hazards
  • Moving a leading comment inside the else block to before the else if keyword changes its association from the inner if to the else clause, altering documentation intent.
  • If the else block contains a function declaration, hoisting behavior differs between block-scoped (else { function f(){} }) and else-if scoped (else if(...) function f(){}) declarations, potentially causing reference errors.
  • An empty else block ({}) after transformation would be invalid syntax, but the rule prevents this by requiring exactly one if statement.
  • When the inner if is labeled (e.g., else { label: if(x){} }), the label is lost during rewrite, breaking goto-like control flow via break/label.
  • If the else block starts with a directive prologue like 'use strict';, moving it before else if alters its scope from the else block to the else-if construct, potentially changing strict mode behavior.
  • Comments placed between the else brace and the inner if (e.g., else { /* comment */ if(){} }) are relocated to precede the else if, which may misalign them with subsequent code in minified outputs.
  • In environments with non-standard semantic whitespace sensitivity (e.g., certain DSLs or transpiled languages), removing braces could alter tokenization or parsing despite identical JS semantics.
  • If the inner if statement is part of a comma sequence (else { (0, if(x){}) }), the rewrite breaks invalid syntax but such cases are already parse errors; however, the rule assumes valid input where if is a statement, not an expression.

Programs

idresulttitlehazard
r1-p001fix okEmpty Else Block PreventionAn empty else block ({}) after transformation would be invalid syntax, but the rule prevents this by requiring exactly one if statement.
r1-p002fired, no fixComments Attached to Removed/Moved Codecomments attached to the removed or moved code
r1-p003fix okGetters/Setters/defineProperty Side Effectsgetters, setters and Object.defineProperty side effects
r1-p004silent prediction offDirective Prologue Scope ChangeIf the else block starts with a directive prologue like 'use strict';, moving it before else if alters its scope from the else block to the else-if construct, potentially changing strict mode behavior
r1-p005fix okExceptions Inside Removed/Rewritten Codeexceptions thrown inside the code the fix removes or rewrites
r1-p006fix okFunction Declaration Hoisting DifferenceIf the else block contains a function declaration, hoisting behavior differs between block-scoped (else { function f(){} }) and else-if scoped (else if(...) function f(){}) declarations, potentially c
r1-p007fix okName Collision Across Scopes After Movename collisions across scopes after a move (shadowing)
r1-p008silentInner If in Comma Sequence (Invalid Assumed Valid Input)If the inner if statement is part of a comma sequence (else { (0, if(x){}) }), the rewrite breaks invalid syntax but such cases are already parse errors; however, the rule assumes valid input where if
r2-p001silent prediction offFunction Declaration Hoisting Behavior DiffersFunction Declaration Hoisting Difference
r2-p002silent prediction offName Collision Across Scopes After Move (Shadowing)Name Collision Across Scopes After Move (shadowing)
r2-p003fired, no fixComments Relocated Preceding Else If Misaligning With Subsequent CodeComments placed between the else brace and the inner if are relocated to precede the else if, which may misalign them with subsequent code in minified outputs.
r2-p004silent prediction offThis Binding: Arrow vs Function, Methods Extracted, Call/Apply/Bindthis binding: arrow vs function, methods extracted from objects, call/apply/bind
r2-p005silent prediction offClass Fields, Static Blocks, Private Names, Super Callsclass fields, static blocks, private names, super calls
r2-p006silent prediction offFunction Declarations Inside Blocks, Switch Cases, Labeled Statementsfunction declarations inside blocks, switch cases, labeled statements
r2-p007invalidLabel/Break/Continue Control Flow, Finally Blocks, Early Returns in Nested Blockslabel/break/continue control flow, finally blocks, early returns in nested blocks
r2-p008invalidStrict vs Sloppy Mode Semanticsstrict vs sloppy mode semantics

Sources

r1-p001 · Empty Else Block Prevention
1let x = 1;
2if (false) {} else {
3 if (true) { x += 1; }
4}
5console.log(x); // Should be 2
--- orig.js	2026-10-17 16:59:32.788110634 -0400
+++ fixed.js	2026-10-17 16:59:33.076114328 -0400
@@ -1,5 +1,3 @@
 let x = 1;
-if (false) {} else {
-  if (true) { x += 1; }
-}
+if (false) {} else if (true) { x += 1; }
 console.log(x); // Should be 2
2
r1-p002 · Comments Attached to Removed/Moved Code
1let y = 0;
2if (true) {} else {/* keep me */
3 if (false) { y = 99; }
4}
5console.log(y); // Should stay 0 due to false condition
0
r1-p003 · Getters/Setters/defineProperty Side Effects
1const obj = {};
2Object.defineProperty(obj, 'prop', {
3 get() { return this._val || 0; },
4 set(v) { this._val = v * 2; }
5});
6if (false) {} else {
7 if (true) { obj.prop = 5; }
8}
9console.log(obj.prop); // Should be 10 due to setter doubling
--- orig.js	2026-10-17 16:59:33.525120084 -0400
+++ fixed.js	2026-10-17 16:59:33.801123622 -0400
@@ -3,7 +3,5 @@
   get() { return this._val || 0; },
   set(v) { this._val = v * 2; }
 });
-if (false) {} else {
-  if (true) { obj.prop = 5; }
-}
+if (false) {} else if (true) { obj.prop = 5; }
 console.log(obj.prop); // Should be 10 due to setter doubling
10
r1-p004 · Directive Prologue Scope Change
1(function() {
2 'use strict';
3 let z = 1;
4 if (false) {} else {
5 'use strict';
6 try { eval('z = 2'); } catch(e) { z = 3; }
7 }
8 console.log(z);
9})();
2
r1-p005 · Exceptions Inside Removed/Rewritten Code
1let w = 0;
2try {
3 if (false) {} else {
4 if (true) { throw new Error('test'); }
5 }
6} catch(e) {
7 w = 1;
8}
9console.log(w); // Should be 1 due to caught exception
--- orig.js	2026-10-17 16:59:34.207128826 -0400
+++ fixed.js	2026-10-17 16:59:34.476132273 -0400
@@ -1,8 +1,6 @@
 let w = 0;
 try {
-  if (false) {} else {
-    if (true) { throw new Error('test'); }
-  }
+  if (false) {} else if (true) { throw new Error('test'); }
 } catch(e) {
   w = 1;
 }
1
r1-p006 · Function Declaration Hoisting Difference
1let v;
2if (false) {} else {
3 if (true) {
4 function inner() { return 42; }
5 v = inner();
6 }
7}
8console.log(typeof v === 'number' ? v : 'undefined'); // Should be undefined in ES2015+ due to TDZ-like block scoping
--- orig.js	2026-10-17 16:59:34.659134618 -0400
+++ fixed.js	2026-10-17 16:59:34.936138167 -0400
@@ -1,8 +1,6 @@
 let v;
-if (false) {} else {
-  if (true) {
+if (false) {} else if (true) {
     function inner() { return 42; }
     v = inner();
   }
-}
 console.log(typeof v === 'number' ? v : 'undefined'); // Should be undefined in ES2015+ due to TDZ-like block scoping
42
r1-p007 · Name Collision Across Scopes After Move
1let u = 1;
2for (let i = 0; i < 2; i++) {
3 if (i === 0) {} else {
4 if (true) {
5 let u = i + 10;
6 console.log(u); // Should log 11 only once (i=1)
7 }
8 }
9}
--- orig.js	2026-10-17 16:59:35.106140345 -0400
+++ fixed.js	2026-10-17 16:59:35.387143944 -0400
@@ -1,9 +1,7 @@
 let u = 1;
 for (let i = 0; i < 2; i++) {
-  if (i === 0) {} else {
-    if (true) {
+  if (i === 0) {} else if (true) {
       let u = i + 10;
       console.log(u); // Should log 11 only once (i=1)
     }
-  }
 }
11
r1-p008 · Inner If in Comma Sequence (Invalid Assumed Valid Input)
1// This case is a parse error and not valid input; rule assumes if is statement.
2// No trigger expected as per hazard description.
3console.log('no-op');
no-op
r2-p001 · Function Declaration Hoisting Behavior Differs
1let result = [];
2if (false) {
3} else {
4 function f() { return 1; }
5 result.push(typeof f === 'function' ? f() : 'not fn');
6}
7result.push(typeof f === 'function' ? f() : 'global fn');
8console.log(result.join(','));
1,global fn
r2-p002 · Name Collision Across Scopes After Move (Shadowing)
1let x = 1;
2if (false) {
3} else {
4 let x = 2;
5 console.log(x); // should be 2
6}
7console.log(x); // should be 1
8// After fix: else if(false) { let x = 2; ... } -> x is block-scoped to else-if, outer x unchanged
2
1
r2-p003 · Comments Relocated Preceding Else If Misaligning With Subsequent Code
1if (false) {
2} else {
3 /* This comment should stay with the inner if logic */
4 if (true) {
5 console.log('A');
6 }
7}
8console.log('B');
9// After fix: else /* This comment... */ if (true) { ... }
10// In minified output, comment may be detached from logic, but we trace execution:
11// Should print A then B regardless of comment placement.
A
B
r2-p004 · This Binding: Arrow vs Function, Methods Extracted, Call/Apply/Bind
1const obj = { val: 42, getVal: function() { return this.val; } };
2const getVal = obj.getVal;
3if (false) {
4} else {
5 const bound = getVal.bind(obj);
6 console.log(bound()); // should be 42
7}
8// After fix: else if(false) { ... } -> bind still happens in same scope, obj captured
9// So output remains 42
42
r2-p005 · Class Fields, Static Blocks, Private Names, Super Calls
1class Base {
2 static staticVal = 100;
3}
4if (false) {
5} else {
6 class Derived extends Base {
7 static staticVal = 200;
8 static {
9 console.log(Base.staticVal + Derived.staticVal); // 100 + 200 = 300
10 }
11 }
12}
13// After fix: else if(false) { ... } -> static block still runs in same scope
14// So output is 300
300
r2-p006 · Function Declarations Inside Blocks, Switch Cases, Labeled Statements
1let fnResult = null;
2if (false) {
3} else {
4 function helper() { return 99; }
5 switch (helper()) {
6 case 99:
7 fnResult = 'hit';
8 break;
9 }
10}
11console.log(fnResult); // should be 'hit'
12// After fix: else if(false) { ... } -> helper() still defined and callable in same scope
hit
r2-p007 · Label/Break/Continue Control Flow, Finally Blocks, Early Returns in Nested Blocks (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-59-2)
1let count = 0;
2outer:
3if (false) {
4} else {
5 while (count < 2) {
6 if (count === 0) {
7 count++;
8 continue outer;
9 }
10 console.log('inner', count); // should print 'inner 1'
11 break;
12 }
13}
14console.log('done', count); // should be 'done 2'
15// After fix: else if(false) { ... } -> label 'outer' still valid, continue works
r2-p008 · Strict vs Sloppy Mode Semantics (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-59-2)
1"use strict";
2let err = null;
3try {
4 if (false) {
5 } else {
6 function junk() {}
7 junk();
8 delete junk; // TypeError in strict mode
9 }
10} catch (e) {
11 err = e.name + ': ' + e.message;
12}
13console.log(err || 'no error');
14// After fix: else if(false) { ... } -> delete junk still throws in strict mode
15// So output shows error