biome complexity/useSimplifiedLogicExpression
labelled safe
Analysis Nemotron 3 Super
Flags redundant literals in logical expressions (`&&`, `||`, `!!`) that can be removed without changing truthiness in boolean contexts.
Fix: Removes the redundant literal: `true && expr` → `expr`, `false || expr` → `expr`, `expr || false` → `expr`, `expr && true` → `expr`, `!a || !b` → `!(a && b)`, `null ?? expr` → `expr`.
12 hazards
- Rewriting `true && expr` to `expr` changes behavior if `expr` has side effects and the surrounding context relies on short-circuiting (e.g., in `if` conditions where `expr` must not run).
- Rewriting `false || expr` to `expr` alters evaluation order and may cause `expr` to execute when it previously would not have due to short-circuiting.
- Rewriting `expr || false` to `expr` removes a falsy fallback, potentially changing the result when `expr` is `undefined` or `null` in non-boolean contexts (though rule only fires in boolean contexts, this notes boundary risk).
- Rewriting `expr && true` to `expr` eliminates a truthy guard, which could affect outcomes if `expr` is falsy and the result is used in a non-boolean context (rule restricts to boolean contexts, but notes potential misuse).
- Rewriting `!a || !b` to `!(a && b)` via De Morgan’s law is logically equivalent but may interfere with debugging or intent if the original form was used for readability or breakpoint placement.
- Rewriting `null ?? expr` to `expr` removes nullish coalescing, causing `expr` to evaluate even when `null` is present (but note: `null ?? expr` only fires if `expr` is truthy? Actually, rule flags `null ?? nonNullExp` as invalid — fix removes `null ??`, so rewrites to `expr`; hazard: if `expr` has side effects, it now runs unconditionally whereas before it only ran if `null` was null/undefined).
- If the expression contains comments (e.g., `true && /* comment */ foo()`), the autofix may drop inline comments attached to the literal or expression.
- In rare cases involving `eval` or `with`, the rewritten expression might resolve variables differently due to scope changes, though unlikely.
- If `expr` is a getter with side effects, removing the literal may change the number of times it is invoked.
- The fix may break parsing if `expr` begins with a lookahead ambiguity (e.g., `true && function(){}()` → `function(){}()` which is invalid as a statement).
- In template literals or tagged templates, removing literals could alter the shape of passed arguments if the expression is spread or concatenated.
- If the logical expression is part of a larger sequence (e.g., `return true && expr, other;`), removing `true &&` may alter comma operator behavior.
Findings
| program | oracle | label | judge | status | summary |
|---|---|---|---|---|---|
| r2-p001 | fix-breaks-syntax | labelled safe | real · high | duplicate | The rule rewrites 'true && function() { return 1; }()' to 'function() { return 1; }()', but a function expression in statement position is a syntax error becau… |
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Rewriting expr || false loses falsy fallback | In boolean contexts like ternary conditionals, replacing `x || false` with `x` removes the guarantee that falsy values become `false`. When `x` is `undefined` or `null`, the conditional branch selecti |
| r1-p002 | fix ok | Getter side effects invocation count changed | If `expr` is a getter with side effects, rewriting `getter || false` to `getter` does not change evaluation count in boolean context—but rewriting `true && getter` to `getter` reduces invocations from |
| r1-p003 | silent prediction off | Sparse array holes treated as undefined in logical expressions | Accessing a hole in a sparse array yields `undefined`. In `arr[i] || false`, if `arr[i]` is a hole, it evaluates to `undefined || false → false`. After fix: `arr[i]` alone is `undefined`, which in boo |
| r1-p004 | silent prediction off | Class field initialization with logical expressions | Class field initializers run during construction. If an initializer contains `expr || false`, rewriting it to `expr` may cause `undefined` to be stored instead of `false`, affecting later behavior. Th |
| r1-p005 | silent prediction off | __proto__ assignment altered by truthiness shift | When assigning to `__proto__`, the value must be an object or `null`. Using `expr || false` ensures a fallback to `false`, which coerces to object-like behavior in some engines? Actually, `__proto__` |
| r1-p006 | fix ok | True && expr removal breaks short-circuiting expectation | Although `true && expr` always evaluates `expr`, rewriting it to `expr` removes the explicit `true` that might be relied upon for readability or tooling. More importantly, if this pattern appears in a |
| r1-p007 | silent prediction off | Nullish coalescing fallback removal | Rewriting `null ?? expr` to `expr` removes the intentional fallback to `expr` when the left side is `null` or `undefined`. If `expr` has side effects, those still occur—but the fallback logic is gone. |
| r1-p008 | silent prediction off | Tagged template literal argument shape changed | In tagged templates, expressions inside `${}` are passed as arguments. If such an expression is `expr || false`, rewriting it to `expr` changes the value passed when `expr` is `undefined` or `null`—fr |
| r2-p001 | fix-breaks-syntax | Lookahead ambiguity breaking parse | Fix removing 'true &&' leaves a function expression starting at statement position, which is invalid syntax. |
| r2-p002 | invalid | Var hoisting and redeclaration | After fixing 'true && var x = 1' to 'var x = 1', hoisting and redeclaration in loop cause ReferenceError or wrong value. |
| r2-p003 | fix ok | False || expr evaluation order changed | Fixing 'false || (a=1)' to '(a=1)' causes assignment to run unconditionally, whereas before it was skipped due to short-circuit. |
| r2-p004 | fix ok | Comments attached to removed code | Fix removes 'true &&' and drops the inline comment on the expression, losing documentation. |
| r2-p005 | fix ok | Nullish coalescing fallback removal | Fix rewrites 'null ?? expr' to 'expr', causing expr to run unconditionally even when null is present. |
| r2-p006 | fix ok | Inline comment dropped | Autofix removes 'true &&' and the attached inline comment on the expression. |
| r2-p007 | fix ok | ASI with leading paren | After fix, expression starts with '(' on next line, causing ASI to misinterpret as function call. |
| r2-p008 | fix ok | Numeric/string coercion hazard | Fix removes 'true &&' from expression involving Symbol.toPrimitive, altering coercion path and result. |
Sources
r1-p001 · Rewriting expr || false loses falsy fallback
1function test(val) {
2 return (val || false) ? 'truthy' : 'falsy';
3}
4console.log(test(undefined));
5console.log(test(null));
--- orig.js 2026-10-17 16:15:29.514988087 -0400 +++ fixed.js 2026-10-17 16:15:29.781984182 -0400 @@ -1,5 +1,5 @@ function test(val) { - return (val || false) ? 'truthy' : 'falsy'; + return (val) ? 'truthy' : 'falsy'; } console.log(test(undefined)); console.log(test(null));
falsy falsy
r1-p002 · Getter side effects invocation count changed
1let callCount = 0;
2const obj = {
3 get value() {
4 callCount++;
5 return 42;
6 }
7};
8if (true && obj.value) {
9 // first call
10}
11if (obj.value) {
12 // second call after fix
13}
14console.log('Getter called:', callCount);
--- orig.js 2026-10-17 16:15:29.940981857 -0400 +++ fixed.js 2026-10-17 16:15:30.203978018 -0400 @@ -5,7 +5,7 @@ return 42; } }; -if (true && obj.value) { +if (obj.value) { // first call } if (obj.value) {
Getter called: 2
r1-p003 · Sparse array holes treated as undefined in logical expressions
1const arr = [, 1]; // hole at index 0
2let target = {};
3const val = arr[0] || false;
4target.prop = val;
5console.log('Stored value:', target.prop, typeof target.prop);
6// After fix: target.prop = arr[0]; → stores undefined
Stored value: false boolean
r1-p004 · Class field initialization with logical expressions
1class C {
2 flag = (this.value || false);
3 constructor() {
4 this.value = undefined;
5 }
6}
7const c = new C();
8console.log('flag:', c.flag);
9// After fix: flag = this.value → undefined
flag: false
r1-p005 · __proto__ assignment altered by truthiness shift
1let fallbackUsed = false;
2const getValue = () => {
3 fallbackUsed = true;
4 return null;
5};
6try {
7 const proto = getValue() || false;
8 Object.defineProperty({}, '__proto__', { value: proto });
9} catch (e) {
10 console.log('Error:', e.name, fallbackUsed ? '(fallback used)' : '(no fallback)');
11}
r1-p006 · True && expr removal breaks short-circuiting expectation
1let i = 0;
2while (true && i < 3) {
3 console.log('Iteration:', i);
4 i++;
5}
6console.log('Loop exited at i=' + i);
--- orig.js 2026-10-17 16:15:31.009966261 -0400 +++ fixed.js 2026-10-17 16:15:31.261962594 -0400 @@ -1,5 +1,5 @@ let i = 0; -while (true && i < 3) { +while (i < 3) { console.log('Iteration:', i); i++; }
Iteration: 0 Iteration: 1 Iteration: 2 Loop exited at i=3
r1-p007 · Nullish coalescing fallback removal
1function getConfig() {
2 return null;
3}
4const settings = getConfig() ?? { debug: true };
5console.log('settings.debug:', settings.debug);
6// After fix: settings = getConfig() → null, then accessing .debug throws
settings.debug: true
r1-p008 · Tagged template literal argument shape changed
1function tags(strings, ...values) {
2 console.log('Values received:', values.map(v => String(v)));
3 return strings.raw.join('');
4}
5const x = undefined;
6const result = tags`value: ${x || false}`;
7console.log('Result:', result);
8// After fix: ${x} → passes undefined, so values[0] is 'undefined'
Values received: [ 'false' ] Result: value:
r2-p001 · Lookahead ambiguity breaking parse
1try {
2 true && function() { return 1; }();
3 console.log('ok');
4} catch (e) {
5 console.log(e.name + ': ' + e.message);
6}
--- orig.js 2026-10-17 16:15:37.605870939 -0400 +++ fixed.js 2026-10-17 16:15:37.866867196 -0400 @@ -1,5 +1,5 @@ try { - true && function() { return 1; }(); + function() { return 1; }(); console.log('ok'); } catch (e) { console.log(e.name + ': ' + e.message);
ok
r2-p002 · Var hoisting and redeclaration (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-15-1)
1for (let i = 0; i < 2; i++) {
2 try {
3 true && var x = i;
4 console.log(x);
5 } catch (e) {
6 console.log(e.name + ': ' + e.message);
7 }
8}
r2-p003 · False || expr evaluation order changed
1let a = 0;
2false || (a = 1);
3console.log(a); // should remain 0 if short-circuited
--- orig.js 2026-10-17 16:15:37.968865734 -0400 +++ fixed.js 2026-10-17 16:15:38.234861928 -0400 @@ -1,3 +1,3 @@ let a = 0; -false || (a = 1); +(a = 1); console.log(a); // should remain 0 if short-circuited
1
r2-p004 · Comments attached to removed code
1let x = true && /* important */ 42;
2console.log(x);
--- orig.js 2026-10-17 16:15:38.398859582 -0400 +++ fixed.js 2026-10-17 16:15:38.666855749 -0400 @@ -1,2 +1,2 @@ -let x = true && /* important */ 42; +let x = 42; console.log(x);
42
r2-p005 · Nullish coalescing fallback removal
1let called = 0;
2const f = () => { called++; return 99; };
3let result = null ?? f();
4console.log('result=' + result + ', called=' + called);
--- orig.js 2026-10-17 16:15:38.826853461 -0400 +++ fixed.js 2026-10-17 16:15:39.100849545 -0400 @@ -1,4 +1,4 @@ let called = 0; const f = () => { called++; return 99; }; -let result = null ?? f(); +let result = f(); console.log('result=' + result + ', called=' + called);
result=99, called=1
r2-p006 · Inline comment dropped
1let y = true && /* note */ parseInt('5');
2console.log(y);
--- orig.js 2026-10-17 16:15:39.263847220 -0400 +++ fixed.js 2026-10-17 16:15:39.530843410 -0400 @@ -1,2 +1,2 @@ -let y = true && /* note */ parseInt('5'); +let y = parseInt('5'); console.log(y);
5
r2-p007 · ASI with leading paren
1let z =
2true &&
3( () => 7 )()
4console.log(z);
--- orig.js 2026-10-17 16:15:39.693841084 -0400 +++ fixed.js 2026-10-17 16:15:39.958837303 -0400 @@ -1,4 +1,3 @@ let z = -true && ( () => 7 )() console.log(z);
7
r2-p008 · Numeric/string coercion hazard
1const obj = {
2 [Symbol.toPrimitive](hint) {
3 return hint === 'string' ? 'str' : 42;
4 }
5};
6let res = true && String(obj);
7console.log(res);
--- orig.js 2026-10-17 16:15:40.131834840 -0400 +++ fixed.js 2026-10-17 16:15:40.399831026 -0400 @@ -3,5 +3,5 @@ return hint === 'string' ? 'str' : 42; } }; -let res = true && String(obj); +let res = String(obj); console.log(res);
str