fixhunt

eslint no-lonely-if

labelled safe
Runs / 2026-10-22T18-18-06-091Z-yztj-eslint-no-lonely-ifeslint 9.39.52026-10-22 18:18 UTC8 programs × 2 round(s)16 programs, 11 fired, 9 fixed, 0 findings, 0 rejectedmodel cost $0.0038

Analysis Nemotron 3 Super

The rule flags when an `if` statement is the sole content of an `else` block.

Fix: Rewrites the lonely `if` inside an `else` block into an `else if` chain by removing the outer `else` braces and placing the inner `if` directly after `else`, preserving all inner statement content and formatting.

12 hazards
  • Moving comments attached to the outer `else` brace or the inner `if` line may alter comment association or readability.
  • If the inner `if` contains a `break` or `continue`, the rewrite changes which loop or switch construct it affects due to altered nesting depth.
  • Labels placed on the outer `else` block become unreachable or misapplied after the rewrite, breaking `goto`-like control flow.
  • In strict mode, if the inner `if` declares a function, hoisting behavior shifts because the block scope is removed.
  • If the `else` block is preceded by a trailing comment on the same line, the rewrite may detach or reattach that comment incorrectly.
  • When the inner `if` is part of a larger ternary expression via implicit return (e.g., arrow functions), the rewrite breaks expression validity.
  • If the outer `if` lacks braces and the `else` block is a single-line lonely `if`, the fix produces invalid syntax by creating an `else if` without proper delimiting.
  • In environments with automatic semicolon insertion, removing braces may cause the following statement to be incorrectly bound to the `else if`.
  • If the inner `if` is immediately followed by a comment-only line that relies on the block for visual grouping, the rewrite may affect code maintenance but not execution.
  • Destructuring parameters in the outer function scope that shadow variables declared in the lonely `if` may experience altered temporal dead zone interactions due to scope flattening.
  • If the lonely `if` contains a `yield` or `await`, the rewrite does not change behavior, but tooling that expects block-scoped async generators may misanalyze the construct.
  • In JSX or template literal contexts embedded within the statement, the rewrite does not apply, but if misapplied by a plugin, it would break HTML or string structure.

Programs

idresulttitlehazard
r1-p001silent prediction offLabel reachability broken by else-if rewriteLabels placed on the outer `else` block become unreachable or misapplied after the rewrite, breaking `goto`-like control flow.
r1-p002fix ok__proto__ key handling in lonely if under else__proto__ keys, prototype access, Object.assign vs spread
r1-p003fired, no fixComment attachment altered by brace removalMoving comments attached to the outer `else` brace or the inner `if` line may alter comment association or readability.
r1-p004silent prediction offMissing braces in outer if with lonely else-ifIf the outer `if` lacks braces and the `else` block is a single-line lonely `if`, the fix produces invalid syntax by creating an `else if` without proper delimiting.
r1-p005fix okNull vs undefined in condition of lonely ifnull/undefined distinction, optional chaining short-circuit, nullish vs falsy
r1-p006fix okAsync await ordering affected by else-if rewriteasync/await ordering, generators and yield position
r1-p007fix okYield in lonely if under else - generator analysisIf the lonely `if` contains a `yield` or `await`, the rewrite does not change behavior, but tooling that expects block-scoped async generators may misanalyze the construct.
r1-p008fix okSparse array access in lonely if under elsesparse arrays, holes, Array(n) vs [n], array-likes
r2-p001fix okCoercion: + vs template literalnumeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt
r2-p002fix okVar hoisting redeclarationvar hoisting and re-declaration inside loops or after use
r2-p003silent prediction offASI binding hazardIn environments with automatic semicolon insertion, removing braces may cause the following statement to be incorrectly bound to the `else if`.
r2-p004silentTernary in arrow functionWhen the inner `if` is part of a larger ternary expression via implicit return (e.g., arrow functions), the rewrite breaks expression validity.
r2-p005fix okTemporal dead zonetemporal dead zone after code is hoisted or reordered
r2-p006fired, no fixTrailing comment detachmentIf the `else` block is preceded by a trailing comment on the same line, the rewrite may detach or reattach that comment incorrectly.
r2-p007fix okAsync await orderingasync/await ordering, generators and yield position
r2-p008silentJSX/template literal contextIn JSX or template literal contexts embedded within the statement, the rewrite does not apply, but if misapplied by a plugin, it would break HTML or string structure.

Sources

