--- /dev/null
+/**
+ * @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) {
+ return (
+ astUtils.isSpecificId(calleeNode, "parseInt") ||
+ astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")
+ );
+}
+
+//------------------------------------------------------------------------------
+// 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 = astUtils.getStaticStringValue(strNode),
+ radix = radixNode.value;
+
+ if (
+ str !== null &&
+ astUtils.isStringLiteral(strNode) &&
+ radixNode.type === "Literal" &&
+ 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.
+ *
+ * If `str` had numeric separators, `+replacement` will evaluate to `NaN` because unary `+`
+ * per the specification doesn't support numeric separators. Thus, the above condition will be `true`
+ * (`NaN !== anything` is always `true`) regardless of the `parseInt(str, radix)` value.
+ * Consequently, no autofixes will be made. This is correct behavior because `parseInt` also
+ * doesn't support numeric separators, but it does parse part of the string before the first `_`,
+ * so the autofix would be invalid:
+ *
+ * parseInt("1_1", 2) // === 1
+ * 0b1_1 // === 3
+ */
+ 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}`);
+ }
+ });
+ }
+ }
+ };
+ }
+};