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 | type: "suggestion",
|
42 |
|
43 | docs: {
|
44 | description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
|
45 | category: "ECMAScript 6",
|
46 | recommended: false,
|
47 | url: "https://eslint.org/docs/rules/prefer-numeric-literals"
|
48 | },
|
49 |
|
50 | schema: [],
|
51 | fixable: "code"
|
52 | },
|
53 |
|
54 | create(context) {
|
55 | const sourceCode = context.getSourceCode();
|
56 |
|
57 | const radixMap = {
|
58 | 2: "binary",
|
59 | 8: "octal",
|
60 | 16: "hexadecimal"
|
61 | };
|
62 |
|
63 | const prefixMap = {
|
64 | 2: "0b",
|
65 | 8: "0o",
|
66 | 16: "0x"
|
67 | };
|
68 |
|
69 | //----------------------------------------------------------------------
|
70 | // Public
|
71 | //----------------------------------------------------------------------
|
72 |
|
73 | return {
|
74 |
|
75 | CallExpression(node) {
|
76 |
|
77 | // doesn't check parseInt() if it doesn't have a radix argument
|
78 | if (node.arguments.length !== 2) {
|
79 | return;
|
80 | }
|
81 |
|
82 | // only error if the radix is 2, 8, or 16
|
83 | const radixName = radixMap[node.arguments[1].value];
|
84 |
|
85 | if (isParseInt(node.callee) &&
|
86 | radixName &&
|
87 | node.arguments[0].type === "Literal"
|
88 | ) {
|
89 | context.report({
|
90 | node,
|
91 | message: "Use {{radixName}} literals instead of {{functionName}}().",
|
92 | data: {
|
93 | radixName,
|
94 | functionName: sourceCode.getText(node.callee)
|
95 | },
|
96 | fix(fixer) {
|
97 | const newPrefix = prefixMap[node.arguments[1].value];
|
98 |
|
99 | if (+(newPrefix + node.arguments[0].value) !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
|
100 |
|
101 | /*
|
102 | * If the newly-produced literal would be invalid, (e.g. 0b1234),
|
103 | * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
|
104 | */
|
105 | return null;
|
106 | }
|
107 | return fixer.replaceText(node, prefixMap[node.arguments[1].value] + node.arguments[0].value);
|
108 | }
|
109 | });
|
110 | }
|
111 | }
|
112 | };
|
113 | }
|
114 | };
|