fixhunt

eslint prefer-numeric-literals

labelled safe
Runs / 2026-10-22T18-27-24-634Z-n55b-eslint-prefer-numeric-literalseslint 9.39.52026-10-22 18:27 UTC8 programs × 2 round(s)16 programs, 9 fired, 9 fixed, 1 findings, 2 rejectedmodel cost $0.01

Analysis Nemotron 3 Super

Flags parseInt() or Number.parseInt() calls with a string literal/template literal as first argument and radix 2, 8, or 16 as second argument.

Fix: Replaces the call with the corresponding numeric literal: binary (0b...), octal (0o...), or hexadecimal (0x...) based on the radix and the string value interpreted in that radix.

8 hazards
  • The string argument might contain whitespace or non-radix characters causing parseInt to return NaN while the literal would be a syntax error, changing behavior from NaN to early error.
  • If the string starts with '0x', '0b', or '0o', parseInt ignores the prefix per radix but the literal would reinterpret it, altering the value.
  • Template literals with embedded expressions (e.g., ${x}) cannot be converted to literals, so the fix would fail or produce invalid code.
  • The string might be empty or only whitespace, making parseInt return NaN whereas the literal would be invalid or zero, changing the result.
  • If the string represents a number too large for the literal form (e.g., beyond safe integer), precision could differ between parseInt and the literal.
  • The call might be inside a larger expression where replacing it with a literal changes operator precedence or associativity if not properly parenthesized.
  • If parseInt or Number.parseInt is rebound to a different value in scope, the fix incorrectly assumes it is the built-in and removes the call entirely.
  • Comments attached to the call or its arguments would be lost when the entire expression is replaced by the literal.

Findings

programoraclelabeljudgestatussummary
r1-p006fix-changes-behaviorlabelled safereal · highrelatedThe program uses the result of parseInt('111110111', 2) as a callable function, but the fix replaces it with the numeric literal 503, which is not callable. Th…

Programs

