biome complexity/noUselessUndefined
labelled safe
Analysis Nemotron 3 Super
Flags explicit uses of `undefined` where it is redundant because it is already the implicit default.
Fix: Removes the explicit `= undefined` initializer or argument default, or replaces `return undefined;`/`yield undefined;` with `return;`/`yield;` respectively.
10 hazards
- Rewriting `let x = undefined;` to `let x;` changes behavior if the variable is read before assignment due to TDZ—though both initialize to `undefined`, the temporal dead zone semantics differ slightly in edge cases involving indirect eval or with statements.
- In destructuring like `const {a = undefined} = obj;`, removing `= undefined` alters behavior when `obj.a` is `null` because the default only applies if the property is missing, not if it is present but `null`.
- For function parameters like `function f(a = undefined)`, removing the default changes behavior when `f()` is called with no argument vs. `f(undefined)`—though both result in `a` being `undefined`, the former triggers the default parameter initialization which may have side effects if the default expression is not a literal (but here it is, so low risk; still, spec-wise distinct).
- In object destructuring parameters like `function f({a = undefined})`, omitting the default breaks when the argument is `{a: null}` because the default does not apply, preserving `null` instead of converting to `undefined`.
- Replacing `return undefined;` with `return;` in a function that is used as a constructor changes behavior because `new f()` returns the instance when the return value is not an object, and `undefined` is not an object, so both behave the same—however, if the function is called via `Reflect.construct` with a `newTarget`, the return value handling is identical, so no actual hazard; but per spec, any non-object return is ignored, so safe.
- Replacing `yield undefined;` with `yield;` in a generator changes behavior if the generator's return value is inspected via `{value, done}` because `yield` produces `{value: undefined, done: false}` just like `yield undefined;`, so equivalent—no hazard.
- If the code relies on the length of the function's parameter list for reflection (e.g., `f.length`), removing a default parameter does not change the length because parameters with defaults still count toward length—so no hazard.
- In strict mode, if `undefined` is reassigned globally, the literal `undefined` might not refer to the actual undefined value, but the rule only flags syntactic defaults, and the autofix removes the reference entirely, so if `undefined` was shadowed, removing the use avoids relying on the possibly mutated binding—this could fix unintended behavior, not break it.
- The autofix may drop comments attached to the `undefined` token, such as `let x = /* TODO */ undefined;`, which would be lost during rewriting.
- In rare cases involving `with` statements or indirect `eval`, the scope resolution of a bare variable in `let x;` vs. an initializer could differ if `undefined` is not resolvable in the expected scope, but since the initializer is removed and the variable is still declared, the behavior remains consistent.
Findings
| program | oracle | label | judge | status | summary |
|---|---|---|---|---|---|
| r1-p006 | fix-changes-behavior | labelled safe | real · high | related | The rule removes the initializer `undefined` from `let x = undefined`, turning it into `let x;`. In the program, a local variable named `undefined` shadows the… |
| r2-p005 | fix-changes-behavior | labelled safe | real · high | duplicate | The rule removes the default parameter `= undefined`, which changes the function's `length` property from 0 to 2 because trailing parameters without defaults a… |
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Destructuring assignment with undefined default overwriting null | In destructuring like `const {a = undefined} = obj;`, removing `= undefined` alters behavior when `obj.a` is `null` because the default only applies if the property is missing, not if it is present bu |
| r1-p002 | fix ok | Function parameter default with undefined changing init timing | For function parameters like `function f(a = undefined)`, removing the default changes behavior when `f()` is called with no argument vs. `f(undefined)`—though both result in `a` being `undefined`, th |
| r1-p003 | fix ok | Object destructuring parameter preserving null vs undefined | In object destructuring parameters like `function f({a = undefined})`, omitting the default breaks when the argument is `{a: null}` because the default does not apply, preserving `null` instead of con |
| r1-p004 | silent | Block-scoped function declaration hoisting differences | function declarations inside blocks, switch cases, labeled statements |
| r1-p005 | fix ok | Yield undefined equivalence verified by inspection | Replacing `yield undefined;` with `yield;` in a generator changes behavior if the generator's return value is inspected via `{value, done}` because `yield` produces `{value: undefined, done: false}` j |
| r1-p006 | fix-changes-behavior | Strict mode undefined shadowing made safe by removal | In strict mode, if `undefined` is reassigned globally, the literal `undefined` might not refer to the actual undefined value, but the rule only flags syntactic defaults, and the autofix removes the re |
| r1-p007 | invalid | Control flow interaction with labeled breaks and finally | label/break/continue control flow, finally blocks, early returns in nested blocks |
| r1-p008 | silent | Proto key access unaffected by undefined removal | __proto__ keys, prototype access, Object.assign vs spread |
| r2-p001 | fix ok | Function Parameter Default Undefined - Initialization Timing | Removing default parameter '= undefined' changes when the default is evaluated versus when argument is passed explicitly. |
| r2-p002 | fix ok | Constructor Return Undefined vs Empty Return - Reflect.construct | Replacing 'return undefined;' with 'return;' in a constructor has no behavioral difference per spec, but we trace the return value to show equivalence. |
| r2-p003 | silent prediction off | ASI Hazard with Leading Parenthesis After Return Undefined | Removing 'undefined' from 'return undefined' can cause ASI to misinterpret the next line starting with '(' as a function call. |
| r2-p004 | fix ok | Getter/Setter with Undefined Return and Side Effects | Replacing 'return undefined;' in a getter with 'return;' may affect side effects if the getter is defined via Object.defineProperty and called multiple times. |
| r2-p005 | fix-changes-behavior prediction off | Function Length Reflection - No Change Expected | Removing '= undefined' from parameter does not change function.length, so no behavioral difference in reflection-based code. |
| r2-p006 | silent | Class Fields and Static Blocks - No Interaction | The rule does not apply to class fields or static blocks, so this is a near-miss to confirm no trigger. |
| r2-p008 | fix ok prediction off | Temporal Dead Zone - Let Redeclaration After Loop | The rule does not cause hoisting or reordering, so no TDZ hazard. We show a loop that reenters a scope with let to prove no interference. |
Sources
r1-p001 · Destructuring assignment with undefined default overwriting null
1let log = [];
2function capture(v){ log.push(v); }
3
4// Hazardous pattern: default in destructuring
5data = {a: null}; // note: value is null, not missing
6try {
7 const {a = undefined} = data;
8 capture('got ' + String(a));
9} catch(e) {
10 capture(e.name + ': ' + e.message);
11}
12
13console.log(log.join(' | '));
--- orig.js 2026-10-17 16:09:21.099526993 -0400 +++ fixed.js 2026-10-17 16:09:21.370517776 -0400 @@ -4,7 +4,7 @@ // Hazardous pattern: default in destructuring data = {a: null}; // note: value is null, not missing try { - const {a = undefined} = data; + const {a } = data; capture('got ' + String(a)); } catch(e) { capture(e.name + ': ' + e.message);
r1-p002 · Function parameter default with undefined changing init timing
1let callLog = [];
2function sideEffect(){
3 callLog.push('init');
4 return undefined;
5}
6
7function f(a = sideEffect()){ // rule triggers here
8 callLog.push('body: ' + (a === undefined ? 'undef' : 'def'));
9}
10
11f(); // triggers default param init
12f(undefined); // does NOT trigger default param init
13
14console.log(callLog.join(' | '));
--- orig.js 2026-10-17 16:09:21.531512300 -0400 +++ fixed.js 2026-10-17 16:09:21.810502811 -0400 @@ -1,7 +1,7 @@ let callLog = []; function sideEffect(){ callLog.push('init'); - return undefined; + return ; } function f(a = sideEffect()){ // rule triggers here
init | body: undef | init | body: undef
r1-p003 · Object destructuring parameter preserving null vs undefined
1let result = [];
2function test(val){
3 result.push(String(val));
4}
5
6// Hazard: default in parameter destructuring
7function g({a = undefined}){
8 test(a);
9}
10
11g({a: null}); // should preserve null -> logs 'null'
12g({a: undefined});// logs 'undefined'
13g({}); // missing -> default -> logs 'undefined'
14
15g = null; // redeclaration after use
16
17console.log(result.join(' | '));
--- orig.js 2026-10-17 16:09:21.969497403 -0400 +++ fixed.js 2026-10-17 16:09:22.249487898 -0400 @@ -4,7 +4,7 @@ } // Hazard: default in parameter destructuring -function g({a = undefined}){ +function g({a }){ test(a); }
null | undefined | undefined
r1-p004 · Block-scoped function declaration hoisting differences
1let out = [];
2{
3 function h(){ out.push('block'); }
4 if(true){ function h(){ out.push('redeclared'); } }
5 h();
6}
7
8switch(0){
9 case 0:
10 function h(){ out.push('case'); }
11 break;
12}
13
14h();
15
16label: {
17 function h(){ out.push('label'); }
18 break label;
19}
20
21h();
22
23console.log(out.join(' | '));
r1-p005 · Yield undefined equivalence verified by inspection
1function* gen(){
2 yield undefined; // triggers rule
3 yield; // equivalent, no trigger
4}
5
6const it = gen();
7let log = [];
8log.push(JSON.stringify(it.next()));
9log.push(JSON.stringify(it.next()));
10log.push(JSON.stringify(it.next()));
11
12console.log(log.join(' | '));
--- orig.js 2026-10-17 16:09:22.645474458 -0400 +++ fixed.js 2026-10-17 16:09:22.919465160 -0400 @@ -1,5 +1,5 @@ function* gen(){ - yield undefined; // triggers rule + yield ; // triggers rule yield; // equivalent, no trigger }
{"done":false} | {"done":false} | {"done":true}r1-p006 · Strict mode undefined shadowing made safe by removal
1"use strict";
2let log = [];
3(function(){
4 var undefined = 42; // shadowing
5 let x = undefined; // rule triggers: removes this line
6 log.push(String(x));
7})();
8
9// After fix: x is undeclared -> ReferenceError
10try {
11 (function(){
12 var undefined = 42;
13 let x; // autofixed version
14 log.push(String(x));
15 })();
16} catch(e) {
17 log.push(e.name + ': ' + e.message);
18}
19
20console.log(log.join(' | '));
--- orig.js 2026-10-17 16:09:23.083459600 -0400 +++ fixed.js 2026-10-17 16:09:23.355450389 -0400 @@ -2,7 +2,7 @@ let log = []; (function(){ var undefined = 42; // shadowing - let x = undefined; // rule triggers: removes this line + let x ; // rule triggers: removes this line log.push(String(x)); })();
42 | undefined
r1-p007 · Control flow interaction with labeled breaks and finally (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-09-0)
1let trace = [];
2outer: for(let i=0; i<2; i++){
3 try{
4 if(i===0){
5 trace.push('a');
6 break outer;
7 }
8 trace.push('b');
9 return undefined; // rule triggers
10 } finally{
11 trace.push('finally');
12 }
13}
14
15function inner(){
16 trace.push('inner start');
17 if(true) yield undefined; // rule triggers
18 trace.push('inner end');
19}
20
21for(const v of inner()){ trace.push('yielded: '+v); }
22
23console.log(trace.join(' | '));
r1-p008 · Proto key access unaffected by undefined removal
1let obj = {__proto__: {toString(){return 'proto'}}};
2let copy = {...obj, a: undefined}; // rule triggers on 'a: undefined'
3
4let log = [];
5log.push(String(copy.a));
6log.push(String(copy.toString()));
7log.push(String(Object.getPrototypeOf(copy)));
8
9// Re-assign after use
10data = {x:1};
11copy = {...data, y: undefined}; // another trigger
12log.push(String(copy.y));
13
14console.log(log.join(' | '));
r2-p001 · Function Parameter Default Undefined - Initialization Timing
1let log = [];
2function side() { log.push('side'); return undefined; }
3function f(a = side()) {
4 return a;
5}
6f(); // call with no arg -> evaluates side()
7f(undefined); // call with undefined arg -> does NOT evaluate side()
8console.log(log.join(','));
--- orig.js 2026-10-17 16:09:29.560241608 -0400 +++ fixed.js 2026-10-17 16:09:29.835232417 -0400 @@ -1,5 +1,5 @@ let log = []; -function side() { log.push('side'); return undefined; } +function side() { log.push('side'); return ; } function f(a = side()) { return a; }
side,side
r2-p002 · Constructor Return Undefined vs Empty Return - Reflect.construct
1function F() {
2 this.x = 1;
3 return undefined;
4}
5let o1 = Reflect.construct(F, [], F);
6console.log(o1.x);
7function G() {
8 this.x = 2;
9 return;
10}
11let o2 = Reflect.construct(G, [], G);
12console.log(o2.x);
--- orig.js 2026-10-17 16:09:30.008226634 -0400 +++ fixed.js 2026-10-17 16:09:30.276217696 -0400 @@ -1,6 +1,6 @@ function F() { this.x = 1; - return undefined; + return ; } let o1 = Reflect.construct(F, [], F); console.log(o1.x);
1 2
r2-p003 · ASI Hazard with Leading Parenthesis After Return Undefined
1function f() {
2 return
3 [
4 function() { return 1; },
5 function() { return 2; }
6 ]
7}
8try {
9 let arr = f();
10 console.log(arr[0]());
11} catch (e) {
12 console.log(e.name + ': ' + e.message);
13}
TypeError: Cannot read properties of undefined (reading '0')
r2-p004 · Getter/Setter with Undefined Return and Side Effects
1let log = [];
2const obj = {};
3Object.defineProperty(obj, 'prop', {
4 get() {
5 log.push('get');
6 return undefined;
7 }
8});
9x = obj.prop;
10y = obj.prop;
11console.log(log.join(','));
12// After fix: getter still returns undefined implicitly, but we trace call count
--- orig.js 2026-10-17 16:09:30.656205022 -0400 +++ fixed.js 2026-10-17 16:09:30.921196184 -0400 @@ -3,7 +3,7 @@ Object.defineProperty(obj, 'prop', { get() { log.push('get'); - return undefined; + return ; } }); x = obj.prop;
r2-p005 · Function Length Reflection - No Change Expected
1function f(a = undefined, b) {}
2console.log(f.length); // should be 2 regardless of default
--- orig.js 2026-10-17 16:09:31.089190587 -0400 +++ fixed.js 2026-10-17 16:09:31.345182068 -0400 @@ -1,2 +1,2 @@ -function f(a = undefined, b) {} +function f(a , b) {} console.log(f.length); // should be 2 regardless of default
0
r2-p006 · Class Fields and Static Blocks - No Interaction
1class C {
2 static field = undefined;
3 static {
4 console.log(C.field);
5 }
6 method() {}
7}
8// No output expected from rule, but static block runs
undefined
r2-p008 · Temporal Dead Zone - Let Redeclaration After Loop
1for (let i = 0; i < 2; i++) {
2 let x = undefined;
3 if (i === 0) {
4 x = 'first';
5 } else {
6 x = 'second';
7 }
8 console.log(x);
9}
--- orig.js 2026-10-17 16:09:31.730169255 -0400 +++ fixed.js 2026-10-17 16:09:32.006160070 -0400 @@ -1,5 +1,5 @@ for (let i = 0; i < 2; i++) { - let x = undefined; + let x ; if (i === 0) { x = 'first'; } else {
first second