/**
							 | 
						|
								 * @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
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								/** @type {import('../shared/types').Rule} */
							 | 
						|
								module.exports = {
							 | 
						|
								    meta: {
							 | 
						|
								        type: "suggestion",
							 | 
						|
								
							 | 
						|
								        docs: {
							 | 
						|
								            description: "Disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
							 | 
						|
								            recommended: false,
							 | 
						|
								            url: "https://eslint.org/docs/latest/rules/prefer-numeric-literals"
							 | 
						|
								        },
							 | 
						|
								
							 | 
						|
								        schema: [],
							 | 
						|
								
							 | 
						|
								        messages: {
							 | 
						|
								            useLiteral: "Use {{system}} literals instead of {{functionName}}()."
							 | 
						|
								        },
							 | 
						|
								
							 | 
						|
								        fixable: "code"
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    create(context) {
							 | 
						|
								        const sourceCode = context.sourceCode;
							 | 
						|
								
							 | 
						|
								        //----------------------------------------------------------------------
							 | 
						|
								        // 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}`);
							 | 
						|
								                        }
							 | 
						|
								                    });
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        };
							 | 
						|
								    }
							 | 
						|
								};
							 |