fixhunt

eslint operator-assignment

labelled safe
Runs / 2026-10-22T18-24-37-533Z-hqb7-eslint-operator-assignmenteslint 9.39.52026-10-22 18:24 UTC8 programs × 2 round(s)15 programs, 11 fired, 10 fixed, 0 findings, 1 rejectedmodel cost $0.0033

Analysis Nemotron 3 Super

Flags assignments like 'x = x op y' where 'op' is an arithmetic or bitwise operator and suggests using the compound assignment shorthand.

Fix: Rewrites 'x = x op y' into 'x op= y', preserving the left-hand side expression and the right-hand side expression exactly as they appear.

10 hazards
  • If the left-hand side has side effects (e.g., a function call or increment), evaluating it twice in the original vs once in the fix changes behavior (e.g., x[foo()] = x[foo()] + 1).
  • If the left-hand side is a complex expression with operators, rewriting may alter evaluation order or precedence (e.g., (x[y]) = (x[y]) + z vs x[y] += z, though generally safe, relies on LHS being stable).
  • If the left-hand side references a variable that is redeclared or changed between evaluations due to hoisting or block scoping, the fix may use a different value (e.g., in a loop with let).
  • If the right-hand side has side effects, the original evaluates LHS then RHS; the fix evaluates LHS once then RHS, but if LHS evaluation has side effects, it now happens only once instead of potentially twice.
  • If the left-hand side is a property access on an object that has a getter with side effects, accessing it twice in the original vs once in the fix changes behavior.
  • If the left-hand side is an array access and the expression used as index has side effects (e.g., x[a++]), evaluating the index twice vs once alters the index value.
  • If the code relies on the specific spacing or formatting for parsing by other tools (e.g., templating engines), removing whitespace could break processing.
  • If there are comments between the tokens of the assignment (e.g., x = /* comment */ x + y;), the fix may discard those comments.
  • If the left-hand side expression is not actually identical due to type coercion or implicit conversion in the original context, the shorthand may not preserve semantics (though rare for these operators).
  • If the assignment is part of a larger expression and the rewrite changes the structure (though it's a statement, unlikely), but if used in an expression context, the shorthand returns a value, same as the original, so generally safe.

Programs

idresulttitlehazard
r1-p001fix okasync await generator yield interactionasync/await ordering, generators and yield position
r1-p002fix okcoercion plus vs template literalnumeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt
r1-p004fired, no fixcomments discarded in fixIf there are comments between the tokens of the assignment (e.g., x = /* comment */ x + y;), the fix may discard those comments.
r1-p005silent prediction offarray index side effect double evalIf the left-hand side is an array access and the expression used as index has side effects (e.g., x[a++]), evaluating the index twice vs once alters the index value.
r1-p006fix okarguments length default restarguments object, function.length, default parameters, rest parameters
r1-p007invalidASI semicolon-less leading parenASI and semicolon-less code, leading ( or [ on the next line
r1-p008fix okclass fields static block private superclass fields, static blocks, private names, super calls
r2-p001fix okSparse Arrays Holessparse arrays, holes, Array(n) vs [n], array-likes
r2-p002fix okVar Hoisting Redeclaration Loopvar hoisting and re-declaration inside loops or after use
r2-p003fix okGetter Side Effect Property AccessIf the left-hand side is a property access on an object that has a getter with side effects, accessing it twice in the original vs once in the fix changes behavior.
r2-p004fix okArguments Object Default Restarguments object, function.length, default parameters, rest parameters
r2-p005fix okWhitespace Dependent TemplatingIf the code relies on the specific spacing or formatting for parsing by other tools (e.g., templating engines), removing whitespace could break processing.
r2-p006fix okThis Binding Arrow Functionthis binding: arrow vs function, methods extracted from objects, call/apply/bind
r2-p007silent prediction offLHS Side Effect Double EvalIf the left-hand side has side effects (e.g., a function call or increment), evaluating it twice in the original vs once in the fix changes behavior (e.g., x[foo()] = x[foo()] + 1).
r2-p008silent prediction offOrder of Evaluation Merged Expressionsorder of evaluation when expressions are merged, duplicated or deleted (side effects run twice or never)

Sources

r1-p001 · async await generator yield interaction
1async function* g() {
2 let i = 0;
3 while (i < 3) {
4 const v = await Promise.resolve(i);
5 if ((yield i++) === undefined) continue;
6 i = i + v; // TRIGGER
7 console.log('state:', i);
8 }
9}
10(async () => {
11 const iter = g();
12 for await (const x of iter) console.log('yielded:', x);
13})().catch(e => console.error(e));
--- orig.js	2026-10-22 14:24:49.429813897 -0400
+++ fixed.js	2026-10-22 14:24:52.737960322 -0400
@@ -3,7 +3,7 @@
   while (i < 3) {
     const v = await Promise.resolve(i);
     if ((yield i++) === undefined) continue;
-    i = i + v; // TRIGGER
+    i += v; // TRIGGER
     console.log('state:', i);
   }
 }
yielded: 0
yielded: 1
yielded: 2
r1-p002 · coercion plus vs template literal
1const o = { valueOf: () => 2, toString: () => 'x' };
2let acc = '';
3acc = acc + o; // TRIGGER
4console.log(typeof acc, acc);
5try {
6 acc = acc + Symbol('foo'); // TRIGGER? No, Symbol + string throws
7} catch (e) {
8 console.log(e.name, e.message);
9}
10console.log('final:', acc);
--- orig.js	2026-10-22 14:24:49.430813942 -0400
+++ fixed.js	2026-10-22 14:24:52.636955850 -0400
@@ -1,9 +1,9 @@
 const o = { valueOf: () => 2, toString: () => 'x' };
 let acc = '';
-acc = acc + o; // TRIGGER
+acc += o; // TRIGGER
 console.log(typeof acc, acc);
 try {
-  acc = acc + Symbol('foo'); // TRIGGER? No, Symbol + string throws
+  acc += Symbol('foo'); // TRIGGER? No, Symbol + string throws
 } catch (e) {
   console.log(e.name, e.message);
 }
string 2
TypeError Cannot convert a Symbol value to a string
final: 2
r1-p004 · comments discarded in fix
1let counter = 0;
2counter = counter /* step */ + 1; // TRIGGER
3console.log('after first:', counter);
4counter = counter /* inline */ + counter; // TRIGGER
5console.log('after second:', counter);
after first: 1
after second: 2
r1-p005 · array index side effect double eval
1const arr = [10, 20, 30];
2let idx = 0;
3arr[idx++] = arr[idx++] + 5; // TRIGGER: idx read twice
4console.log('idx after:', idx, 'arr:', arr);
5// Reset
6idx = 0;
7arr[idx++] = arr[idx++] + 5; // Same line again
8console.log('idx after2:', idx, 'arr2:', arr);
idx after: 2 arr: [ 25, 20, 30 ]
idx after2: 2 arr2: [ 25, 20, 30 ]
r1-p006 · arguments length default rest
1function sum(a, b = 0, ...rest) {
2 let total = a;
3 total = total + b; // TRIGGER
4 for (const v of rest) total = total + v; // TRIGGER
5 console.log('args length:', arguments.length, 'total:', total);
6 return total;
7}
8console.log(sum(1, 2, 3, 4));
--- orig.js	2026-10-22 14:24:49.432814030 -0400
+++ fixed.js	2026-10-22 14:24:52.801963156 -0400
@@ -1,7 +1,7 @@
 function sum(a, b = 0, ...rest) {
   let total = a;
-  total = total + b; // TRIGGER
-  for (const v of rest) total = total + v; // TRIGGER
+  total += b; // TRIGGER
+  for (const v of rest) total += v; // TRIGGER
   console.log('args length:', arguments.length, 'total:', total);
   return total;
 }
args length: 4 total: 10
10
r1-p007 · ASI semicolon-less leading paren (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-24-3)
1let x = 1
2x = x
3+ 2 // TRIGGER: ASI would break without fix? Actually x = x
4+2 is invalid; but we rely on the fix changing behavior if applied incorrectly
5// Instead, make a case where ASI affects
6let y = 1
7y = y
8+ 3
9console.log('x:', x, 'y:', y)
r1-p008 · class fields static block private super
1class Base {
2 constructor() { this.x = 0; }
3 method() { return this.x; }
4}
5class Der extends Base {
6 #y = 5;
7 static {
8 Der.prototype.z = Der.prototype.z + 1; // TRIGGER
9 }
10 constructor() {
11 super();
12 this.#y = this.#y + 2; // TRIGGER
13 }
14 getY() { return this.#y; }
15}
16const d = new Der();
17console.log('instance y:', d.getY(), 'static z:', Object.getPrototypeOf(d).z);
--- orig.js	2026-10-22 14:24:49.454815004 -0400
+++ fixed.js	2026-10-22 14:24:52.689958197 -0400
@@ -9,7 +9,7 @@
   }
   constructor() {
     super();
-    this.#y = this.#y + 2; // TRIGGER
+    this.#y += 2; // TRIGGER
   }
   getY() { return this.#y; }
 }
instance y: 7 static z: NaN
r2-p001 · Sparse Arrays Holes
1let arr = new Array(3);
2arr[0] = 1;
3arr[2] = arr[2] + 1;
4console.log(arr[1]); // hole -> undefined
--- orig.js	2026-10-22 14:24:59.382254681 -0400
+++ fixed.js	2026-10-22 14:25:03.121420489 -0400
@@ -1,4 +1,4 @@
 let arr = new Array(3);
 arr[0] = 1;
-arr[2] = arr[2] + 1;
+arr[2] += 1;
 console.log(arr[1]); // hole -> undefined
undefined
r2-p002 · Var Hoisting Redeclaration Loop
1for (var i = 0; i < 2; i++) {
2 var x = 1;
3 x = x + i;
4}
5console.log(x); // hoisted, accessible outside loop
--- orig.js	2026-10-22 14:24:59.383254725 -0400
+++ fixed.js	2026-10-22 14:25:03.144421510 -0400
@@ -1,5 +1,5 @@
 for (var i = 0; i < 2; i++) {
   var x = 1;
-  x = x + i;
+  x += i;
 }
 console.log(x); // hoisted, accessible outside loop
2
r2-p003 · Getter Side Effect Property Access
1const obj = {
2 get prop() {
3 console.log('get');
4 return 5;
5 }
6};
7obj.prop = obj.prop + 1; // getter called twice originally
--- orig.js	2026-10-22 14:24:59.384254769 -0400
+++ fixed.js	2026-10-22 14:25:03.143421466 -0400
@@ -4,4 +4,4 @@
     return 5;
   }
 };
