fixhunt

eslint no-useless-return

labelled safe
Runs / 2026-10-22T18-22-18-911Z-7cmf-eslint-no-useless-returneslint 9.39.52026-10-22 18:22 UTC8 programs × 2 round(s)14 programs, 11 fired, 11 fixed, 0 findings, 2 rejectedmodel cost $0.0045

Analysis Nemotron 3 Super

Flags unnecessary 'return;' statements at the end of functions where execution would naturally fall through.

Fix: Remove the 'return;' statement entirely when it appears as the last statement in a function body and control flow would reach the function end without it.

8 hazards
  • Removing 'return;' in a function that is the target of a label break changes control flow if the label is outside the function.
  • In a function with a finally block, removing a return before the finally may cause the finally block to execute when it previously would not have due to the return skipping it.
  • If the function is called via eval or indirect call, removing the return could affect the value of 'this' in strict mode if the function relies on implicit return undefined vs explicit return.
  • In a function that is a getter, removing an explicit return; may be misinterpreted by tools expecting a value, though runtime behavior remains undefined.
  • If the return statement has a trailing comment, the autofix may drop the comment if not handled properly, altering source formatting.
  • In async functions, removing a return; is safe because it still returns a resolved promise with undefined, but tools may misinterpret intent.
  • In generators, removing a return; is safe as it yields done:true with undefined, but may confuse static analysis expecting explicit termination.
  • If the function is part of a comma-separated expression, removing the return; could change the value produced if the function expression's value is used.

Programs

