diff --git a/lib/rules/prefer-numeric-literals.js b/lib/rules/prefer-numeric-literals.js index 672d9bcaa0c..c352d88dc07 100644 --- a/lib/rules/prefer-numeric-literals.js +++ b/lib/rules/prefer-numeric-literals.js @@ -15,6 +15,12 @@ const astUtils = require("./utils/ast-utils"); // Helpers //------------------------------------------------------------------------------ +const radixMap = new Map([ + [2, { system: "binary", literalPrefix: "0b" }], + [8, { system: "octal", literalPrefix: "0o" }], + [16, { system: "hexadecimal", literalPrefix: "0x" }] +]); + /** * Checks to see if a CallExpression's callee node is `parseInt` or * `Number.parseInt`. @@ -54,49 +60,44 @@ module.exports = { }, schema: [], + + messages: { + useLiteral: "Use {{system}} literals instead of {{functionName}}()." + }, + fixable: "code" }, create(context) { const sourceCode = context.getSourceCode(); - const radixMap = { - 2: "binary", - 8: "octal", - 16: "hexadecimal" - }; - - const prefixMap = { - 2: "0b", - 8: "0o", - 16: "0x" - }; - //---------------------------------------------------------------------- // Public //---------------------------------------------------------------------- return { - CallExpression(node) { - - // doesn't check parseInt() if it doesn't have a radix argument - if (node.arguments.length !== 2) { - return; - } + "CallExpression[arguments.length=2]"(node) { + const [strNode, radixNode] = node.arguments, + str = strNode.value, + radix = radixNode.value; + + if ( + strNode.type === "Literal" && + radixNode.type === "Literal" && + typeof str === "string" && + typeof radix === "number" && + radixMap.has(radix) && + isParseInt(node.callee) + ) { - // only error if the radix is 2, 8, or 16 - const radixName = radixMap[node.arguments[1].value]; + const { system, literalPrefix } = radixMap.get(radix); - if (isParseInt(node.callee) && - radixName && - node.arguments[0].type === "Literal" - ) { context.report({ node, - message: "Use {{radixName}} literals instead of {{functionName}}().", + messageId: "useLiteral", data: { - radixName, + system, functionName: sourceCode.getText(node.callee) }, fix(fixer) { @@ -104,9 +105,9 @@ module.exports = { return null; } - const replacement = `${prefixMap[node.arguments[1].value]}${node.arguments[0].value}`; + const replacement = `${literalPrefix}${str}`; - if (+replacement !== parseInt(node.arguments[0].value, node.arguments[1].value)) { + if (+replacement !== parseInt(str, radix)) { /* * If the newly-produced literal would be invalid, (e.g. 0b1234), diff --git a/tests/lib/rules/prefer-numeric-literals.js b/tests/lib/rules/prefer-numeric-literals.js index ddd6e36c8ac..4d3037f4469 100644 --- a/tests/lib/rules/prefer-numeric-literals.js +++ b/tests/lib/rules/prefer-numeric-literals.js @@ -31,7 +31,28 @@ ruleTester.run("prefer-numeric-literals", rule, { "parseInt(foo);", "parseInt(foo, 2);", "Number.parseInt(foo);", - "Number.parseInt(foo, 2);" + "Number.parseInt(foo, 2);", + "parseInt(11, 2);", + "Number.parseInt(1, 8);", + "parseInt(1e5, 16);", + "parseInt('11', '2');", + "Number.parseInt('11', '8');", + { + code: "parseInt('11', 2n);", + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "Number.parseInt('11', 8n);", + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "parseInt('11', 16n);", + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "parseInt(1n, 2);", + parserOptions: { ecmaVersion: 2020 } + } ], invalid: [ {