.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / prefer-numeric-literals.js
1 /**
2  * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
3  * @author Annie Zhang, Henry Zhu
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Helpers
16 //------------------------------------------------------------------------------
17
18 const radixMap = new Map([
19     [2, { system: "binary", literalPrefix: "0b" }],
20     [8, { system: "octal", literalPrefix: "0o" }],
21     [16, { system: "hexadecimal", literalPrefix: "0x" }]
22 ]);
23
24 /**
25  * Checks to see if a CallExpression's callee node is `parseInt` or
26  * `Number.parseInt`.
27  * @param {ASTNode} calleeNode The callee node to evaluate.
28  * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
29  * false otherwise.
30  */
31 function isParseInt(calleeNode) {
32     return (
33         astUtils.isSpecificId(calleeNode, "parseInt") ||
34         astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")
35     );
36 }
37
38 //------------------------------------------------------------------------------
39 // Rule Definition
40 //------------------------------------------------------------------------------
41
42 module.exports = {
43     meta: {
44         type: "suggestion",
45
46         docs: {
47             description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
48             category: "ECMAScript 6",
49             recommended: false,
50             url: "https://eslint.org/docs/rules/prefer-numeric-literals"
51         },
52
53         schema: [],
54
55         messages: {
56             useLiteral: "Use {{system}} literals instead of {{functionName}}()."
57         },
58
59         fixable: "code"
60     },
61
62     create(context) {
63         const sourceCode = context.getSourceCode();
64
65         //----------------------------------------------------------------------
66         // Public
67         //----------------------------------------------------------------------
68
69         return {
70
71             "CallExpression[arguments.length=2]"(node) {
72                 const [strNode, radixNode] = node.arguments,
73                     str = astUtils.getStaticStringValue(strNode),
74                     radix = radixNode.value;
75
76                 if (
77                     str !== null &&
78                     astUtils.isStringLiteral(strNode) &&
79                     radixNode.type === "Literal" &&
80                     typeof radix === "number" &&
81                     radixMap.has(radix) &&
82                     isParseInt(node.callee)
83                 ) {
84
85                     const { system, literalPrefix } = radixMap.get(radix);
86
87                     context.report({
88                         node,
89                         messageId: "useLiteral",
90                         data: {
91                             system,
92                             functionName: sourceCode.getText(node.callee)
93                         },
94                         fix(fixer) {
95                             if (sourceCode.getCommentsInside(node).length) {
96                                 return null;
97                             }
98
99                             const replacement = `${literalPrefix}${str}`;
100
101                             if (+replacement !== parseInt(str, radix)) {
102
103                                 /*
104                                  * If the newly-produced literal would be invalid, (e.g. 0b1234),
105                                  * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
106                                  *
107                                  * If `str` had numeric separators, `+replacement` will evaluate to `NaN` because unary `+`
108                                  * per the specification doesn't support numeric separators. Thus, the above condition will be `true`
109                                  * (`NaN !== anything` is always `true`) regardless of the `parseInt(str, radix)` value.
110                                  * Consequently, no autofixes will be made. This is correct behavior because `parseInt` also
111                                  * doesn't support numeric separators, but it does parse part of the string before the first `_`,
112                                  * so the autofix would be invalid:
113                                  *
114                                  *   parseInt("1_1", 2) // === 1
115                                  *   0b1_1 // === 3
116                                  */
117                                 return null;
118                             }
119
120                             const tokenBefore = sourceCode.getTokenBefore(node),
121                                 tokenAfter = sourceCode.getTokenAfter(node);
122                             let prefix = "",
123                                 suffix = "";
124
125                             if (
126                                 tokenBefore &&
127                                 tokenBefore.range[1] === node.range[0] &&
128                                 !astUtils.canTokensBeAdjacent(tokenBefore, replacement)
129                             ) {
130                                 prefix = " ";
131                             }
132
133                             if (
134                                 tokenAfter &&
135                                 node.range[1] === tokenAfter.range[0] &&
136                                 !astUtils.canTokensBeAdjacent(replacement, tokenAfter)
137                             ) {
138                                 suffix = " ";
139                             }
140
141                             return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
142                         }
143                     });
144                 }
145             }
146         };
147     }
148 };