--- /dev/null
+/**
+ * @fileoverview Rule to flag use of parseInt without a radix argument
+ * @author James Allardice
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const MODE_ALWAYS = "always",
+ MODE_AS_NEEDED = "as-needed";
+
+/**
+ * Checks whether a given variable is shadowed or not.
+ * @param {eslint-scope.Variable} variable A variable to check.
+ * @returns {boolean} `true` if the variable is shadowed.
+ */
+function isShadowed(variable) {
+ return variable.defs.length >= 1;
+}
+
+/**
+ * Checks whether a given node is a MemberExpression of `parseInt` method or not.
+ * @param {ASTNode} node A node to check.
+ * @returns {boolean} `true` if the node is a MemberExpression of `parseInt`
+ * method.
+ */
+function isParseIntMethod(node) {
+ return (
+ node.type === "MemberExpression" &&
+ !node.computed &&
+ node.property.type === "Identifier" &&
+ node.property.name === "parseInt"
+ );
+}
+
+/**
+ * Checks whether a given node is a valid value of radix or not.
+ *
+ * The following values are invalid.
+ *
+ * - A literal except numbers.
+ * - undefined.
+ * @param {ASTNode} radix A node of radix to check.
+ * @returns {boolean} `true` if the node is valid.
+ */
+function isValidRadix(radix) {
+ return !(
+ (radix.type === "Literal" && typeof radix.value !== "number") ||
+ (radix.type === "Identifier" && radix.name === "undefined")
+ );
+}
+
+/**
+ * Checks whether a given node is a default value of radix or not.
+ * @param {ASTNode} radix A node of radix to check.
+ * @returns {boolean} `true` if the node is the literal node of `10`.
+ */
+function isDefaultRadix(radix) {
+ return radix.type === "Literal" && radix.value === 10;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ type: "suggestion",
+
+ docs: {
+ description: "enforce the consistent use of the radix argument when using `parseInt()`",
+ category: "Best Practices",
+ recommended: false,
+ url: "https://eslint.org/docs/rules/radix"
+ },
+
+ schema: [
+ {
+ enum: ["always", "as-needed"]
+ }
+ ]
+ },
+
+ create(context) {
+ const mode = context.options[0] || MODE_ALWAYS;
+
+ /**
+ * Checks the arguments of a given CallExpression node and reports it if it
+ * offends this rule.
+ * @param {ASTNode} node A CallExpression node to check.
+ * @returns {void}
+ */
+ function checkArguments(node) {
+ const args = node.arguments;
+
+ switch (args.length) {
+ case 0:
+ context.report({
+ node,
+ message: "Missing parameters."
+ });
+ break;
+
+ case 1:
+ if (mode === MODE_ALWAYS) {
+ context.report({
+ node,
+ message: "Missing radix parameter."
+ });
+ }
+ break;
+
+ default:
+ if (mode === MODE_AS_NEEDED && isDefaultRadix(args[1])) {
+ context.report({
+ node,
+ message: "Redundant radix parameter."
+ });
+ } else if (!isValidRadix(args[1])) {
+ context.report({
+ node,
+ message: "Invalid radix parameter."
+ });
+ }
+ break;
+ }
+ }
+
+ return {
+ "Program:exit"() {
+ const scope = context.getScope();
+ let variable;
+
+ // Check `parseInt()`
+ variable = astUtils.getVariableByName(scope, "parseInt");
+ if (!isShadowed(variable)) {
+ variable.references.forEach(reference => {
+ const node = reference.identifier;
+
+ if (astUtils.isCallee(node)) {
+ checkArguments(node.parent);
+ }
+ });
+ }
+
+ // Check `Number.parseInt()`
+ variable = astUtils.getVariableByName(scope, "Number");
+ if (!isShadowed(variable)) {
+ variable.references.forEach(reference => {
+ const node = reference.identifier.parent;
+
+ if (isParseIntMethod(node) && astUtils.isCallee(node)) {
+ checkArguments(node.parent);
+ }
+ });
+ }
+ }
+ };
+ }
+};