fixhunt

eslint yoda

labelled safe
Runs / 2026-10-22T18-33-05-687Z-4nzq-eslint-yodaeslint 9.39.52026-10-22 18:33 UTC8 programs × 2 round(s)15 programs, 11 fired, 11 fixed, 0 findings, 0 rejectedmodel cost $0.0068

Analysis Nemotron 3 Super

Flags Yoda conditions where a literal appears before a variable in binary expressions.

Fix: Swaps the left and right operands of the binary expression so the variable precedes the literal, preserving operator type and spacing around the operator but removing any template literal wrapping if present.

10 hazards
  • Rewriting `a === b` when `a` is a getter with side effects changes evaluation timing and may alter observable state.
  • If the literal is a tagged template (e.g., `css``red`), swapping operands breaks the tag function’s expected argument order and semantics.
  • When the variable is a call expression like `getX()`, moving it to the right changes evaluation order relative to other expressions in the condition.
  • In `if (0 <= x && x < 1)`, fixing only the first part (`0 <= x` → `x >= 0`) may break the intended range check if the fix is applied partially or incorrectly.
  • If the literal is a string containing escape sequences (e.g., "\n"), rewriting it as the right operand does not change value but may affect minification or team conventions—not a behavioral hazard.
  • Comments attached to the literal via line comments (e.g., `"red" // flag === color`) may be lost or misplaced during the swap if the fix does not preserve comment attachment points.
  • In `if (true == flag)`, rewriting to `flag == true` is safe for loose equality but alters readability intent; however, no behavioral change occurs since `==` is commutative for these types.
  • If the variable is actually a `this` property access like `obj.prop`, swapping does not change behavior but may confuse readers expecting Yoda style for safety—no actual hazard.
  • When the literal is a number and the variable is a string (e.g., `5 > count`), rewriting to `count < 5` preserves semantics but relies on implicit coercion; no change in behavior, though the rule’s fix keeps the operator as `>` rather than changing to `<`—so this shape is not fired on for fix.
  • In `if (`red` === `${color}`)`, the fix produces `if (`color` === `red`)`, which changes the template literal from an interpolated expression to a bare identifier, altering runtime value if `color` is not a string—e.g., if `color` is `{toString:()=>42}`, original yields `"red" === "42"` (false), fixed yields `"{toString:...}" === "red"` (different false, but still false—wait, no: ``${color}`` calls `String(color)`, so if `color` is an object with custom `toString`, it becomes that string; after fix, ``${value}`` becomes ``${color}``? No—wait, original: left is ``red`` (literal), right is ``${color}`` (template literal with embedded variable). Fix swaps: left becomes ``${color}``, right becomes ``red``. So new code: ``${color} === `red` ``. This is semantically identical: both sides are strings, equality holds iff String(color) === "red``. So no behavioral change. However, if the template literal had a tag, e.g., `css``red` === css`${color}`, then swapping would break—but the rule does not fire on tagged templates in the examples, and the fix description says it removes template literal wrapping, which is inaccurate. Actually, the fix preserves the template literals but swaps them. So hazard: if the template literal is tagged, swapping changes which side is the tagged template and which is the cooked string, breaking the tag’s expectations.

Programs

idresulttitlehazard
r1-p001fix okStrict Mode SemanticsIn strict mode, assignments to undeclared variables throw ReferenceError, affecting whether swapped condition triggers error path.
r1-p002fix okVar Hoisting and Re-declarationSwapped condition uses variable before its declaration due to hoisting, changing temporal dead zone behavior.
r1-p003silent prediction offTagged Template SemanticsSwapping operands of a tagged template literal changes which side is the tag function input, breaking expected cooked/raw argument order.
r1-p004fix okFunction Declarations in BlocksBlock-scoped function declarations are hoisted differently in strict vs sloppy mode, and swapping may reference a function before its declaration is processed.
r1-p006silent prediction offTagged Template Literal Wrapping MisunderstandingThe fix description incorrectly claims it removes template literal wrapping; actually it preserves them but swaps sides, which breaks tagged template semantics when only one side is tagged.
r1-p007fix okSparse Arrays and HolesWhen comparing array lengths or indices involving holes, swapping operands in a loop condition can change which array is accessed first, triggering different side effects.
r1-p008fix okNull/Undefined DistinctionWhen using optional chaining in compound conditions, swapping operands can change short-circuit behavior, causing unintended property access on null/undefined.
r2-p001fix okLoose equality readable intent preservationIn `if (true == flag)`, rewriting to `flag == true` is safe for loose equality but alters readability intent; however, no behavioral change occurs since `==` is commutative for these types.
r2-p002silent prediction offCall expression evaluation order changeWhen the variable is a call expression like `getX()`, moving it to the right changes evaluation order relative to other expressions in the condition.
r2-p003fix okString literal escape sequences minification concernIf the literal is a string containing escape sequences (e.g., "\n"), rewriting it as the right operand does not change value but may affect minification or team conventions—not a behavioral hazard.
r2-p004fix ok prediction offNumber-string comparison coercion relianceWhen the literal is a number and the variable is a string (e.g., `5 > count`), rewriting to `count < 5` preserves semantics but relies on implicit coercion; no change in behavior, though the rule’s fi
r2-p005fix okExceptions inside rewritten codeexceptions thrown inside the code the fix removes or rewrites
r2-p006fix okPartial fix breaking range checkIn `if (0 <= x && x < 1)`, fixing only the first part (`0 <= x` → `x >= 0`) may break the intended range check if the fix is applied partially or incorrectly.
r2-p007fix okGetter side effects evaluation timingRewriting `a === b` when `a` is a getter with side effects changes evaluation timing and may alter observable state.
r2-p008silent prediction offASI and semicolon-less code leading paren/bracketASI and semicolon-less code, leading ( or [ on the next line

Sources

r1-p001 · Strict Mode Semantics
1"use strict";
2let result = [];
3function test(val) {
4 if ("red" === val) { // rule fires: swap to val === "red"
5 try { x = 1; } catch(e) { result.push('strict-assign-error'); }
6 result.push('matched');
7 } else {
8 try { x = 2; } catch(e) { result.push('non-strict-assign-error'); }
9 result.push('unmatched');
10 }
11}
12test("red");
13test("blue");
14console.log(result.join('|'));
--- orig.js	2026-10-22 14:33:44.674427750 -0400
+++ fixed.js	2026-10-22 14:33:47.897469693 -0400
@@ -1,7 +1,7 @@
 "use strict";
 let result = [];
 function test(val) {
-  if ("red" === val) {   // rule fires: swap to val === "red"
+  if (val === "red") {   // rule fires: swap to val === "red"
     try { x = 1; } catch(e) { result.push('strict-assign-error'); }
     result.push('matched');
   } else {
strict-assign-error|matched|non-strict-assign-error|unmatched
r1-p002 · Var Hoisting and Re-declaration
1function test() {
2 if (5 > count) { // rule fires: swap to count > 5
3 var count = 3;
4 return 'inside';
5 }
6 var count = 10;
7 return 'outside';
8}
9console.log(test());
--- orig.js	2026-10-22 14:33:44.675427763 -0400
+++ fixed.js	2026-10-22 14:33:48.033471473 -0400
@@ -1,5 +1,5 @@
 function test() {
-  if (5 > count) {       // rule fires: swap to count > 5
+  if (count < 5) {       // rule fires: swap to count > 5
     var count = 3;
     return 'inside';
   }
outside
r1-p003 · Tagged Template Semantics
1function tag(strings, ...values) {
2 return strings.raw[0] + '-' + (values[0] || 'empty');
3}
4let value = 'hello';
5let result = tag`red` === tag`${value}` ? 'equal' : 'not-equal';
6console.log(result); // Original: 'red-' + 'hello', Fixed would be: tag`${value}` === tag`red` → same comparison
7// But if we had: tag`red` === ${value} --- this doesn't fire rule because RHS is not template literal
8// Adjust: make it tag`red` === tag`${value}` --- still symmetric
9// Need asymmetric: tagged vs untagged
10function id(s) { return s; }
11result = id`red` === `${value}` ? 'equal' : 'not-equal';
12console.log(result); // Rule fires on: id`red` === `${value}` → swap to: `${value}` === id`red`
13// Original: left=id`red` (returns 'red'), right=`${value}` (string 'hello') → 'red' === 'hello' → false
14// Fixed: left=`${value}` ('hello'), right=id`red` (tag function called with ['red'], returns 'red') → 'hello' === 'red' → false
15// Still same? Yes, because both are string comparisons.
16// Real hazard: if tag has side effect
17let callLog = '';
18function sideTag(strings, ...values) {
19 callLog += 'tag-called:' + (strings.raw[0]||'');
20 return strings.raw[0];
21}
22result = sideTag`red` === `${value}` ? 'equal' : 'not-equal';
23console.log(result + '|' + callLog);
not-equal
not-equal
not-equal|tag-called:red
r1-p004 · Function Declarations in Blocks
1function test() {
2 if (typeof helper === 'function') {
3 if (0 === helper()) { // rule fires: swap to helper() === 0
4 return 'early';
5 }
6 }
7 function helper() { return 42; }
8 return helper();
9}
10console.log(test());
--- orig.js	2026-10-22 14:33:44.677427789 -0400
+++ fixed.js	2026-10-22 14:33:47.912469889 -0400
@@ -1,6 +1,6 @@
 function test() {
   if (typeof helper === 'function') {
-    if (0 === helper()) {   // rule fires: swap to helper() === 0
+    if (helper() === 0) {   // rule fires: swap to helper() === 0
       return 'early';
     }
   }
42
r1-p006 · Tagged Template Literal Wrapping Misunderstanding
1function emoji(strings, ...values) {
2 return strings[0] + (values[0]||'').toUpperCase() + '😀';
3}
4let mood = 'happy';
5let result = emoji`cool` === `${mood}` ? 'match' : 'no-match';
6console.log(result); // Original: left=emoji`cool`→'cool😀', right=`${mood}`→'happy' → false
7 // Fixed: left=`${mood}`→'happy', right=emoji`cool`→'cool😀' → still false
8// Change test: make tagged template produce non-string
9function objTag(strings, ...values) {
10 return { toString: () => strings[0] + '-obj' };
11}
12result = objTag`gem` === `${mood}` ? 'match' : 'no-match';
13console.log(result); // Original: left={toString:'gem-obj'}, right='happy' → compares objects via ToPrimitive → 'gem-obj' === 'happy' → false
14 // Fixed: left='happy', right={toString:'gem-obj'} → same comparison → still false
15// Need: make one side effectful
16let tagCallSide = 0;
17function sideTag(strings, ...values) {
18 tagCallSide++;
19 return strings[0];
20}
21if (sideTag`start` === `${mood}`) {
22 tagCallSide += 100;
23}
24console.log('tag-call-side:' + tagCallSide); // Original: condition false, sideTag called once → 1
25 // Fixed: same, sideTag still called once as right operand
26// Hazard only if tag is on swapped-to-left side and we rely on it being first arg
27// Final test: make tag return different based on position
28let tagCallLog = '';
29function posTag(strings, ...values) {
30 tagCallLog += 'arg0:' + (strings.raw[0]||'');
31 return strings.raw[0];
32}
33let testVal = 'foo';
34let eq = posTag`red` === `${testVal}`;
35console.log('equality:' + eq + '|log:' + tagCallLog); // Original: tagCallLog='arg0:red'
36 // Fixed: tagCallLog='arg0:foo' (because now left side is `${testVal}`? No—wait)
37// Original code: posTag`red` === `${testVal}`
38// Left: posTag`red` → calls posTag with strings=['raw':'red'] → tagCallLog+='arg0:red'
39// Right: `${testVal}` → 'foo'
40// After fix: `${testVal}` === posTag`red`
41// Left: `${testVal}` → 'foo'
42// Right: posTag`red` → calls posTag again → tagCallLog+='arg0:red' (same!)
43// So still logs 'arg0:red' twice? No, only once because condition is evaluated once.
44// Actually: the tag function is called exactly once in either case, as the tagged template subexpression.
45// Therefore, no behavioral change from swapping alone.
46// Conclusion: hazard is theoretical but not triggered by this rule's actual fix.
47// Instead, use: the fix does not remove wrapping, so no issue there.
48// Fallback to near-miss: when both sides are same type, no change.
49console.log('no-change-confirmed');
no-match
no-match
tag-call-side:1
equality:false|log:arg0:red
no-change-confirmed
r1-p007 · Sparse Arrays and Holes
1let arr1 = [, , 3]; // length 3, holes at 0,1
2let arr2 = [1, 2, 3];
3let sum = 0;
4for (let i = 0; i < arr1.length; i++) {
5 if (0 <= i && i < arr2.length) { // rule fires on 0 <= i : swap to i >= 0 (no change)
6 // and on i < arr2.length : swap to arr2.length > i
7 sum += (arr1[i] ?? 0) + (arr2[i] ?? 0);
8 }
9}
10console.log(sum); // Original: i=0: 0<=0 true, i<3 true → add (undefined??0)+1 = 0+1=1
11 // i=1: 0<=1 true, i<3 true → add 0+2=2
12 // i=2: 0<=2 true, i<3 true → add 3+3=6 → total=9
13// Fix on i < arr2.length → arr2.length > i: same behavior
14// Make it fire on left side: change to -1 < i
15sum = 0;
16for (let i = 0; i < 3; i++) {
17 if (-1 < i && i < 3) { // rule fires on -1 < i : swap to i > -1
18 sum += (arr1[i] ?? 0) * (arr2[i] ?? 0);
19 }
20}
21console.log(sum); // Original: i=0: -1<0 true → include → 0*1=0
22 // i=1: -1<1 true → 0*2=0
23 // i=2: -1<2 true → 3*3=9 → total=9
24// Fixed: i > -1 → same
25// Need: make the swapped condition have side effect
26let triggerLeft = 0;
27function check() {
28 triggerLeft++;
29 return 5;
30}
31sum = 0;
32for (let i = 0; i < 3; i++) {
33 if (check() > i && i < 3) { // rule fires on check() > i : swap to i < check()
34 sum += i;
35 }
36}
37console.log('sum:' + sum + '|triggerLeft:' + triggerLeft); // Original: i=0: check()→5>0 true → sum+=0, triggerLeft=1
38 // i=1: check()→5>1 true → sum+=1, triggerLeft=2
39 // i=2: check()→5>2 true → sum+=2, triggerLeft=3 → sum=3
40// Fixed: i < check()
41// i=0: 0<check()→5 true → sum+=0, triggerLeft=1 (check called)
42// i=1: 1<5 true → sum+=1, triggerLeft=2
43// i=2: 2<5 true → sum+=2, triggerLeft=3 → same
44// Hazard: if check() had side effect that changed based on call order? Not here.
45// Final: make check return i-dependent value
46let callLog = '';
47function checkVal(x) {
48 callLog += 'c' + x;
49 return x + 2;
50}
51sum = 0;
52for (let i = 0; i < 3; i++) {
53 if (checkVal(i) > i) { // rule fires: swap to i < checkVal(i)
54 sum += i;
55 }
56}
57console.log('sum:' + sum + '|log:' + callLog); // Original: i=0: checkVal(0)=2>0 true → sum+=0, log='c0'
58 // i=1: checkVal(1)=3>1 true → sum+=1, log='c0c1'
59 // i=2: checkVal(2)=4>2 true → sum+=2, log='c0c1c2' → sum=3
60// Fixed: i < checkVal(i)
61// i=0: 0 < checkVal(0) → checkVal(0) called → log+='c0', returns 2 → 0<2 true → sum+=0
62// i=1: 1 < checkVal(1) → checkVal(1) → log+='c1', returns 3 → 1<3 true → sum+=1
63// i=2: 2 < checkVal(2) → checkVal(2) → log+='c2', returns 4 → 2<4 true → sum+=2 → same
64// Still no difference because condition is pure and deterministic.
65// Accept that for this hazard, behavioral change is rare, but we show interaction with loop and side effects in predicate.
66console.log('end');
--- orig.js	2026-10-22 14:33:44.678427802 -0400
+++ fixed.js	2026-10-22 14:33:47.953470425 -0400
@@ -2,7 +2,7 @@
 let arr2 = [1, 2, 3];
 let sum = 0;
 for (let i = 0; i < arr1.length; i++) {
-  if (0 <= i && i < arr2.length) {   // rule fires on 0 <= i : swap to i >= 0 (no change)
+  if (i >= 0 && i < arr2.length) {   // rule fires on 0 <= i : swap to i >= 0 (no change)
                                      // and on i < arr2.length : swap to arr2.length > i
     sum += (arr1[i] ?? 0) + (arr2[i] ?? 0);
   }
@@ -14,7 +14,7 @@
 // Make it fire on left side: change to -1 < i
 sum = 0;
 for (let i = 0; i < 3; i++) {
-  if (-1 < i && i < 3) {             // rule fires on -1 < i : swap to i > -1
+  if (i > -1 && i < 3) {             // rule fires on -1 < i : swap to i > -1
     sum += (arr1[i] ?? 0) * (arr2[i] ?? 0);
   }
 }
9
9
sum:3|triggerLeft:3
sum:3|log:c0c1c2
end
r1-p008 · Null/Undefined Distinction
1let obj = { nested: { value: 42 } };
2let mid = null;
3let result = [];
4function test() {
5 if (mid?.nested === undefined && 0 < obj.nested.value) { // rule fires on 0 < obj.nested.value : swap to obj.nested.value > 0
6 result.push('branch-a');
7 } else {
8 result.push('branch-b');
9 }
10}
11test(); // Original: mid?.nested is undefined (since mid=null), undefined === undefined → true
12 // and 0 < obj.nested.value (42) → true → branch-a
13// Fixed: obj.nested.value > 0 is evaluated first? No, && leaves left-to-right.
14// Actually: swap only changes the comparison, not order of evaluation.
15// Original: (mid?.nested === undefined) && (0 < obj.nested.value)
16// Fixed: (mid?.nested === undefined) && (obj.nested.value > 0) → same
17// Make it: if (0 < obj.nested.value && mid?.nested === undefined)
18test = () => {
19 if (0 < obj.nested.value && mid?.nested === undefined) { // rule fires on 0 < obj.nested.value : swap to obj.nested.value > 0
20 result.push('branch-a');
21 } else {
22 result.push('branch-b');
23 }
24};
25result = [];
26test(); // Original: 0 < obj.nested.value (42) → true, then mid?.nested === undefined → true → branch-a
27// Fixed: obj.nested.value > 0 → true, then mid?.nested === undefined → true → same
28// Need: make second part have side effect that only runs if first is true
29let accessLog = '';
30obj = {
31 get nested() {
32 accessLog += 'nested-access';
33 return { value: 42 };
34 }
35};
36test = () => {
37 if (0 < obj.nested.value && mid?.nested === undefined) { // rule fires on 0 < obj.nested.value
38 result.push('yes');
39 }
40};
41result = [];
42test();
43console.log('access-log:' + accessLog + '|result:' + (result[0]||'none')); // Original: accesses obj.nested → log+='nested-access', then checks mid?.nested
44 // Fixed: same, because left operand of && is still obj.nested.value access
45// Hazard only if the swapped expression is on the left of && and we rely on short-circuit skipping it
46// Example: if (false && expensive()) {} vs if (expensive() && false) {} — but rule doesn't fire on false
47// Make it: if (`red` === color && obj.prop) --- rule fires on left
48obj = { prop: { get value() { accessLog += 'prop'; return 5; } } };
49let color = 'blue';
50test = () => {
51 if (`red` === color && obj.prop.value > 0) { // rule fires: swap to color === `red`
52 result.push('in');
53 }
54};
55result = [];
56test();
57console.log('access-log:' + accessLog + '|result:' + (result[0]||'none')); // Original: `red` === 'blue' → false → short-circuit, skips obj.prop.value → log unchanged
58 // Fixed: color === `red` → 'blue' === 'red' → false → same short-circuit
59// Still no difference because equality is commutative.
60// Conclusion: for primitive equality, swapping never changes truth value or short-circuit behavior.
61// Instead, use non-commutative operator that rule doesn't touch, or accept that hazard is not applicable.
62// Fallback: demonstrate that rule does not fire on nullish coalescing, so no swap occurs there.
63console.log('null-undifferentiated');
--- orig.js	2026-10-22 14:33:47.217460802 -0400
+++ fixed.js	2026-10-22 14:33:50.452503315 -0400
@@ -2,7 +2,7 @@
 let mid = null;
 let result = [];
 function test() {
-  if (mid?.nested === undefined && 0 < obj.nested.value) {   // rule fires on 0 < obj.nested.value : swap to obj.nested.value > 0
+  if (mid?.nested === undefined && obj.nested.value > 0) {   // rule fires on 0 < obj.nested.value : swap to obj.nested.value > 0
     result.push('branch-a');
   } else {
     result.push('branch-b');
@@ -16,7 +16,7 @@
 //        Fixed:    (mid?.nested === undefined) && (obj.nested.value > 0) → same
 // Make it: if (0 < obj.nested.value && mid?.nested === undefined)
 test = () => {
-  if (0 < obj.nested.value && mid?.nested === undefined) {   // rule fires on 0 < obj.nested.value : swap to obj.nested.value > 0
+  if (obj.nested.value > 0 && mid?.nested === undefined) {   // rule fires on 0 < obj.nested.value : swap to obj.nested.value > 0
     result.push('branch-a');
   } else {
     result.push('branch-b');
@@ -34,7 +34,7 @@
   }
 };
 test = () => {
-  if (0 < obj.nested.value && mid?.nested === undefined) {   // rule fires on 0 < obj.nested.value
+  if (obj.nested.value > 0 && mid?.nested === undefined) {   // rule fires on 0 < obj.nested.value
     result.push('yes');
   }
 };
@@ -48,7 +48,7 @@
 obj = { prop: { get value() { accessLog += 'prop'; return 5; } } };
 let color = 'blue';
 test = () => {
-  if (`red` === color && obj.prop.value > 0) {   // rule fires: swap to color === `red`
+  if (color === `red` && obj.prop.value > 0) {   // rule fires: swap to color === `red`
     result.push('in');
   }
 };
access-log:nested-access|result:yes
access-log:nested-access|result:none
null-undifferentiated
r2-p001 · Loose equality readable intent preservation
1let flag = false;
2console.log(true == flag ? 'yes' : 'no'); // triggers yoda
3// After fix: flag == true -> same result
4flag = true;
5console.log(true == flag ? 'yes' : 'no');
--- orig.js	2026-10-22 14:33:59.931630833 -0400
+++ fixed.js	2026-10-22 14:34:03.105674510 -0400
@@ -1,5 +1,5 @@
 let flag = false;
-console.log(true == flag ? 'yes' : 'no'); // triggers yoda
+console.log(flag == true ? 'yes' : 'no'); // triggers yoda
 // After fix: flag == true -> same result
 flag = true;
-console.log(true == flag ? 'yes' : 'no');
+console.log(flag == true ? 'yes' : 'no');
no
yes
r2-p002 · Call expression evaluation order change
1let called = 0;
2function getX() { called++; return 5; }
3const y = 3;
4if (y < getX()) { // triggers yoda: 3 < getX()
5 console.log('first', called); // getX called once
6}
7called = 0;
8if (getX() > y) { // would be fix: getX() > 3
9 console.log('second', called); // getX called once
10}
first 1
second 1
r2-p003 · String literal escape sequences minification concern
1let input = '\n';
2console.log('\n' === input ? 'eq' : 'ne'); // triggers yoda
3input = 'a';
4console.log('\n' === input ? 'eq' : 'ne');
--- orig.js	2026-10-22 14:33:59.933630860 -0400
+++ fixed.js	2026-10-22 14:34:04.238690221 -0400
@@ -1,4 +1,4 @@
 let input = '\n';
-console.log('\n' === input ? 'eq' : 'ne'); // triggers yoda
+console.log(input === '\n' ? 'eq' : 'ne'); // triggers yoda
 input = 'a';
-console.log('\n' === input ? 'eq' : 'ne');
+console.log(input === '\n' ? 'eq' : 'ne');
eq
ne
r2-p004 · Number-string comparison coercion reliance
1let count = '3';
2console.log(5 > count ? 'gt' : 'le'); // NOT yoda trigger: 5 > '3' -> true
3// Rule does not fire because fix would change operator direction
4// Output remains deterministic regardless
5count = '10';
6console.log(5 > count ? 'gt' : 'le');
--- orig.js	2026-10-22 14:33:59.933630860 -0400
+++ fixed.js	2026-10-22 14:34:02.543666745 -0400
@@ -1,6 +1,6 @@
 let count = '3';
-console.log(5 > count ? 'gt' : 'le'); // NOT yoda trigger: 5 > '3' -> true
+console.log(count < 5 ? 'gt' : 'le'); // NOT yoda trigger: 5 > '3' -> true
 // Rule does not fire because fix would change operator direction
 // Output remains deterministic regardless
 count = '10';
-console.log(5 > count ? 'gt' : 'le');
+console.log(count < 5 ? 'gt' : 'le');
gt
le
r2-p005 · Exceptions inside rewritten code
1function bad() { throw new Error('side effect'); }
2let x = 1;
3try {
4 if (0 == bad()) { // triggers yoda: 0 == bad()
5 console.log('unreachable');
6 }
7} catch (e) {
8 console.log('caught:', e.name, e.message);
9}
10x = 0;
11try {
12 if (bad() == 0) { // would be fix: bad() == 0
13 console.log('unreachable');
14 }
15} catch (e) {
16 console.log('caught:', e.name, e.message);
17}
--- orig.js	2026-10-22 14:33:59.934630874 -0400
+++ fixed.js	2026-10-22 14:34:04.414692668 -0400
@@ -1,7 +1,7 @@
 function bad() { throw new Error('side effect'); }
 let x = 1;
 try {
-  if (0 == bad()) { // triggers yoda: 0 == bad()
+  if (bad() == 0) { // triggers yoda: 0 == bad()
     console.log('unreachable');
   }
 } catch (e) {
caught: Error side effect
caught: Error side effect
r2-p006 · Partial fix breaking range check
1let x = 0.5;
2if (0 <= x && x < 1) { // triggers yoda on 0 <= x
3 console.log('in range'); // should print
4} else {
5 console.log('out');
6}
7x = 1.5;
8if (0 <= x && x < 1) {
9 console.log('in range');
10} else {
11 console.log('out'); // should print
12}
--- orig.js	2026-10-22 14:33:59.935630888 -0400
+++ fixed.js	2026-10-22 14:34:04.268690638 -0400
@@ -1,11 +1,11 @@
 let x = 0.5;
-if (0 <= x && x < 1) { // triggers yoda on 0 <= x
+if (x >= 0 && x < 1) { // triggers yoda on 0 <= x
   console.log('in range'); // should print
 } else {
   console.log('out');
 }
 x = 1.5;
-if (0 <= x && x < 1) {
+if (x >= 0 && x < 1) {
   console.log('in range');
 } else {
   console.log('out'); // should print
in range
out
r2-p007 · Getter side effects evaluation timing
1let callCount = 0;
2const obj = {
3 get val() {
4 callCount++;
5 return 42;
6 }
7};
8if (42 === obj.val) { // triggers yoda: 42 === obj.val
9 console.log('first:', callCount); // 1
10}
11callCount = 0;
12if (obj.val === 42) { // would be fix
13 console.log('second:', callCount); // 1
14}
--- orig.js	2026-10-22 14:34:04.159689122 -0400
+++ fixed.js	2026-10-22 14:34:06.785725758 -0400
@@ -5,7 +5,7 @@
     return 42;
   }
 };
-if (42 === obj.val) { // triggers yoda: 42 === obj.val
+if (obj.val === 42) { // triggers yoda: 42 === obj.val
   console.log('first:', callCount); // 1
 }
 callCount = 0;
first: 1
second: 1
r2-p008 · ASI and semicolon-less code leading paren/bracket
1let x = 1
2let y = 2
3if (x < y) [].push.call(arguments, 1) // triggers yoda: x < y
4console.log('no error')
5let a = 1
6let b = 2
7if (a > b) (function(){ console.log('should not run') })()
8console.log('no error')
no error
no error