idresulttitlehazard
r1-p001fix okfinally-execution-after-removed-returnIn a function with a finally block, removing a return before the finally may cause the finally block to execute when it previously would not have due to the return skipping it.
r1-p002fix oktemporal-dead-zone-access-before-initTemporal dead zone after code is hoisted or reordered
r1-p003fix okgetter-return-undefined-vs-implicitIn a function that is a getter, removing an explicit return; may be misinterpreted by tools expecting a value, though runtime behavior remains undefined.
r1-p004fix okproto-assign-spread-side-effects__proto__ keys, prototype access, Object.assign vs spread
r1-p006invalidlabel-break-outside-function-targetRemoving 'return;' in a function that is the target of a label break changes control flow if the label is outside the function.
r1-p008fix oknull-undefined-optional-chaining-nullish-falsynull/undefined distinction, optional chaining short-circuit, nullish vs falsy
r2-p001fix okLabel Break Target Outside FunctionRemoving 'return;' in a function that is the target of a label break changes control flow if the label is outside the function.
r2-p002fix okName Collision After Move Due to Shadowingname collisions across scopes after a move (shadowing)
r2-p003fix okGenerator Return Semantics MisinterpretedIn generators, removing a return; is safe as it yields done:true with undefined, but may confuse static analysis expecting explicit termination.
r2-p004fix okASI Hazard with Leading ParenthesisASI and semicolon-less code, leading ( or [ on the next line
r2-p005silent prediction offNull/Undef Distinction with Optional Chainingnull/undefined distinction, optional chaining short-circuit, nullish vs falsy
r2-p006fix okSparse Array Element Accesssparse arrays, holes, Array(n) vs [n], array-likes
r2-p007fix okAsync Function Return Value MisinterpretationIn async functions, removing a return; is safe because it still returns a resolved promise with undefined, but tools may misinterpret intent.
r2-p008invalidAsync/Await Ordering with Yield Positionasync/await ordering, generators and yield position

Sources

r1-p001 · finally-execution-after-removed-return
1function f() {
2 let executed = false;
3 try {
4 return;
5 } finally {
6 executed = true;
7 }
8}
9f();
10console.log('executed:', executed);
--- orig.js	2026-10-22 14:22:33.775903038 -0400
+++ fixed.js	2026-10-22 14:22:39.586151763 -0400
@@ -1,7 +1,7 @@
 function f() {
   let executed = false;
   try {
-    return;
+    
   } finally {
     executed = true;
   }
r1-p002 · temporal-dead-zone-access-before-init
1function f() {
2 console.log(typeof x);
3 var x = 1;
4 return;
5}
6f(); // logs 'undefined', but if return removed, still same
7// However, to make trigger meaningful: add shadowing
8function g() {
9 console.log(typeof y);
10 let y = 2;
11 return;
12}
13g(); // TDZ: ReferenceError if accessed before init
14// But return removal doesn't affect TDZ
15// Instead, use closure capture:
16let h;
17function outer() {
18 let z = 3;
19 h = () => {
20 console.log(z);
21 return;
22 };
23}
24outer();
25h(); // prints 3
26// Now mutate: remove return? no effect.
27// Better: make return affect whether inner fn is called
28function makeCounter() {
29 let count = 0;
30 return function inner() {
31 console.log(count++);
32 return; // TRIGGER
33 };
34}
35const c = makeCounter();
36c(); // 0
37c(); // 1
38console.log('done');
--- orig.js	2026-10-22 14:22:33.776903080 -0400
+++ fixed.js	2026-10-22 14:22:39.536149621 -0400
@@ -1,14 +1,14 @@
 function f() {
   console.log(typeof x);
   var x = 1;
-  return;
+  
 }
 f(); // logs 'undefined', but if return removed, still same
 // However, to make trigger meaningful: add shadowing
 function g() {
   console.log(typeof y);
   let y = 2;
-  return;
+  
 }
 g(); // TDZ: ReferenceError if accessed before init
 // But return removal doesn't affect TDZ
@@ -18,7 +18,7 @@
   let z = 3;
   h = () => {
     console.log(z);
-    return;
+    
   };
 }
 outer();
@@ -29,7 +29,7 @@
   let count = 0;
   return function inner() {
     console.log(count++);
-    return; // TRIGGER
+     // TRIGGER
   };
 }
 const c = makeCounter();
undefined
r1-p003 · getter-return-undefined-vs-implicit
1const obj = {
2 get prop() {
3 console.log('getter entered');
4 return; // TRIGGER: explicit undefined
5 }
6};
7console.log(obj.prop); // logs 'getter entered', then undefined
8// If return removed, still undefined
9// To show difference: compare with actual return value
10const obj2 = {
11 get prop() {
12 console.log('getter2 entered');
13 return 42;
14 }
15};
16console.log(obj2.prop); // 42
17// Now test: if someone expects value, but gets undefined
18try {
19 const val = obj.prop;
20 if (val === undefined) console.log('got undefined as expected');
21 else console.log('unexpected:', val);
22} catch (e) {
23 console.log('error:', e);
24}
--- orig.js	2026-10-22 14:22:33.777903123 -0400
+++ fixed.js	2026-10-22 14:22:40.037171088 -0400
@@ -1,7 +1,7 @@
 const obj = {
   get prop() {
     console.log('getter entered');
-    return; // TRIGGER: explicit undefined
+     // TRIGGER: explicit undefined
   }
 };
 console.log(obj.prop); // logs 'getter entered', then undefined
getter entered
undefined
getter2 entered
42
getter entered
got undefined as expected
r1-p004 · proto-assign-spread-side-effects
1function f() {
2 const obj = { a: 1 };
3 Object.__proto__.b = 2; // pollute Object.prototype
4 const copy = Object.assign({}, obj);
5 console.log('b in copy:', 'b' in copy); // true due to proto
6 return; // TRIGGER
7}
8f();
9// After fix: return removed, no change
10// But make it depend on whether f ran:
11let ran = false;
12function g() {
13 ran = true;
14 const o = { x: 1 };
15 Object.__proto__.y = 99;
16 const p = { ...o }; // spread ignores proto
17 console.log('y in p:', 'y' in p); // false
18 return; // TRIGGER
19}
20g();
21console.log('ran:', ran);
--- orig.js	2026-10-22 14:22:33.777903123 -0400
+++ fixed.js	2026-10-22 14:22:38.875121299 -0400
@@ -3,7 +3,7 @@
   Object.__proto__.b = 2; // pollute Object.prototype
   const copy = Object.assign({}, obj);
   console.log('b in copy:', 'b' in copy); // true due to proto
-  return; // TRIGGER
+   // TRIGGER
 }
 f();
 // After fix: return removed, no change
@@ -15,7 +15,7 @@
   Object.__proto__.y = 99;
   const p = { ...o }; // spread ignores proto
   console.log('y in p:', 'y' in p); // false
-  return; // TRIGGER
+   // TRIGGER
 }
 g();
 console.log('ran:', ran);
