biome style/noYodaExpression
labelled safe
Analysis Nemotron 3 Super
Flags binary comparisons where the static literal/expression appears on the left-hand side (a Yoda condition).
Fix: Swaps the left and right operands of the binary operator while preserving the operator token and all surrounding syntactic context.
12 hazards
- Rewriting `if (\"red\" == value)` to `if (value == \"red\")` changes operator precedence when embedded in expressions like `x && \"red\" == value`, potentially altering evaluation order.
- If the left-hand side is a getter with side effects (e.g., `if (getMagic() == value)`), swapping operands changes when the getter is invoked relative to other subexpressions.
- When the right-hand side involves `void 0` or `undefined`, moving it left may trigger unintended global lookups in non-strict mode due to implicit property creation.
- In `delete obj[\"prop\"] == value`, swapping operands produces invalid syntax because `delete` expects an unbound reference on its left, breaking parsing.
- For `new Foo(5 == size)`, swapping yields `new Foo(size == 5)`, which changes argument evaluation timing if `size` is a getter with side effects.
- In `if (\"\" + obj == value)`, moving the string literal left breaks concatenation order, yielding different intermediate values if `obj.valueOf` has side effects.
- When the comparison is inside a `with` block, swapping operands may alter which object scope provides identifier resolution for the moved operand.
- If the left-hand side is a comma expression like `if ((console.log(\"x\"), 0) == value)`, swapping moves the side effect (`console.log`) to a different evaluation point relative to surrounding expressions.
- Rewriting `if (value != \"red\")` preserves `!=` but if the original relied on left-to-right short-circuiting in a larger expression like `a && \"red\" != value`, the swap changes short-circuit behavior.
- In template literals such as ``if (`hello${world}` == msg)``, swapping operands is safe only if the template has no side effects in its substitutions; otherwise evaluation order shifts.
- Moving a literal with attached comments (e.g., `if (\"red\" /* TODO */ == value)`) may detach or misplace those comments depending on how the formatter handles whitespace.
- For `if ((value) == (\"red\"))`, excessive parentheses may be preserved or dropped inconsistently by the fixer, affecting readability though not semantics.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Null/undefined distinction with optional chaining | Rewriting 'null == value' to 'value == null' when value uses optional chaining can change short-circuit behavior—original throws if base is null/undefined, swapped does not. |
| r1-p002 | fix ok | New expression with getter side effects | Swapping 'new Foo(5 == size)' to 'new Foo(size == 5)' changes when size getter is called—original evaluates 5 first, swapped calls size getter first. |
| r1-p003 | fix ok prediction off | Operator precedence in logical AND | Rewriting 'x && "red" == value' to 'x && value == "red"' is same, but if rule fired on internal comparison alone, swap could change grouping in larger expression—here we show dependency via explicit g |
| r1-p004 | invalid | With block scope interaction | In 'with (scope) { if ("x" == y) }', swapping to 'if (y == "x")' changes whether y is resolved from scope object or outer scope if y is not in scope. |
| r1-p005 | silent prediction off | ASI with leading parenthesis | Without semicolon, leading '(' on next line can be interpreted as function call. Swapping operands in a comparison before such line changes what gets called. |
| r1-p006 | fix ok | Inequality short-circuit in logical AND | In 'a && "red" != value', if a is false, "red" != value is not evaluated. After swap 'a && value != "red"', same behavior—but if value is a getter with side effects, original never calls it when a fal |
| r1-p007 | fix ok | Arguments object interaction | In function with arguments, rewriting comparison may interfere with arguments aliasing in non-strict mode if parameters are reassigned. |
| r1-p008 | silent prediction off | String concatenation with valueOf side effects | In '"" + obj == value', swapping breaks concatenation order—if obj.valueOf has side effects, original executes it before comparison, swapped after (but value is on left now). Actually, swap moves lite |
| r2-p001 | invalid | var hoisting and redeclaration | var hoisting and re-declaration inside loops or after use |
| r2-p002 | invalid | arguments object interaction | arguments object, function.length, default parameters, rest parameters |
| r2-p003 | invalid | getter side effects | getters, setters and Object.defineProperty side effects |
| r2-p004 | silent prediction off | comma expression side effect shift | If the left-hand side is a comma expression like `if ((console.log("x"), 0) == value)`, swapping moves the side effect (`console.log`) to a different evaluation point relative to surrounding expressio |
| r2-p005 | silent | delete operand invalid syntax | In `delete obj["prop"] == value`, swapping operands produces invalid syntax because `delete` expects an unbound reference on its left, breaking parsing. |
| r2-p006 | silent prediction off | template literal substitution side effects | In template literals such as ``if (`hello${world}` == msg)``, swapping operands is safe only if the template has no side effects in its substitutions; otherwise evaluation order shifts. |
| r2-p007 | invalid | async/await ordering | async/await ordering, generators and yield position |
| r2-p008 | invalid | class fields interaction | class fields, static blocks, private names, super calls |
Sources
r1-p001 · Null/undefined distinction with optional chaining
1let obj = null;
2try {
3 if (null == obj?.prop) console.log('matched');
4} catch (e) {
5 console.log(e.name + ': ' + e.message);
6}
7obj = {prop: null};
8if (null == obj?.prop) console.log('matched');
--- orig.js 2026-10-17 16:58:49.574550036 -0400 +++ fixed.js 2026-10-17 16:58:49.866553869 -0400 @@ -1,8 +1,8 @@ let obj = null; try { - if (null == obj?.prop) console.log('matched'); + if (obj?.prop == null) console.log('matched'); } catch (e) { console.log(e.name + ': ' + e.message); } obj = {prop: null}; -if (null == obj?.prop) console.log('matched'); +if (obj?.prop == null) console.log('matched');
matched matched
r1-p002 · New expression with getter side effects
1let callCount = 0;
2const size = {get value() { callCount++; return 5; }};
3class Foo {
4 constructor(x) {
5 console.log('Foo arg:', x, 'calls:', callCount);
6 }
7}
8new Foo(5 == size.value);
9console.log('After:');
10new Foo(size.value == 5);
--- orig.js 2026-10-17 16:58:50.039556140 -0400 +++ fixed.js 2026-10-17 16:58:50.321559839 -0400 @@ -5,6 +5,6 @@ console.log('Foo arg:', x, 'calls:', callCount); } } -new Foo(5 == size.value); +new Foo(size.value == 5 ); console.log('After:'); new Foo(size.value == 5);
Foo arg: true calls: 1 After: Foo arg: true calls: 2
r1-p003 · Operator precedence in logical AND
1let value = 'blue';
2if (false && ("red" == value)) {
3 console.log('unreachable');
4} else {
5 console.log('false path');
6}
7// Rule triggers on parenthesized form
8if (false && (value == "red")) {
9 console.log('also unreachable');
10} else {
11 console.log('false path 2');
12}
--- orig.js 2026-10-17 16:58:50.491562069 -0400 +++ fixed.js 2026-10-17 16:58:50.753565506 -0400 @@ -1,5 +1,5 @@ let value = 'blue'; -if (false && ("red" == value)) { +if (false && (value == "red")) { console.log('unreachable'); } else { console.log('false path');
false path false path 2
r1-p004 · With block scope interaction (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-3)
1const scope = {x: 1};
2let y = 2;
3with (scope) {
4 if ("x" == y) {
5 console.log('matched in scope');
6 } else {
7 console.log('not matched in scope:', y);
8 }
9}
10// Outside with, y is outer
11if ("x" == y) {
12 console.log('matched outer');
13} else {
14 console.log('not matched outer:', y);
15}
r1-p005 · ASI with leading parenthesis
1let x = 1
2;
3(function(){ return 5; })()
4console.log('first:', x)
5x = 0
6;
7(function(){ return 6; })()
8console.log('second:', x)
first: 1 second: 0
r1-p006 · Inequality short-circuit in logical AND
1let callCount = 0;
2const obj = {get val() { callCount++; return 'blue'; }};
3let a = true;
4if (a && "red" != obj.val) {
5 console.log('unequal');
6}
7console.log('Calls after first:', callCount);
8a = true;
9if (a && obj.val != "red") {
10 console.log('unequal swapped');
11}
12console.log('Calls after second:', callCount);
--- orig.js 2026-10-17 16:58:51.197571329 -0400 +++ fixed.js 2026-10-17 16:58:51.476574987 -0400 @@ -1,7 +1,7 @@ let callCount = 0; const obj = {get val() { callCount++; return 'blue'; }}; let a = true; -if (a && "red" != obj.val) { +if (a && obj.val != "red" ) { console.log('unequal'); } console.log('Calls after first:', callCount);
unequal Calls after first: 1 unequal swapped Calls after second: 2
r1-p007 · Arguments object interaction
1function foo(a) {
2 arguments[0] = 99;
3 if (5 == a) {
4 return 'match';
5 }
6 return 'no';
7}
8console.log(foo(5));
9function bar(a) {
10 arguments[0] = 99;
11 if (a == 5) {
12 return 'match';
13 }
14 return 'no';
15}
16console.log(bar(5));
--- orig.js 2026-10-17 16:58:51.651577281 -0400 +++ fixed.js 2026-10-17 16:58:51.927580900 -0400 @@ -1,6 +1,6 @@ function foo(a) { arguments[0] = 99; - if (5 == a) { + if (a == 5) { return 'match'; } return 'no';
match match
r1-p008 · String concatenation with valueOf side effects
1let side = 0;
2const obj = {
3 valueOf() {
4 side += 1;
5 return 42;
6 }
7};
8let value = 42;
9if ("" + obj == value) {
10 console.log('equal, side=', side);
11}
12side = 0;
13if (value == "" + obj) {
14 console.log('equal swapped, side=', side);
15}
equal, side= 1 equal swapped, side= 1
r2-p001 · var hoisting and redeclaration (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-3)
1function f() {
2 for (var i = 0; i < 2; i++) {
3 if (\"test\" == x) console.log('match'); // rule triggers here
4 var x = i;
5 }
6 console.log('x:', typeof x);
7}
8f();
r2-p002 · arguments object interaction (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-3)
1function g(a, b = 1, ...rest) {
2 if (\"hi\" == arguments[0]) console.log('first arg matched'); // rule triggers
3 return [a, b, rest.length];
4}
5console.log(g('hi', 2, 3, 4));
r2-p003 · getter side effects (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-3)
1let val = 0;
2const obj = {
3 get x() {
4 val++;
5 return 5;
6 }
7};
8if (\"5\" == obj.x) console.log('equal'); // rule triggers, val increments once
9console.log('val:', val);
r2-p004 · comma expression side effect shift
1let logged = false;
2function logX() { logged = true; console.log('x'); return 0; }
3let v = 0;
4if ((logX(), 0) == v) console.log('matched'); // rule triggers, logs before compare
5console.log('logged:', logged);
x matched logged: true
r2-p005 · delete operand invalid syntax
1try {
2 const obj = { prop: 1 };
3 // This line would trigger rule but is invalid after swap: delete obj['prop'] == 1
4 // So we avoid trigger by using != instead (rule ignores !=)
5 if (delete obj.prop != 1) console.log('unexpected');
6} catch (e) {
7 console.log(e.name + ': ' + e.message);
8}
9console.log('prop in obj:', 'prop' in { prop: 1 });
prop in obj: true
r2-p006 · template literal substitution side effects
1let worldVal = undefined;
2function getWorld() { return worldVal = 'substituted'; }
3let msg = 'hellosubstituted';
4if (`hello${getWorld()}` == msg) console.log('match'); // rule triggers, getWorld runs before compare
5console.log('worldVal:', worldVal);
match worldVal: substituted
r2-p007 · async/await ordering (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-3)
1async function seq() {
2 let a = await Promise.resolve(1);
3 if (\"1\" == a) console.log('equal'); // rule triggers
4 return a;
5}
6seq().then(r => console.log('result:', r));
r2-p008 · class fields interaction (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-58-3)
1class Base {
2 constructor() {
3 if (\"base\" == this.kind) console.log('base match'); // rule triggers
4 }
5}
6class Derived extends Base {
7 kind = 'base';
8}
9new Derived();