2 * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
3 * @author Annie Zhang, Henry Zhu
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 const radixMap = new Map([
19 [2, { system: "binary", literalPrefix: "0b" }],
20 [8, { system: "octal", literalPrefix: "0o" }],
21 [16, { system: "hexadecimal", literalPrefix: "0x" }]
25 * Checks to see if a CallExpression's callee node is `parseInt` or
27 * @param {ASTNode} calleeNode The callee node to evaluate.
28 * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
31 function isParseInt(calleeNode) {
32 switch (calleeNode.type) {
34 return calleeNode.name === "parseInt";
35 case "MemberExpression":
36 return calleeNode.object.type === "Identifier" &&
37 calleeNode.object.name === "Number" &&
38 calleeNode.property.type === "Identifier" &&
39 calleeNode.property.name === "parseInt";
47 //------------------------------------------------------------------------------
49 //------------------------------------------------------------------------------
56 description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
57 category: "ECMAScript 6",
59 url: "https://eslint.org/docs/rules/prefer-numeric-literals"
65 useLiteral: "Use {{system}} literals instead of {{functionName}}()."
72 const sourceCode = context.getSourceCode();
74 //----------------------------------------------------------------------
76 //----------------------------------------------------------------------
80 "CallExpression[arguments.length=2]"(node) {
81 const [strNode, radixNode] = node.arguments,
83 radix = radixNode.value;
86 strNode.type === "Literal" &&
87 radixNode.type === "Literal" &&
88 typeof str === "string" &&
89 typeof radix === "number" &&
90 radixMap.has(radix) &&
91 isParseInt(node.callee)
94 const { system, literalPrefix } = radixMap.get(radix);
98 messageId: "useLiteral",
101 functionName: sourceCode.getText(node.callee)
104 if (sourceCode.getCommentsInside(node).length) {
108 const replacement = `${literalPrefix}${str}`;
110 if (+replacement !== parseInt(str, radix)) {
113 * If the newly-produced literal would be invalid, (e.g. 0b1234),
114 * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
119 const tokenBefore = sourceCode.getTokenBefore(node),
120 tokenAfter = sourceCode.getTokenAfter(node);
126 tokenBefore.range[1] === node.range[0] &&
127 !astUtils.canTokensBeAdjacent(tokenBefore, replacement)
134 node.range[1] === tokenAfter.range[0] &&
135 !astUtils.canTokensBeAdjacent(replacement, tokenAfter)
140 return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);