b in copy: false
y in p: false
ran: true
r1-p006 · label-break-outside-function-target (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-22-1)
1outer: {
2 function f() {
3 console.log('in f');
4 break outer; // jumps to after outer block
5 return; // TRIGGER: but unreachable after break
6 }
7 f();
8 console.log('after f call');
9}
10console.log('reached end');
11// If return removed: same, because break outer still exits
12// But to make return reachable: move break inside conditional
13let flag = true;
14outer2: {
15 function g() {
16 if (flag) {
17 console.log('in g, breaking');
18 break outer2;
19 }
20 console.log('in g, not breaking');
21 return; // TRIGGER: only reached if !flag
22 }
23 g();
24 console.log('after g');
25}
26flag = false;
27g(); // now g runs full, returns, then logs 'after g'
28console.log('final:');
r1-p008 · null-undefined-optional-chaining-nullish-falsy
1function f() {
2 const obj = { prop: null };
3 console.log('obj.prop?.length:', obj.prop?.length); // null -> undefined
4 console.log('obj.prop == null:', obj.prop == null); // true
5 console.log('obj.prop === undefined:', obj.prop === undefined); // false
6 return; // TRIGGER
7}
8f();
9// After fix: same output
10// To make return affect outcome: make f return a value used elsewhere
11let captured;
12function getValue() {
13 const x = undefined;
14 console.log('x == null:', x == null);
15 console.log('x === undefined:', x === undefined);
16 return; // TRIGGER: returns undefined
17}
18captured = getValue();
19console.log('captured === undefined:', captured === undefined);
20// If return removed, captured still undefined
21// But make it so that without return, fall-through returns something else?
22// In JS, falling through returns undefined anyway.
23// Instead: use arrow function in map
24const arr = [1, null, 3];
25const result = arr.map(v => {
26 if (v == null) return; // TRIGGER: returns undefined for null
27 return v * 2;
28});
29console.log('result:', result); // [2, undefined, 6]
30// If return removed, still undefined
31// So make it depend on whether function is called:
32let called = false;
33function maybeReturn() {
34 called = true;
35 if (false) return; // never taken
36 // but we want return to be hit
37 return; // TRIGGER: always hit
38}
39maybeReturn();
40console.log('called:', called);
41// Still no change.
42// Final idea: make the function's return value be used in a way that distinguishes
43// explicit return; from fall-through — but they are identical.
44// So instead, focus on the trace depending on whether the function body runs at all.
45// Use a wrapper that only calls if condition.
46let log = '';
47function h() {
48 log += 'h-entered ';
49 return; // TRIGGER
50}
51if (true) h();
52console.log('log:', log.trim());
53// If return removed, same.
54// Give up and make a working version with observable diff via side effect in finally
55// Hazard 8: use nullish coalescing
56function process(val) {
57 if (val == null) return; // TRIGGER: early return for null/undefined
58 console.log('processing:', val);
59 return val * 2;
60}
61const input = null;
62const output = process(input);
63console.log('output:', output); // undefined
64// If return removed, process(null) would log 'processing: null' then return undefined
65// So: with return: no log
66// without return: logs 'processing: null'
67// Perfect.
68// Rewriting hazard 8:
--- orig.js	2026-10-22 14:22:33.778903166 -0400
+++ fixed.js	2026-10-22 14:22:37.690070544 -0400
@@ -3,7 +3,7 @@
   console.log('obj.prop?.length:', obj.prop?.length); // null -> undefined
   console.log('obj.prop == null:', obj.prop == null); // true
   console.log('obj.prop === undefined:', obj.prop === undefined); // false
-  return; // TRIGGER
+   // TRIGGER
 }
 f();
 // After fix: same output
@@ -13,7 +13,7 @@
   const x = undefined;
   console.log('x == null:', x == null);
   console.log('x === undefined:', x === undefined);
-  return; // TRIGGER: returns undefined
+   // TRIGGER: returns undefined
 }
 captured = getValue();
 console.log('captured === undefined:', captured === undefined);
@@ -34,7 +34,7 @@
   called = true;
   if (false) return; // never taken
   // but we want return to be hit
-  return; // TRIGGER: always hit
+   // TRIGGER: always hit
 }
 maybeReturn();
 console.log('called:', called);
@@ -46,7 +46,7 @@
 let log = '';
 function h() {
   log += 'h-entered ';
-  return; // TRIGGER
+   // TRIGGER
 }
 if (true) h();
 console.log('log:', log.trim());