r1-p001 · Label reachability broken by else-if rewrite
1function test() {
2 let x = 0;
3 outer: if (false) {
4 x = 1;
5 } else {
6 inner: if (true) {
7 x += 2;
8 break inner;
9 }
10 }
11 if (x === 2) console.log('label worked');
12 else console.log('label failed:', x);
13}
14test();
label worked
r1-p002 · __proto__ key handling in lonely if under else
1const obj = {};
2if ('a' in obj) {
3 obj.a = 1;
4} else {
5 if ('__proto__' in obj) {
6 obj.__proto__ = {toString:()=>'proto'};
7 console.log(obj.toString());
8 }
9}
10console.log('__proto__ present:', '__proto__' in obj);
--- orig.js	2026-10-22 14:18:21.354748569 -0400
+++ fixed.js	2026-10-22 14:18:29.125034921 -0400
@@ -1,10 +1,8 @@
 const obj = {};
 if ('a' in obj) {
   obj.a = 1;
-} else {
-  if ('__proto__' in obj) {
+} else if ('__proto__' in obj) {
     obj.__proto__ = {toString:()=>'proto'};
     console.log(obj.toString());
   }
-}
 console.log('__proto__ present:', '__proto__' in obj);
proto
__proto__ present: true
r1-p003 · Comment attachment altered by brace removal
1function f() {
2 let flag = false;
3 // outer-else-brace-comment
4 if (!flag) {
5 flag = true;
6 } else {
7 // inner-if-line-comment
8 if (flag) {
9 console.log('inner taken');
10 }
11 }
12 console.log('flag:', flag);
13}
14f();
flag: true
r1-p004 · Missing braces in outer if with lonely else-if
1if (false)
2 console.log('not here');
3else
4 if (true) {
5 console.log('lonely if in else');
6 console.log('second line');
7 }
8console.log('done');
lonely if in else
second line
done
r1-p005 · Null vs undefined in condition of lonely if
1let val = null;
2if (val !== undefined) {
3 console.log('defined');
4} else {
5 if (val == null) {
6 console.log('nullish');
7 }
8}
9// Change val to undefined to see difference
10val = undefined;
11if (val !== undefined) {
12 console.log('defined');
13} else {
14 if (val == null) {
15 console.log('nullish');
16 }
17}
--- orig.js	2026-10-22 14:18:21.357748679 -0400
+++ fixed.js	2026-10-22 14:18:23.446825446 -0400
@@ -1,17 +1,13 @@
 let val = null;
 if (val !== undefined) {
   console.log('defined');
-} else {
-  if (val == null) {
+} else if (val == null) {
     console.log('nullish');
   }
-}
 // Change val to undefined to see difference
 val = undefined;
 if (val !== undefined) {
   console.log('defined');
-} else {
-  if (val == null) {
+} else if (val == null) {
     console.log('nullish');
   }
-}
defined
nullish
r1-p006 · Async await ordering affected by else-if rewrite
1async function test() {
2 let ok = false;
3 if (ok) {
4 await Promise.resolve(1);
5 } else {
6 if (!ok) {
7 const res = await Promise.resolve(2);
8 console.log('got', res);
9 }
10 }
11 console.log('end');
12}
13test().catch(e => console.error(e));
--- orig.js	2026-10-22 14:18:21.358748716 -0400
+++ fixed.js	2026-10-22 14:18:25.364896072 -0400
@@ -2,12 +2,10 @@
   let ok = false;
   if (ok) {
     await Promise.resolve(1);
-  } else {
-    if (!ok) {
+  } else if (!ok) {
       const res = await Promise.resolve(2);
       console.log('got', res);
     }
-  }
   console.log('end');
 }
 test().catch(e => console.error(e));
got 2
end
r1-p007 · Yield in lonely if under else - generator analysis
1function* gen() {
2 yield 1;
3 if (false) {
4 yield 2;
5 } else {
6 if (true) {
7 yield 3;
8 }
9 }
10 yield 4;
11}
12const it = gen();
13console.log([...it].join(','));
--- orig.js	2026-10-22 14:18:24.514864756 -0400
+++ fixed.js	2026-10-22 14:18:26.600941659 -0400
@@ -2,11 +2,9 @@
   yield 1;
   if (false) {
     yield 2;
-  } else {
-    if (true) {
+  } else if (true) {
       yield 3;
     }
-  }
   yield 4;
 }
 const it = gen();
1,3,4
r1-p008 · Sparse array access in lonely if under else
1const arr = Array(3);
2arr[1] = 'present';
3for (let i = 0; i < 3; i++) {
4 if (i in arr) {
5 console.log('has', arr[i]);
6 } else {
7 if (i === 0) {
8 console.log('hole at', i);
9 }
10 }
11}
--- orig.js	2026-10-22 14:18:25.236891354 -0400
+++ fixed.js	2026-10-22 14:18:28.378007297 -0400
@@ -3,9 +3,7 @@
 for (let i = 0; i < 3; i++) {
   if (i in arr) {
     console.log('has', arr[i]);
-  } else {
-    if (i === 0) {
+  } else if (i === 0) {
       console.log('hole at', i);
     }
-  }
 }
