Skip to content

Commit 45364af

Browse files
mdjermanovicbtmills
authored andcommitted
Fix: prefer-numeric-literals doesn't check types of literal arguments (#12655)
1 parent e3c570e commit 45364af

2 files changed

Lines changed: 51 additions & 29 deletions

File tree

lib/rules/prefer-numeric-literals.js

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ const astUtils = require("./utils/ast-utils");
1515
// Helpers
1616
//------------------------------------------------------------------------------
1717

18+
const radixMap = new Map([
19+
[2, { system: "binary", literalPrefix: "0b" }],
20+
[8, { system: "octal", literalPrefix: "0o" }],
21+
[16, { system: "hexadecimal", literalPrefix: "0x" }]
22+
]);
23+
1824
/**
1925
* Checks to see if a CallExpression's callee node is `parseInt` or
2026
* `Number.parseInt`.
@@ -54,59 +60,54 @@ module.exports = {
5460
},
5561

5662
schema: [],
63+
64+
messages: {
65+
useLiteral: "Use {{system}} literals instead of {{functionName}}()."
66+
},
67+
5768
fixable: "code"
5869
},
5970

6071
create(context) {
6172
const sourceCode = context.getSourceCode();
6273

63-
const radixMap = {
64-
2: "binary",
65-
8: "octal",
66-
16: "hexadecimal"
67-
};
68-
69-
const prefixMap = {
70-
2: "0b",
71-
8: "0o",
72-
16: "0x"
73-
};
74-
7574
//----------------------------------------------------------------------
7675
// Public
7776
//----------------------------------------------------------------------
7877

7978
return {
8079

81-
CallExpression(node) {
82-
83-
// doesn't check parseInt() if it doesn't have a radix argument
84-
if (node.arguments.length !== 2) {
85-
return;
86-
}
80+
"CallExpression[arguments.length=2]"(node) {
81+
const [strNode, radixNode] = node.arguments,
82+
str = strNode.value,
83+
radix = radixNode.value;
84+
85+
if (
86+
strNode.type === "Literal" &&
87+
radixNode.type === "Literal" &&
88+
typeof str === "string" &&
89+
typeof radix === "number" &&
90+
radixMap.has(radix) &&
91+
isParseInt(node.callee)
92+
) {
8793

88-
// only error if the radix is 2, 8, or 16
89-
const radixName = radixMap[node.arguments[1].value];
94+
const { system, literalPrefix } = radixMap.get(radix);
9095

91-
if (isParseInt(node.callee) &&
92-
radixName &&
93-
node.arguments[0].type === "Literal"
94-
) {
9596
context.report({
9697
node,
97-
message: "Use {{radixName}} literals instead of {{functionName}}().",
98+
messageId: "useLiteral",
9899
data: {
99-
radixName,
100+
system,
100101
functionName: sourceCode.getText(node.callee)
101102
},
102103
fix(fixer) {
103104
if (sourceCode.getCommentsInside(node).length) {
104105
return null;
105106
}
106107

107-
const replacement = `${prefixMap[node.arguments[1].value]}${node.arguments[0].value}`;
108+
const replacement = `${literalPrefix}${str}`;
108109

109-
if (+replacement !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
110+
if (+replacement !== parseInt(str, radix)) {
110111

111112
/*
112113
* If the newly-produced literal would be invalid, (e.g. 0b1234),

tests/lib/rules/prefer-numeric-literals.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,28 @@ ruleTester.run("prefer-numeric-literals", rule, {
3131
"parseInt(foo);",
3232
"parseInt(foo, 2);",
3333
"Number.parseInt(foo);",
34-
"Number.parseInt(foo, 2);"
34+
"Number.parseInt(foo, 2);",
35+
"parseInt(11, 2);",
36+
"Number.parseInt(1, 8);",
37+
"parseInt(1e5, 16);",
38+
"parseInt('11', '2');",
39+
"Number.parseInt('11', '8');",
40+
{
41+
code: "parseInt('11', 2n);",
42+
parserOptions: { ecmaVersion: 2020 }
43+
},
44+
{
45+
code: "Number.parseInt('11', 8n);",
46+
parserOptions: { ecmaVersion: 2020 }
47+
},
48+
{
49+
code: "parseInt('11', 16n);",
50+
parserOptions: { ecmaVersion: 2020 }
51+
},
52+
{
53+
code: "parseInt(1n, 2);",
54+
parserOptions: { ecmaVersion: 2020 }
55+
}
3556
],
3657
invalid: [
3758
{

0 commit comments

Comments
 (0)