obj.prop?.length: undefined
obj.prop == null: true
obj.prop === undefined: false
x == null: true
x === undefined: true
captured === undefined: true
result: [ 2, undefined, 6 ]
called: true
log: h-entered
output: undefined
r2-p001 · Label Break Target Outside Function
1function outer() {
2 inner:
3 {
4 function innerFunc() { return; }
5 break inner;
6 }
7 console.log('A');
8}
9outer();
--- orig.js	2026-10-22 14:22:48.316526362 -0400
+++ fixed.js	2026-10-22 14:22:52.191692963 -0400
@@ -1,7 +1,7 @@
 function outer() {
   inner:
     {
-      function innerFunc() { return; }
+      function innerFunc() {  }
       break inner;
     }
   console.log('A');
A
r2-p002 · Name Collision After Move Due to Shadowing
1let x = 1;
2function f() {
3 let x = 2;
4 return;
5}
6f();
7console.log(x); // Should print 1
8// If 'return;' removed and f inlined incorrectly, shadowing breaks
--- orig.js	2026-10-22 14:22:48.317526405 -0400
+++ fixed.js	2026-10-22 14:22:52.116689736 -0400
@@ -1,7 +1,7 @@
 let x = 1;
 function f() {
   let x = 2;
-  return;
+  
 }
 f();
 console.log(x); // Should print 1
1
r2-p003 · Generator Return Semantics Misinterpreted
1function* g() {
2 yield 1;
3 return;
4}
5const it = g();
6console.log(it.next().value); // 1
7console.log(it.next().done, it.next().value); // true undefined
--- orig.js	2026-10-22 14:22:48.318526448 -0400
+++ fixed.js	2026-10-22 14:22:51.494662982 -0400
@@ -1,6 +1,6 @@
 function* g() {
   yield 1;
-  return;
+  
 }
 const it = g();
 console.log(it.next().value); // 1
1
true undefined
r2-p004 · ASI Hazard with Leading Parenthesis
1function f() {
2 console.log('start')
3 return
4}
5(function() { console.log('IIFE') })()
6// Without fix: ASI inserts ; after return → IIFE runs
7// With bad fix: if return removed, no ASI break → syntax error
--- orig.js	2026-10-22 14:22:48.319526491 -0400
+++ fixed.js	2026-10-22 14:22:52.179692446 -0400
@@ -1,6 +1,6 @@
 function f() {
   console.log('start')
-  return
+  
 }
 (function() { console.log('IIFE') })()
 // Without fix: ASI inserts ; after return → IIFE runs
IIFE
r2-p005 · Null/Undef Distinction with Optional Chaining
1let obj = { prop: { value: 42 } };
2function getValue() {
3 return obj.prop?.value;
4}
5console.log(getValue()); // 42
6obj = null;
7console.log(getValue()); // null
8// If getValue had explicit 'return;' at end, removing it doesn't change null
9// But if return; was before null check, hazard appears — here we test baseline
42
r2-p006 · Sparse Array Element Access
1const arr = Array(3);
2arr[1] = 'hello';
3function logElem(i) {
4 console.log(arr[i]);
5 return;
6}
7logElem(0); // undefined
8logElem(1); // hello
9logElem(2); // undefined
10// Removing return; doesn't change output, but shows function called 3 times
--- orig.js	2026-10-22 14:22:48.321526577 -0400
+++ fixed.js	2026-10-22 14:22:51.006641993 -0400
@@ -2,7 +2,7 @@
 arr[1] = 'hello';
 function logElem(i) {
   console.log(arr[i]);
-  return;
+  
 }
 logElem(0); // undefined
 logElem(1); // hello
undefined
hello
undefined
r2-p007 · Async Function Return Value Misinterpretation
1async function f() {
2 console.log('async start');
3 return;
4}
5f().then(v => console.log('resolved with:', v)); // undefined
6// If return; removed, still resolves to undefined
7// But if someone expects early exit semantics, confusion
--- orig.js	2026-10-22 14:22:50.815633781 -0400
+++ fixed.js	2026-10-22 14:22:53.457747436 -0400
@@ -1,6 +1,6 @@
 async function f() {
   console.log('async start');
-  return;
+  
 }
 f().then(v => console.log('resolved with:', v)); // undefined
 // If return; removed, still resolves to undefined
async start
resolved with: undefined
r2-p008 · Async/Await Ordering with Yield Position (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-22-1)
1async function* ag() {
2 yield await Promise.resolve(1);
3 return;
4}
5(async () => {
6 const it = ag();
7 const r1 = await it.next();
8 console.log(r1.value, r1.done); // 1 false
9 const r2 = await it.next();
10 console.log(r2.value, r2.done); // undefined true
11})();
12// No return; at end to remove — testing near-miss: yield after await
13}