hole at 0
has present
r2-p001 · Coercion: + vs template literal
1let x = { valueOf: () => 1 };
2let y = { toString: () => '2' };
3function test(a, b) {
4 if (a == 0) {
5 return String(a) + String(b);
6 } else {
7 if (b == '2') {
8 return `${a}` + `${b}`;
9 }
10 }
11}
12console.log(test(0, y));
13console.log(test(x, y));
--- orig.js	2026-10-22 14:18:37.510346425 -0400
+++ fixed.js	2026-10-22 14:18:41.233485537 -0400
@@ -3,11 +3,9 @@
 function test(a, b) {
   if (a == 0) {
     return String(a) + String(b);
-  } else {
-    if (b == '2') {
+  } else if (b == '2') {
       return `${a}` + `${b}`;
     }
-  }
 }
 console.log(test(0, y));
 console.log(test(x, y));
02
[object Object]2
r2-p002 · Var hoisting redeclaration
1for (let i = 0; i < 2; i++) {
2 if (i === 0) {
3 console.log('first');
4 } else {
5 if (true) {
6 var x = i;
7 console.log(x);
8 }
9 }
10}
11// After loop, x should be 1 due to hoisting
12console.log(typeof x === 'number' ? x : 'undef');
--- orig.js	2026-10-22 14:18:37.511346463 -0400
+++ fixed.js	2026-10-22 14:18:40.138444569 -0400
@@ -1,12 +1,10 @@
 for (let i = 0; i < 2; i++) {
   if (i === 0) {
     console.log('first');
-  } else {
-    if (true) {
+  } else if (true) {
       var x = i;
       console.log(x);
     }
-  }
 }
 // After loop, x should be 1 due to hoisting
 console.log(typeof x === 'number' ? x : 'undef');
first
1
1
r2-p003 · ASI binding hazard
1let flag = 0;
2if (flag) {
3 console.log('a');
4} else {
5 if (flag === 0) console.log('b')
6 console.log('c');
7}
8// Expected: logs 'b' then 'c'.
9// If ASI binds 'c' to else if, 'c' might not print.
b
c
r2-p004 · Ternary in arrow function
1const f = (a, b) =>
2 a > 0
3 ? a + b
4 : (() => {
5 if (b === 0) return 0;
6 if (b > 0) return b;
7 })();
8try {
9 console.log(f(-1, 5));
10} catch (e) {
11 console.log(e.name + ': ' + e.message);
12}
5
r2-p005 · Temporal dead zone
1function test() {
2 if (false) {
3 let x = 1;
4 } else {
5 if (true) {
6 console.log(typeof x);
7 let x = 2;
8 }
9 }
10}
11try {
12 test();
13} catch (e) {
14 console.log(e.name + ': ' + e.message);
15}
--- orig.js	2026-10-22 14:18:37.512346500 -0400
+++ fixed.js	2026-10-22 14:18:40.752467536 -0400
@@ -1,12 +1,10 @@
 function test() {
   if (false) {
     let x = 1;
-  } else {
-    if (true) {
+  } else if (true) {
       console.log(typeof x);
       let x = 2;
     }
-  }
 }
 try {
   test();
ReferenceError: Cannot access 'x' before initialization
r2-p006 · Trailing comment detachment
1let a = 0;
2if (a) {/* unreachable */} else { // this comment should stay
3 if (a === 0) console.log('zero');
4}
5// Output should show comment proximity affects nothing, but we log to see if
6// formatting change alters execution (it shouldn't, but we check)
7console.log('done');
zero
done
r2-p007 · Async await ordering
1async function fn() {
2 if (false) {
3 return 1;
4 } else {
5 if (true) {
6 await Promise.resolve(2);
7 return 3;
8 }
9 }
10}
11fn().then(v => console.log('result:', v));
--- orig.js	2026-10-22 14:18:40.020440155 -0400
+++ fixed.js	2026-10-22 14:18:43.414567258 -0400
@@ -1,11 +1,9 @@
 async function fn() {
   if (false) {
     return 1;
-  } else {
-    if (true) {
+  } else if (true) {
       await Promise.resolve(2);
       return 3;
     }
-  }
 }
 fn().then(v => console.log('result:', v));
result: 3
r2-p008 · JSX/template literal context
1const cond = false;
2const html = cond
3 ? '<div>yes</div>'
4 : (() => {
5 if (true) return '<span>no</span>';
6 return '';
7 })();
8console.log(html);
<span>no</span>