You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
5.1 KiB

4 years ago
/**
* @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
* @author Annie Zhang, Henry Zhu
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
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`.
* @param {ASTNode} calleeNode The callee node to evaluate.
* @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
* false otherwise.
*/
function isParseInt(calleeNode) {
switch (calleeNode.type) {
case "Identifier":
return calleeNode.name === "parseInt";
case "MemberExpression":
return calleeNode.object.type === "Identifier" &&
calleeNode.object.name === "Number" &&
calleeNode.property.type === "Identifier" &&
calleeNode.property.name === "parseInt";
// no default
}
return false;
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
category: "ECMAScript 6",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-numeric-literals"
},
schema: [],
messages: {
useLiteral: "Use {{system}} literals instead of {{functionName}}()."
},
fixable: "code"
},
create(context) {
const sourceCode = context.getSourceCode();
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
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)
) {
const { system, literalPrefix } = radixMap.get(radix);
context.report({
node,
messageId: "useLiteral",
data: {
system,
functionName: sourceCode.getText(node.callee)
},
fix(fixer) {
if (sourceCode.getCommentsInside(node).length) {
return null;
}
const replacement = `${literalPrefix}${str}`;
if (+replacement !== parseInt(str, radix)) {
/*
* If the newly-produced literal would be invalid, (e.g. 0b1234),
* or it would yield an incorrect parseInt result for some other reason, don't make a fix.
*/
return null;
}
const tokenBefore = sourceCode.getTokenBefore(node),
tokenAfter = sourceCode.getTokenAfter(node);
let prefix = "",
suffix = "";
if (
tokenBefore &&
tokenBefore.range[1] === node.range[0] &&
!astUtils.canTokensBeAdjacent(tokenBefore, replacement)
) {
prefix = " ";
}
if (
tokenAfter &&
node.range[1] === tokenAfter.range[0] &&
!astUtils.canTokensBeAdjacent(replacement, tokenAfter)
) {
suffix = " ";
}
return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
}
});
}
}
};
}
};