1 | /**
|
2 | * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
|
3 | * @author Annie Zhang, Henry Zhu
|
4 | */
|
5 |
|
6 | ;
|
7 |
|
8 | //------------------------------------------------------------------------------
|
9 | // Helpers
|
10 | //------------------------------------------------------------------------------
|
11 |
|
12 | /**
|
13 | * Checks to see if a CallExpression's callee node is `parseInt` or
|
14 | * `Number.parseInt`.
|
15 | * @param {ASTNode} calleeNode The callee node to evaluate.
|
16 | * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
|
17 | * false otherwise.
|
18 | */
|
19 | function isParseInt(calleeNode) {
|
20 | switch (calleeNode.type) {
|
21 | case "Identifier":
|
22 | return calleeNode.name === "parseInt";
|
23 | case "MemberExpression":
|
24 | return calleeNode.object.type === "Identifier" &&
|
25 | calleeNode.object.name === "Number" &&
|
26 | calleeNode.property.type === "Identifier" &&
|
27 | calleeNode.property.name === "parseInt";
|
28 |
|
29 | // no default
|
30 | }
|
31 |
|
32 | return false;
|
33 | }
|
34 |
|
35 | //------------------------------------------------------------------------------
|
36 | // Rule Definition
|
37 | //------------------------------------------------------------------------------
|
38 |
|
39 | module.exports = {
|
40 | meta: {
|
41 | docs: {
|
42 | description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
|
43 | category: "ECMAScript 6",
|
44 | recommended: false,
|
45 | url: "https://eslint.org/docs/rules/prefer-numeric-literals"
|
46 | },
|
47 |
|
48 | schema: [],
|
49 |
|
50 | fixable: "code"
|
51 | },
|
52 |
|
53 | create(context) {
|
54 | const sourceCode = context.getSourceCode();
|
55 |
|
56 | const radixMap = {
|
57 | 2: "binary",
|
58 | 8: "octal",
|
59 | 16: "hexadecimal"
|
60 | };
|
61 |
|
62 | const prefixMap = {
|
63 | 2: "0b",
|
64 | 8: "0o",
|
65 | 16: "0x"
|
66 | };
|
67 |
|
68 | //----------------------------------------------------------------------
|
69 | // Public
|
70 | //----------------------------------------------------------------------
|
71 |
|
72 | return {
|
73 |
|
74 | CallExpression(node) {
|
75 |
|
76 | // doesn't check parseInt() if it doesn't have a radix argument
|
77 | if (node.arguments.length !== 2) {
|
78 | return;
|
79 | }
|
80 |
|
81 | // only error if the radix is 2, 8, or 16
|
82 | const radixName = radixMap[node.arguments[1].value];
|
83 |
|
84 | if (isParseInt(node.callee) &&
|
85 | radixName &&
|
86 | node.arguments[0].type === "Literal"
|
87 | ) {
|
88 | context.report({
|
89 | node,
|
90 | message: "Use {{radixName}} literals instead of {{functionName}}().",
|
91 | data: {
|
92 | radixName,
|
93 | functionName: sourceCode.getText(node.callee)
|
94 | },
|
95 | fix(fixer) {
|
96 | const newPrefix = prefixMap[node.arguments[1].value];
|
97 |
|
98 | if (+(newPrefix + node.arguments[0].value) !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
|
99 |
|
100 | /*
|
101 | * If the newly-produced literal would be invalid, (e.g. 0b1234),
|
102 | * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
|
103 | */
|
104 | return null;
|
105 | }
|
106 | return fixer.replaceText(node, prefixMap[node.arguments[1].value] + node.arguments[0].value);
|
107 | }
|
108 | });
|
109 | }
|
110 | }
|
111 | };
|
112 | }
|
113 | };
|