idresulttitlehazard
r1-p001silent prediction offWhitespace causes NaN vs SyntaxErrorString argument contains whitespace making parseInt return NaN, but numeric literal would throw SyntaxError during parsing.
r1-p002fix okComments lost during rewriteComments attached to the parseInt call are discarded when replaced with a numeric literal, altering source-code documentation.
r1-p003silent prediction offLarge integer precision lossBinary string represents a number > Number.MAX_SAFE_INTEGER; parseInt returns approximate float, while 0b literal may overflow or lose precision differently.
r1-p004fix okVar hoisting and redeclaration in loopRewriting inside a loop interacts with var hoisting and possible redeclaration, changing temporal dead zone or initialization timing.
r1-p005silentTemplate literal with embedded expressionTemplate literal contains ${x}, so rule should not fire (silent), but if incorrectly applied would produce invalid code.
r1-p006fix-changes-behaviorASI with leading parenthesisMissing semicolon causes next line's '(' to be interpreted as function call, breaking after rewrite due to altered precedence.
r1-p007silent prediction offRegex interaction via lastIndexRewrite occurs inside regex replacement function; altered value changes match outcome due to side effects on global state.
r1-p008fix ok__proto__ key in object literalRewritten numeric literal used as __proto__ key in object literal, where original parseInt call might have been dynamic but literal causes early error or different proto assignment.
r2-p001silent prediction offLarge integer precision lossIf the string represents a number too large for the literal form (e.g., beyond safe integer), precision could differ between parseInt and the literal.
r2-p002fix okFunction declarations inside blocksfunction declarations inside blocks, switch cases, labeled statements
r2-p003invalidPrefix reinterpretation (0x, 0b, 0o)If the string starts with '0x', '0b', or '0o', parseInt ignores the prefix per radix but the literal would reinterpret it, altering the value.
r2-p004fix okVar hoisting and redeclaration inside loopsvar hoisting and re-declaration inside loops or after use
r2-p005fix okArguments object and function.lengtharguments object, function.length, default parameters, rest parameters
r2-p006fix okTemporal dead zone after hoisting/reordertemporal dead zone after code is hoisted or reordered
r2-p007fix okThis binding: arrow vs function, methods extractedthis binding: arrow vs function, methods extracted from objects, call/apply/bind
r2-p008invalidLabel/break/continue control flow, finally blocks, early returnslabel/break/continue control flow, finally blocks, early returns in nested blocks

Sources

r1-p001 · Whitespace causes NaN vs SyntaxError
1let s = ' 111110111 '; try { console.log(parseInt(s, 2)); } catch (e) { console.log(e.name + ': ' + e.message); }
503
r1-p002 · Comments lost during rewrite
1// comment before
2console.log(parseInt('111110111', 2) /* inline */ + 1); // comment after
--- orig.js	2026-10-22 14:27:32.530083244 -0400
+++ fixed.js	2026-10-22 14:27:36.975020401 -0400
@@ -1,2 +1,2 @@
 // comment before
-console.log(parseInt('111110111', 2) /* inline */ + 1); // comment after
+console.log(0b111110111 /* inline */ + 1); // comment after
504
r1-p003 · Large integer precision loss
1let big = '1'.repeat(53); console.log(String(parseInt(big, 2)) === String(0b1.concat('1'.repeat(52))) ? 'same' : 'diff');
r1-p004 · Var hoisting and redeclaration in loop
1for (var i = 0; i < 2; i++) { if (i === 0) var x = parseInt('111110111', 2); else console.log(typeof x); }
--- orig.js	2026-10-22 14:27:32.531083229 -0400
+++ fixed.js	2026-10-22 14:27:36.969020485 -0400
@@ -1 +1 @@
-for (var i = 0; i < 2; i++) { if (i === 0) var x = parseInt('111110111', 2); else console.log(typeof x); }
+for (var i = 0; i < 2; i++) { if (i === 0) var x = 0b111110111; else console.log(typeof x); }
number
r1-p005 · Template literal with embedded expression
1let x = 1; console.log(parseInt(`1${x}`, 2) === NaN ? 'NaN' : 'unexpected');
unexpected
r1-p006 · ASI with leading parenthesis
1let a = parseInt('111110111', 2)
2(function(){ console.log('called'); })()
--- orig.js	2026-10-22 14:27:32.532083215 -0400
+++ fixed.js	2026-10-22 14:27:35.136046154 -0400
@@ -1,2 +1,2 @@
-let a = parseInt('111110111', 2)
+let a = 0b111110111
 (function(){ console.log('called'); })()
r1-p007 · Regex interaction via lastIndex
1let str = '111110111'; let regex = /(\d+)/g; regex.lastIndex = 0; let result = str.replace(regex, (m) => parseInt(m, 2)); console.log(result);
503
r1-p008 · __proto__ key in object literal
1let o = { ['__proto__']: parseInt('111110111', 2) }; console.log(o.__proto__ === Number.prototype ? 'Number' : 'other');
--- orig.js	2026-10-22 14:27:35.585039842 -0400
+++ fixed.js	2026-10-22 14:27:39.132990632 -0400
@@ -1 +1 @@
-let o = { ['__proto__']: parseInt('111110111', 2) }; console.log(o.__proto__ === Number.prototype ? 'Number' : 'other');
+let o = { ['__proto__']: 0b111110111 }; console.log(o.__proto__ === Number.prototype ? 'Number' : 'other');
other
r2-p001 · Large integer precision loss
1// Binary string representing 2^53 + 1
2const s = '1' + '0'.repeat(53) + '1'; // 1000...001 (54 bits)
3console.log('parseInt:', parseInt(s, 2));
4// This triggers prefer-numeric-literals: rewrite to 0b1000...001
5// But 0b1000...001 > Number.MAX_SAFE_INTEGER, so literal loses precision
6console.log('literal attempt:', 0b1000000000000000000000000000000000000000000000000000001);
7console.log('Are they equal?', parseInt(s, 2) === 0b1000000000000000000000000000000000000000000000000000001);
parseInt: 18014398509481984
literal attempt: 18014398509481984
Are they equal? true
r2-p002 · Function declarations inside blocks
1if (true) {
2 function f() { return parseInt('1010', 2); } // triggers rule: rewrite to 0b1010
3 console.log('f():', f());
4}
5// After fix: f() returns 10
6console.log('After fix behavior:', typeof f === 'function' ? f() : 'ReferenceError');
--- orig.js	2026-10-22 14:27:56.415769142 -0400
+++ fixed.js	2026-10-22 14:28:00.782717869 -0400
@@ -1,5 +1,5 @@
 if (true) {
-  function f() { return parseInt('1010', 2); } // triggers rule: rewrite to 0b1010
+  function f() { return 0b1010; } // triggers rule: rewrite to 0b1010
   console.log('f():', f());
 }
 // After fix: f() returns 10
f(): 10
After fix behavior: 10
r2-p003 · Prefix reinterpretation (0x, 0b, 0o) (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-27-2)
1const str = '0b101'; // starts with 0b
2console.log('parseInt(str, 2):', parseInt(str, 2)); // parses '0b101' as binary -> 5
3// Rule triggers: rewrite to 0b0b101
4// But 0b0b101 is invalid? Actually, 0b0b101 -> binary 0b0b101 = 0b10101 = 21
5console.log('Literal 0b0b101:', 0b0b101);
6try {
7 console.log('Are equal?', parseInt(str, 2) === 0b0b101);
8} catch (e) {
9 console.log('Comparison failed:', e.name + ': ' + e.message);
10}
r2-p004 · Var hoisting and redeclaration inside loops
1for (let i = 0; i < 2; i++) {
2 var x = parseInt('10', 2); // triggers rule: rewrite to 0b10 = 2
3 if (i === 0) {
4 var x = 99; // redeclaration in same scope
5 console.log('Loop 0, x after redeclare:', x); // should be 99
6 } else {
7 console.log('Loop 1, x:', x); // after fix: x is 2 from rewritten parseInt
8 }
9}
10console.log('Final x:', x);
--- orig.js	2026-10-22 14:27:56.416769130 -0400
+++ fixed.js	2026-10-22 14:27:59.063737822 -0400
@@ -1,5 +1,5 @@
 for (let i = 0; i < 2; i++) {
-  var x = parseInt('10', 2); // triggers rule: rewrite to 0b10 = 2
+  var x = 0b10; // triggers rule: rewrite to 0b10 = 2
   if (i === 0) {
     var x = 99; // redeclaration in same scope
     console.log('Loop 0, x after redeclare:', x); // should be 99
Loop 0, x after redeclare: 99
Loop 1, x: 2
Final x: 2
r2-p005 · Arguments object and function.length
1function test(a, b = parseInt('10', 2), ...rest) { // b initializer triggers rule: rewrite to 2
2 console.log('a:', a, 'b:', b, 'rest.length:', rest.length, 'arguments.length:', arguments.length);
3 return b;
4}
5console.log('Result:', test(1)); // a=1, b=2 (from fix), rest=[], args len=1
6// Before fix: b would be parseInt('10',2)=2 -> same? Wait, need difference.
7// Let's make b depend on something that changes if rewritten.
8// Actually, the value is same. Need side effect in default param.
9// Redo: make default param have observable effect if rewritten.
--- orig.js	2026-10-22 14:27:56.417769118 -0400
+++ fixed.js	2026-10-22 14:27:59.062737834 -0400
@@ -1,4 +1,4 @@
-function test(a, b = parseInt('10', 2), ...rest) { // b initializer triggers rule: rewrite to 2
+function test(a, b = 0b10, ...rest) { // b initializer triggers rule: rewrite to 2
   console.log('a:', a, 'b:', b, 'rest.length:', rest.length, 'arguments.length:', arguments.length);
   return b;
 }
a: 1 b: 2 rest.length: 0 arguments.length: 1
Result: 2
r2-p006 · Temporal dead zone after hoisting/reorder
1console.log(typeof y); // y not declared yet -> undefined
2var y = (function() { return parseInt('11', 2); })(); // triggers rule: rewrite to 0b11 = 3
3console.log('y:', y);
4// After fix: y = 3
5// Before fix: same value, but let's make TDZ visible via let
6// Redo with let to trigger TDZ error if accessed before declaration
7console.log('\nSecond part:');
8try {
9 console.log(z); // z not declared -> ReferenceError
10} catch (e) {
11 console.log('Caught:', e.name + ': ' + e.message);
12}
13let z = (() => parseInt('11', 2))(); // triggers rule
14console.log('z:', z);
--- orig.js	2026-10-22 14:27:56.417769118 -0400
+++ fixed.js	2026-10-22 14:27:59.158736715 -0400
@@ -1,5 +1,5 @@
 console.log(typeof y); // y not declared yet -> undefined
-var y = (function() { return parseInt('11', 2); })(); // triggers rule: rewrite to 0b11 = 3
+var y = (function() { return 0b11; })(); // triggers rule: rewrite to 0b11 = 3
 console.log('y:', y);
 // After fix: y = 3
 // Before fix: same value, but let's make TDZ visible via let
@@ -10,5 +10,5 @@
 } catch (e) {
   console.log('Caught:', e.name + ': ' + e.message);
 }
-let z = (() => parseInt('11', 2))(); // triggers rule
+let z = (() => 0b11)(); // triggers rule
 console.log('z:', z);
undefined
y: 3

Second part:
Caught: ReferenceError: Cannot access 'z' before initialization
z: 3
r2-p007 · This binding: arrow vs function, methods extracted
1const obj = {
2 val: 5,
3 getNum: function() { return parseInt('101', 2); } // triggers rule: rewrite to 0b101 = 5
4};
5const fn = obj.getNum;
6console.log('obj.getNum():', obj.getNum()); // this bound to obj, but unused -> 5
7console.log('fn():', fn()); // this is global -> still 5 (no this use)
8// Need to use this in the parsed value? Not possible.
9// Instead, make the string depend on this? No, string is literal.
10// Alternative: use this in a way that if rewritten, affects something else.
11// Actually, the value is same. Must rely on side effect.
12// Let's make the parseInt call have a side effect via getter.
13const obj2 = {
14 get str() { return '101'; },
15 getNum: function() { return parseInt(this.str, 2); }
16};
17console.log('obj2.getNum():', obj2.getNum()); // '101' -> 5
18// After fix: still 5
19// Not showing difference. Abandon this hazard for now and use a working one.
20// Switch to label/break hazard which we know works.
--- orig.js	2026-10-22 14:27:56.442768820 -0400
+++ fixed.js	2026-10-22 14:28:00.791717765 -0400
@@ -1,6 +1,6 @@
 const obj = {
   val: 5,
-  getNum: function() { return parseInt('101', 2); } // triggers rule: rewrite to 0b101 = 5
+  getNum: function() { return 0b101; } // triggers rule: rewrite to 0b101 = 5
 };
 const fn = obj.getNum;
 console.log('obj.getNum():', obj.getNum()); // this bound to obj, but unused -> 5
obj.getNum(): 5
fn(): 5
obj2.getNum(): 5
r2-p008 · Label/break/continue control flow, finally blocks, early returns (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-27-2)
1outer:
2for (let i = 0; i < 2; i++) {
3 if (i === 0) {
4 var result = parseInt('10', 2); // triggers rule: rewrite to 0b10 = 2
5 continue outer;
6 }
7 console.log('This should not print');
8}
9console.log('Result after loop:', result); // should be 2
10// After fix: result = 2
11// Before fix: same
12// Make it so that if rewritten, the value changes control flow
13// Need parseInt to return different value.
14// Use hazard 3: prefix reinterpretation but in loop.
15// Redo this whole thing for hazard 8 properly.
16
17// Let's use: if parseInt returns 0, break; else continue
18// String that parseInt interprets differently than literal
19const tricky = '0x10'; // starts with 0x
20console.log('parseInt(tricky, 16):', parseInt(tricky, 16)); // ignores 0x -> parses '0x10' as hex -> 16
21// Rule triggers: rewrite to 0x0x10
22// 0x0x10 = hex 0x0x10 = 0x10 * 16? Actually: 0x0x10 -> hex digits: 0, x, 1, 0 -> invalid? Wait, 0x is prefix, so 0x0x10 is 0x0x10 = 0x10 in hex? No.
23// In JS, 0x0x10 is parsed as: 0x0x10 -> the 0x is prefix, then remaining 0x10 is hex? Actually, after 0x, it expects hex digits. '0x10' has '0' then 'x' -> 'x' is invalid hex digit -> SyntaxError.
24// So: rewrite causes SyntaxError, original returns 16.
25try {
26 const lit = 0x0x10; // This line will throw SyntaxError at parse time
27} catch (e) {
28 console.log('Literal 0x0x10 throws:', e.name + ': ' + e.message);
29}
30console.log('parseInt result:', parseInt(tricky, 16));
31// Now use in control flow:
32if (parseInt(tricky, 16) > 10) {
33 console.log('Value > 10, taking true branch');
34} else {
35 console.log('Value <= 10, false branch');
36}
37// After fix: if we replace parseInt with literal, we get SyntaxError -> script fails.
38// But we want the script to run both before and after to compare.
39// Instead, wrap literal in try/catch if we were to use it, but we don't.
40// Final approach: the rule fires, we show what the literal would be, and note it's different/syntax error.
41// We already did that in hazard 3. For hazard 8, do a clean control flow with a value that changes.
42
43// Use hazard 1: large number, but in control flow.
44// String: 2^53 + 2, which parseInt returns as float, literal may differ.
45// But hard to see in control flow.
46
47// Give up and use a working version for hazard 8: simple loop with continue affected by value.
48// Assume we have a case where parseInt(X,2) vs literal leads to different boolean.
49// Let X be such that parseInt(X,2) == 0 but literal != 0, or vice versa.
50// Impossible since same value unless precision loss.
51// Use precision loss to make equality check fail.
52
53console.log('\n--- Hazard 8 attempt ---');
54const bigStr = '1'.repeat(53); // 53 ones in binary = 2^53 - 1
55console.log('parseInt(bigStr, 2):', parseInt(bigStr, 2)); // 9007199254740991
56console.log('0b' + bigStr + ' as literal would be syntax error (too long)? Actually, we can\'t write it.');
57// Instead, use a known big number.
58console.log('Number.MAX_SAFE_INTEGER:', Number.MAX_SAFE_INTEGER);
59const s2 = '1000000000000000000000000000000000000000000000000000000'; // 1 followed by 52 zeros = 2^52
60console.log('parseInt(s2, 2):', parseInt(s2, 2)); // 2^52 = 4503599627370496
61console.log('Literal 0b' + s2 + ':', 0b1000000000000000000000000000000000000000000000000000000); // same
62// Not helpful.
63
64// Fallback: use hazard 3 value in a conditional that breaks.
65console.log('\nUsing hazard 3 value in conditional:');
66const tricky2 = '0b101'; // parseInt -> 5
67console.log('parseInt(\"0b101\", 2):', parseInt(tricky2, 2)); // 5
68// Literal would be 0b0b101
69console.log('What 0b0b101 would be:', 0b0b101); // 21
70if (parseInt(tricky2, 2) === 5) {
71 console.log('True branch (parseInt)');
72} else {
73 console.log('False branch');
74}
75// After fix: if replaced with literal, condition becomes (21 === 5) -> false
76// So we would see false branch.
77// To show this, we need to run the code as-if it were fixed, but we can't.
78// Instead, we compute both and show the difference.
79console.log('After fix, condition would be:', (0b0b101 === 5) ? 'true' : 'false');
80console.log('This demonstrates control flow would change.');
81}