eslint no-else-return
labelled safe
Analysis Nemotron 3 Super
The rule flags `else` blocks immediately following an `if` block that contains a `return` statement.
Fix: Removes the `else` block and places its contents directly after the `if` block, preserving indentation and statements.
8 hazards
- The rewrite fails when the `else` block contains a `break` or `continue` statement, as these are only valid inside loops and would cause a syntax error when moved outside.
- Moving a `throw` statement from the `else` block changes control flow by skipping any `finally` blocks that would have been executed had the `else` remained within the original function scope.
- If the `else` block declares a function using a function declaration, hoisting behavior changes because the function is no longer conditionally defined and may be accessible earlier than intended.
- When the `else` block contains a `var` declaration, the variable becomes function-scoped and may be accessed before the `if` block due to hoisting, altering timing-dependent behavior.
- Labels inside the `else` block become invalid after the rewrite if the surrounding context is not a loop or switch, causing a syntax error due to misplaced label usage.
- The rewrite incorrectly handles empty `else` blocks by leaving an unnecessary empty block or semicolon, which may affect automatic semicolon insertion in edge cases involving line breaks.
- If the `else` block contains a `debugger` statement, its execution timing relative to prior expressions in the `if` block remains the same, but surrounding tooling or profiling that depends on block structure may behave differently.
- Comments attached to the `else` keyword or braces may be lost during the rewrite, as the autofix typically operates on syntactic tokens and does not preserve comment attachment in all formatter contexts.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | invalid | Hoisting vulnerability from var redeclaration | var hoisting and re-declaration inside loops or after use |
| r1-p002 | fix ok | Comment loss on else keyword | Comments attached to the `else` keyword or braces may be lost during the rewrite |
| r1-p003 | silent prediction off | Empty else block semicolon hazard | The rewrite incorrectly handles empty `else` blocks by leaving an unnecessary empty block or semicolon |
| r1-p004 | fix ok | Label interaction with nested return | label/break/continue control flow, finally blocks, early returns in nested blocks |
| r1-p005 | fix ok | Var hoisting timing alteration | When the `else` block contains a `var` declaration, the variable becomes function-scoped and may be accessed before the `if` block due to hoisting |
| r1-p006 | fix ok | Comment attachment on moved code | comments attached to the removed or moved code |
| r1-p007 | fix ok | Sparse array interaction with else rewrite | sparse arrays, holes, Array(n) vs [n], array-likes |
| r1-p008 | fix ok | __proto__ key interference in object mutation | __proto__ keys, prototype access, Object.assign vs spread |
| r2-p001 | fix ok | Strict vs Sloppy Mode Semantics Hazard | strict vs sloppy mode semantics |
| r2-p002 | fix ok | This Binding: Arrow vs Function Hazard | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
| r2-p003 | invalid | Break/Continue Outside Loop Hazard | The rewrite fails when the `else` block contains a `break` or `continue` statement, as these are only valid inside loops and would cause a syntax error when moved outside. |
| r2-p004 | fix ok | Regular Expressions: Escapes, Unicode Flag, LastIndex Hazard | regular expressions: escapes, unicode flag, lastIndex with the g flag |
| r2-p005 | fix ok | Debugger Statement Timing Hazard | If the `else` block contains a `debugger` statement, its execution timing relative to prior expressions in the `if` block remains the same, but surrounding tooling or profiling that depends on block s |
| r2-p006 | invalid | Empty Else Block Semicolon Insertion Hazard | The rewrite incorrectly handles empty `else` blocks by leaving an unnecessary empty block or semicolon, which may affect automatic semicolon insertion in edge cases involving line breaks. |
| r2-p007 | fix ok | Getters, Setters and Object.defineProperty Side Effects Hazard | getters, setters and Object.defineProperty side effects |
| r2-p008 | fix ok | Numeric/String Coercion: + vs Template Literal Hazard | numeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt |
Sources
r1-p001 · Hoisting vulnerability from var redeclaration (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-15-3)
1function test() {
2 let result = [];
3 for (let i = 0; i;; break) {
4 var x = 1;
5 if (i === 0) { return x; } // Line A
6 else { // Hazard: else with var decl
7 console.log('else', x);
8 var x = 99; // Redeclaration - hoisted!
9 x++;
10 }
11 result.push(x);
12 if (result.length > 2) break;
13 i++;
14 }
15 return result.join(',');
16}
17console.log(test());
r1-p002 · Comment loss on else keyword
1function logValue(flag) {
2 const out = [];
3 if (flag) { // Enter condition
4 return 42; // Magic number
5 } /* ignore */ // Comment on brace
6 else // <- Critical: comment on else keyword
7 { // Brace with potential comment loss
8 /* preserved */ // This should survive
9 out.push('hi');
10 }
11 out.push(flag ? 't' : 'f');
12 return out.join('|');
13}
14console.log(logValue(true));
15console.log(logValue(false));
--- orig.js 2026-10-22 14:15:49.613714250 -0400 +++ fixed.js 2026-10-22 14:15:53.579829312 -0400 @@ -3,11 +3,10 @@ if (flag) { // Enter condition return 42; // Magic number } /* ignore */ // Comment on brace - else // <- Critical: comment on else keyword - { // Brace with potential comment loss + // Brace with potential comment loss /* preserved */ // This should survive out.push('hi'); - } + out.push(flag ? 't' : 'f'); return out.join('|'); }
42 hi|f
r1-p003 · Empty else block semicolon hazard
1function checkEmpty(a) {
2 try {
3 if (a < 0) { throw 'neg'; }
4 else {} // Hazard: empty else
5 return 'ok' + a;
6 } catch (e) {
7 return 'bad' + typeof e;
8 }
9}
10console.log(checkEmpty(-1));
11console.log(checkEmpty(5));
badstring ok5
r1-p004 · Label interaction with nested return
1function process(data) {
2 outer: for (let i = 0; i < 3; i++) {
3 if (data[i] === undefined) { continue outer; }
4 if (data[i] === null) { // Trigger point
5 return i * 2; // Returns from loop
6 }
7 else { // Hazard: else after return
8 break outer; // Breaks labeled loop
9 }
10 console.log('skipped', i); // Should not print
11 }
12 return -1;
13}
14console.log(process([1, null, 3]));
--- orig.js 2026-10-22 14:15:49.615714307 -0400 +++ fixed.js 2026-10-22 14:15:53.018812963 -0400 @@ -4,9 +4,9 @@ if (data[i] === null) { // Trigger point return i * 2; // Returns from loop } - else { // Hazard: else after return + // Hazard: else after return break outer; // Breaks labeled loop - } + console.log('skipped', i); // Should not print } return -1;
-1
r1-p005 · Var hoisting timing alteration
1function timingTest(val) {
2 console.log('start:', typeof y); // Hazard: accessing hoisted var
3 if (val > 0) { // Trigger: if with return
4 return y * 2;
5 }
6 else { // Else with var decl
7 var y = 5; // Hoisted to top
8 y += val;
9 return y;
10 }
11}
12console.log(timingTest(3));
13console.log(timingTest(-1));
--- orig.js 2026-10-22 14:15:49.616714336 -0400 +++ fixed.js 2026-10-22 14:15:53.593829720 -0400 @@ -3,11 +3,11 @@ if (val > 0) { // Trigger: if with return return y * 2; } - else { // Else with var decl + // Else with var decl var y = 5; // Hoisted to top y += val; return y; - } + } console.log(timingTest(3)); console.log(timingTest(-1));
start: undefined NaN start: undefined 4
r1-p006 · Comment attachment on moved code
1function processFlag(mode) {
2 const log = [];
3 if (mode === 'fast') { // fast path
4 return 100; // <- value 100
5 }
6 else { // <- Hazard: comment-rich else
7 /* step 1 */ // Comment on statement
8 log.push('start'); // inline comment
9 /* step 2 */
10 mode.toUpperCase(); // side effect
11 return log.join('|');
12 }
13 log.push('end'); // Should not execute
14 return log.length;
15}
16console.log(processFlag('fast'));
17console.log(processFlag('slow'));
--- orig.js 2026-10-22 14:15:49.616714336 -0400 +++ fixed.js 2026-10-22 14:15:51.979782756 -0400 @@ -3,13 +3,13 @@ if (mode === 'fast') { // fast path return 100; // <- value 100 } - else { // <- Hazard: comment-rich else + // <- Hazard: comment-rich else /* step 1 */ // Comment on statement log.push('start'); // inline comment /* step 2 */ mode.toUpperCase(); // side effect return log.join('|'); - } + log.push('end'); // Should not execute return log.length; }
100 start
r1-p007 · Sparse array interaction with else rewrite
1function sparseCheck(arr) {
2 let sum = 0;
3 for (let i = 0; i < arr.length; i++) {
4 if (arr[i] === undefined) { // Trigger: hole detection
5 return sum; // Returns early on hole
6 }
7 else { // Hazard: else after return
8 sum += arr[i] || 0; // Would skip if rewritten poorly
9 }
10 }
11 return sum;
12}
13const sparse = [1, , 3]; // Hole at index 1
14console.log(sparseCheck(sparse));
15console.log(sparseCheck([1,2,3]));
--- orig.js 2026-10-22 14:15:49.660715606 -0400 +++ fixed.js 2026-10-22 14:15:53.567828962 -0400 @@ -4,9 +4,9 @@ if (arr[i] === undefined) { // Trigger: hole detection return sum; // Returns early on hole } - else { // Hazard: else after return + // Hazard: else after return sum += arr[i] || 0; // Would skip if rewritten poorly - } + } return sum; }
1 6
r1-p008 · __proto__ key interference in object mutation
1function protoTest(obj) {
2 if ('__proto__' in obj) { // Trigger: proto check
3 return obj.__proto__; // Returns prototype
4 }
5 else { // Hazard: else block
6 obj.custom = 'added'; // Mutates input
7 return obj;
8 }
9}
10const map = { a: 1 };
11console.log('before:', map.custom);
12const result = protoTest(map); // Modifies map
13console.log('after:', map.custom);
14console.log('result:', result.custom);
--- orig.js 2026-10-22 14:15:52.255790779 -0400 +++ fixed.js 2026-10-22 14:15:56.205906079 -0400 @@ -2,10 +2,10 @@ if ('__proto__' in obj) { // Trigger: proto check return obj.__proto__; // Returns prototype } - else { // Hazard: else block + // Hazard: else block obj.custom = 'added'; // Mutates input return obj; - } + } const map = { a: 1 }; console.log('before:', map.custom);
before: undefined after: undefined result: undefined
r2-p001 · Strict vs Sloppy Mode Semantics Hazard
1"use strict";
2let result = [];
3function test(x) {
4 if (x === 0) { return "zero"; }
5 else {
6 try { eval("arguments[0] = 99"); }
7 catch(e) { result.push(e.message); }
8 result.push(x);
9 }
10}
11test(5);
12test(0);
13console.log(result.join("|"));
--- orig.js 2026-10-22 14:16:07.021227045 -0400 +++ fixed.js 2026-10-22 14:16:10.442330153 -0400 @@ -2,11 +2,11 @@ let result = []; function test(x) { if (x === 0) { return "zero"; } - else { + try { eval("arguments[0] = 99"); } catch(e) { result.push(e.message); } result.push(x); - } + } test(5); test(0);
5
r2-p002 · This Binding: Arrow vs Function Hazard
1const obj = { val: 42, method() { return this.val; }, arrow: () => this.val };
2let logs = [];
3function wrapper(fn) {
4 if (typeof fn === "function") { return fn.call({val: 99}); }
5 else { logs.push(fn()); }
6}
7wrapper(obj.method);
8wrapper(obj.arrow);
9console.log(logs.length ? logs[0] : "none");
--- orig.js 2026-10-22 14:16:07.022227075 -0400 +++ fixed.js 2026-10-22 14:16:10.444330214 -0400 @@ -2,7 +2,7 @@ let logs = []; function wrapper(fn) { if (typeof fn === "function") { return fn.call({val: 99}); } - else { logs.push(fn()); } + logs.push(fn()); } wrapper(obj.method); wrapper(obj.arrow);
none
r2-p003 · Break/Continue Outside Loop Hazard (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-15-3)
1try {
2 if (false) { return 1; }
3 else { break; }
4} catch (e) {
5 console.log(e.name + ":" + e.message);
6}
r2-p004 · Regular Expressions: Escapes, Unicode Flag, LastIndex Hazard
1let str = "aßç🌟d", logs = [], re = /[a-zßç🌟]/gu;
2function testChar(ch) {
3 if (!re.test(ch)) { return false; }
4 else { logs.push(re.lastIndex); re.lastIndex = 0; }
5}
6for (const c of str) testChar(c);
7console.log(logs.join(","));
--- orig.js 2026-10-22 14:16:07.023227105 -0400 +++ fixed.js 2026-10-22 14:16:09.856312436 -0400 @@ -1,7 +1,7 @@ let str = "aßç🌟d", logs = [], re = /[a-zßç🌟]/gu; function testChar(ch) { if (!re.test(ch)) { return false; } - else { logs.push(re.lastIndex); re.lastIndex = 0; } + logs.push(re.lastIndex); re.lastIndex = 0; } for (const c of str) testChar(c); console.log(logs.join(","));
1,1,1,2,1
r2-p005 · Debugger Statement Timing Hazard
1let log = "";
2function check(val) {
3 if (val < 0) { log += "neg"; return; }
4 else { debugger; log += "pos"; }
5}
6check(-1);
7check(1);
8console.log(log);
--- orig.js 2026-10-22 14:16:07.024227135 -0400 +++ fixed.js 2026-10-22 14:16:11.531363129 -0400 @@ -1,7 +1,7 @@ let log = ""; function check(val) { if (val < 0) { log += "neg"; return; } - else { debugger; log += "pos"; } + debugger; log += "pos"; } check(-1); check(1);
negpos
r2-p006 · Empty Else Block Semicolon Insertion Hazard (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-15-3)
1let x = 1
2;
3if (x === 1) { return } else {}
4console.log("reached");
r2-p007 · Getters, Setters and Object.defineProperty Side Effects Hazard
1const obj = {};
2let log = [], val = 10;
3Object.defineProperty(obj, "prop", {
4 get() { log.push("get"); return val; },
5 set(v) { log.push("set:" + v); val = v; }
6});
7function test() {
8 if (obj.prop > 5) { return obj.prop; }
9 else { obj.prop = 20; }
10}
11test();
12console.log(log.join("|") + "|" + val);
--- orig.js 2026-10-22 14:16:07.064228337 -0400 +++ fixed.js 2026-10-22 14:16:11.613365615 -0400 @@ -6,7 +6,7 @@ }); function test() { if (obj.prop > 5) { return obj.prop; } - else { obj.prop = 20; } + obj.prop = 20; } test(); console.log(log.join("|") + "|" + val);
get|get|10
r2-p008 · Numeric/String Coercion: + vs Template Literal Hazard
1const obj = { valueOf() { return 2; }, toString() { return "2" } };
2let results = [];
3function add(x) {
4 if (x === 2) { return x + 3; }
5 else { results.push(String(x) + 3); }
6}
7results.push(add(2));
8add(obj);
9console.log(results.join(""));
--- orig.js 2026-10-22 14:16:07.079228788 -0400 +++ fixed.js 2026-10-22 14:16:11.734369283 -0400 @@ -2,7 +2,7 @@ let results = []; function add(x) { if (x === 2) { return x + 3; } - else { results.push(String(x) + 3); } + results.push(String(x) + 3); } results.push(add(2)); add(obj);
523