-obj.prop = obj.prop + 1; // getter called twice originally
+obj.prop += 1; // getter called twice originally
get
r2-p004 · Arguments Object Default Rest
1function f(a = 1, b = 2, ...rest) {
2 arguments[0] = arguments[0] + 1;
3 console.log(arguments.length, rest.length);
4}
5f(); // args: [1,2] -> after: [2,2]; length unchanged but values changed
--- orig.js	2026-10-22 14:24:59.384254769 -0400
+++ fixed.js	2026-10-22 14:25:01.995370544 -0400
@@ -1,5 +1,5 @@
 function f(a = 1, b = 2, ...rest) {
-  arguments[0] = arguments[0] + 1;
+  arguments[0] += 1;
   console.log(arguments.length, rest.length);
 }
 f(); // args: [1,2] -> after: [2,2]; length unchanged but values changed
0 0
r2-p005 · Whitespace Dependent Templating
1let template = `value: ${ x = x + 1 }`;
2x = 0;
3console.log(template); // depends on spacing inside ${}
--- orig.js	2026-10-22 14:24:59.385254814 -0400
+++ fixed.js	2026-10-22 14:25:03.119420401 -0400
@@ -1,3 +1,3 @@
-let template = `value: ${ x = x + 1 }`;
+let template = `value: ${ x += 1 }`;
 x = 0;
 console.log(template); // depends on spacing inside ${}
r2-p006 · This Binding Arrow Function
1const obj = {
2 x: 0,
3 update: function() {
4 this.x = this.x + 1;
5 }
6};
7const fn = obj.update;
8fn.call(obj);
9console.log(obj.x);
--- orig.js	2026-10-22 14:24:59.385254814 -0400
+++ fixed.js	2026-10-22 14:25:03.138421244 -0400
@@ -1,7 +1,7 @@
 const obj = {
   x: 0,
   update: function() {
-    this.x = this.x + 1;
+    this.x += 1;
   }
 };
 const fn = obj.update;
1
r2-p007 · LHS Side Effect Double Eval
1let i = 0;
2const arr = [0, 0];
3arr[i++] = arr[i++] + 1;
4console.log(arr[0], arr[1], i); // i incremented twice vs once
1 0 2
r2-p008 · Order of Evaluation Merged Expressions
1let a = { val: 0 };
2let b = { val: 0 };
3a.val = (b.val = 1) + a.val;
4console.log(a.val, b.val); // depends on order: original: a.val used after b.val assigned
1 1