summaryrefslogtreecommitdiff
path: root/tools/eslint/lib/rules/prefer-numeric-literals.js
blob: 08deedd624dc83607cd0a62c1f40a894c7b2e607 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
 * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
 * @author Annie Zhang, Henry Zhu
 */

"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * 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: {
        docs: {
            description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
            category: "ECMAScript 6",
            recommended: false
        },

        schema: [],

        fixable: "code"
    },

    create(context) {
        const sourceCode = context.getSourceCode();

        const radixMap = {
            2: "binary",
            8: "octal",
            16: "hexadecimal"
        };

        const prefixMap = {
            2: "0b",
            8: "0o",
            16: "0x"
        };

        //----------------------------------------------------------------------
        // Public
        //----------------------------------------------------------------------

        return {

            CallExpression(node) {

                // doesn't check parseInt() if it doesn't have a radix argument
                if (node.arguments.length !== 2) {
                    return;
                }

                // only error if the radix is 2, 8, or 16
                const radixName = radixMap[node.arguments[1].value];

                if (isParseInt(node.callee) &&
                    radixName &&
                    node.arguments[0].type === "Literal"
                ) {
                    context.report({
                        node,
                        message: "Use {{radixName}} literals instead of {{functionName}}().",
                        data: {
                            radixName,
                            functionName: sourceCode.getText(node.callee)
                        },
                        fix(fixer) {
                            const newPrefix = prefixMap[node.arguments[1].value];

                            if (+(newPrefix + node.arguments[0].value) !== parseInt(node.arguments[0].value, node.arguments[1].value)) {

                                // 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;
                            }
                            return fixer.replaceText(node, prefixMap[node.arguments[1].value] + node.arguments[0].value);
                        }
                    });
                }
            }
        };
    }
};