eslint eqeqeq
labelled safe
Analysis Nemotron 3 Super
Flags uses of loose equality (`==`, `!=`) unless they involve typeof expressions or compare two literals of the same type.
Fix: Replaces `==` with `===` and `!=` with `!==` while preserving whitespace around the operator.
10 hazards
- Changing `x == y` to `x === y` alters behavior when x and y are different types that coerce loosely (e.g., `0 == "0"` is true but `0 === "0"` is false).
- Rewriting `obj.prop != undefined` to `obj.prop !== undefined` fails if obj.prop is undefined but accessing obj.prop throws (e.g., obj is null), though both sides already evaluate the property access—this hazard is invalid; instead, consider: Rewriting `a == b` to `a === b` may break code that relies on type coercion for equivalence checks (e.g., numeric string comparison).
- If the operands include getters with side effects, the fix does not reorder evaluation, so no hazard from evaluation order change.
- If the operands are function calls, the fix does not alter call order or number of invocations.
- If the expression contains comments between operands and operator (e.g., `a /* comment */ == b`), the fix preserves comments because it only replaces the operator token.
- In non-strict mode, the fix does not introduce new syntax errors as `===` and `!==` are valid everywhere `==` and `!=` are.
- If one operand is `null` and the other is `undefined`, `null == undefined` is true but `null !== undefined` is true, so the fix changes semantics—this is a hazard when nullish values are treated as equivalent.
- If the operands are objects that define custom `valueOf` or `toString`, loose equality may invoke them, while strict equality does not, changing behavior (e.g., `{ valueOf: () => 0 } == 0` is true but `{ valueOf: () => 0 } === 0` is false).
- If the code relies on loose equality to treat `-0` and `+0` as equal, the fix preserves this as `===` also treats them as equal—no hazard here.
- If the operands are NaN, `NaN == NaN` is false and `NaN !== NaN` is true, so the fix changes the result from false to true.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fired, no fix | Custom valueOf/toString | If the operands are objects that define custom `valueOf` or `toString`, loose equality may invoke them, while strict equality does not, changing behavior. |
| r1-p002 | fired, no fix | Loose equality with -0 and +0 | If the code relies on loose equality to treat `-0` and `+0` as equal, the fix preserves this as `===` also treats them as equal—no hazard here. |
| r1-p003 | fired, no fix | Type coercion for numeric strings | Rewriting `a == b` to `a === b` may break code that relies on type coercion for equivalence checks (e.g., numeric string comparison). |
| r1-p004 | fired, no fix | Getters with side effects | If the operands include getters with side effects, the fix does not reorder evaluation, so no hazard from evaluation order change. |
| r1-p005 | fired, no fix | Function call operands | If the operands are function calls, the fix does not alter call order or number of invocations. |
| r1-p006 | fired, no fix | NaN comparison | If the operands are NaN, `NaN == NaN` is false and `NaN !== NaN` is true, so the fix changes the result from false to true. |
| r1-p007 | fired, no fix | Regular expression object equality | regular expressions: escapes, unicode flag, lastIndex with the g flag |
| r1-p008 | fired, no fix | Var hoisting and redeclaration | var hoisting and re-declaration inside loops or after use |
| r2-p001 | fired, no fix | ASI and semicolon-less code with leading parenthesis | ASI and semicolon-less code, leading ( or [ on the next line |
| r2-p002 | fired, no fix | Getter with side effects in operands | If the operands include getters with side effects, the fix does not reorder evaluation, so no hazard from evaluation order change. |
| r2-p003 | fired, no fix | Strict vs sloppy mode semantics | strict vs sloppy mode semantics |
| r2-p004 | fired, no fix | Function declarations inside blocks, switch cases, labeled statements | function declarations inside blocks, switch cases, labeled statements |
| r2-p005 | fired, no fix | Var hoisting and re-declaration inside loops or after use | var hoisting and re-declaration inside loops or after use |
| r2-p006 | fired, no fix prediction off | Loose equality treating -0 and +0 as equal | If the code relies on loose equality to treat `-0` and `+0` as equal, the fix preserves this as `===` also treats them as equal—no hazard here. |
| r2-p007 | fired, no fix | Comments between operands and operator | If the expression contains comments between operands and operator (e.g., `a /* comment */ == b`), the fix preserves comments because it only replaces the operator token. |
| r2-p008 | fired, no fix | This binding: arrow vs function, methods extracted from objects, call/apply/bind | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
Sources
r1-p001 · Custom valueOf/toString
1const obj = { valueOf: () => { console.log('valueOf called'); return 0; } };
2console.log(obj == 0);
3console.log(obj === 0);
valueOf called true false
r1-p002 · Loose equality with -0 and +0
1console.log(-0 == +0);
2console.log(-0 === +0);
true true
r1-p003 · Type coercion for numeric strings
1let a = '5', b = 5;
2if (a == b) {
3 console.log('equal via ==');
4}
5if (a === b) {
6 console.log('equal via ===');
7}
equal via ==
r1-p004 · Getters with side effects
1let count = 0;
2const obj = {
3 get prop() {
4 count++;
5 return 42;
6 }
7};
8console.log(obj.prop == 42);
9console.log(count);
true 1
r1-p005 · Function call operands
1let calls = 0;
2function f() { calls++; return 1; }
3console.log(f() == 1);
4console.log(calls);
true 1
r1-p006 · NaN comparison
1console.log(NaN == NaN);
2console.log(NaN !== NaN);
false true
r1-p007 · Regular expression object equality
1const re1 = /a/g;
2const re2 = /a/g;
3re1.lastIndex = 1;
4console.log(re1 == re2);
5console.log(re1 === re2);
false false
r1-p008 · Var hoisting and redeclaration
1for (var i = 0; i < 2; i++) {
2 if (i == 0) {
3 var i = 10;
4 console.log(i);
5 }
6}
7console.log(i);
10 11
r2-p001 · ASI and semicolon-less code with leading parenthesis
1let a = 0
2let b = 0
3(function(){ if (a == b) console.log('equal') })()
4[1,2].forEach(v=>{})
r2-p002 · Getter with side effects in operands
1const obj = { get a() { return console.log('get a'), 1 }, get b() { return console.log('get b'), 2 } }
2if (obj.a == obj.b) console.log('match')
3else console.log('no match')
get a get b no match
r2-p003 · Strict vs sloppy mode semantics
1'use strict'
2function f() { return this }
3console.log((f() == undefined) ? 'undef' : 'defined')
4console.log((f() === undefined) ? 'undef' : 'defined')
undef undef
r2-p004 · Function declarations inside blocks, switch cases, labeled statements
1{ function g() { return 1 }
2if (g() == 1) console.log('one')
3else console.log('not one') }
4switch(0) { case 0: function h(){return 2}; if (h() == 2) console.log('two'); break }
one two
r2-p005 · Var hoisting and re-declaration inside loops or after use
1for (var i = 0; i < 2; i++) {
2 if (i == 0) console.log('zero')
3 else console.log('non-zero')
4 var i = 99
5}
6console.log('i:', i)
zero i: 100
r2-p006 · Loose equality treating -0 and +0 as equal
1let x = -0
2let y = +0
3console.log(x == y ? 'equal' : 'not equal')
4console.log(x === y ? 'equal' : 'not equal')
equal equal
r2-p007 · Comments between operands and operator
1let a = 1
2let b = 1
3if (a /* same value */ == b) console.log('equal')
4else console.log('not equal')
equal
r2-p008 · This binding: arrow vs function, methods extracted from objects, call/apply/bind
1const obj = { value: 42, getValue: function() { return this.value } }
2const fn = obj.getValue
3console.log((fn() == undefined) ? 'undef' : 'defined')
4console.log((fn.call(obj) == 42) ? 'match' : 'no match')
undef match