.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-unsafe-optional-chaining.js
1 /**
2  * @fileoverview Rule to disallow unsafe optional chaining
3  * @author Yeon JuAn
4  */
5
6 "use strict";
7
8 const UNSAFE_ARITHMETIC_OPERATORS = new Set(["+", "-", "/", "*", "%", "**"]);
9 const UNSAFE_ASSIGNMENT_OPERATORS = new Set(["+=", "-=", "/=", "*=", "%=", "**="]);
10 const UNSAFE_RELATIONAL_OPERATORS = new Set(["in", "instanceof"]);
11
12 /**
13  * Checks whether a node is a destructuring pattern or not
14  * @param {ASTNode} node node to check
15  * @returns {boolean} `true` if a node is a destructuring pattern, otherwise `false`
16  */
17 function isDestructuringPattern(node) {
18     return node.type === "ObjectPattern" || node.type === "ArrayPattern";
19 }
20
21 module.exports = {
22     meta: {
23         type: "problem",
24
25         docs: {
26             description: "disallow use of optional chaining in contexts where the `undefined` value is not allowed",
27             category: "Possible Errors",
28             recommended: false,
29             url: "https://eslint.org/docs/rules/no-unsafe-optional-chaining"
30         },
31         schema: [{
32             type: "object",
33             properties: {
34                 disallowArithmeticOperators: {
35                     type: "boolean",
36                     default: false
37                 }
38             },
39             additionalProperties: false
40         }],
41         fixable: null,
42         messages: {
43             unsafeOptionalChain: "Unsafe usage of optional chaining. If it short-circuits with 'undefined' the evaluation will throw TypeError.",
44             unsafeArithmetic: "Unsafe arithmetic operation on optional chaining. It can result in NaN."
45         }
46     },
47
48     create(context) {
49         const options = context.options[0] || {};
50         const disallowArithmeticOperators = (options.disallowArithmeticOperators) || false;
51
52         /**
53          * Reports unsafe usage of optional chaining
54          * @param {ASTNode} node node to report
55          * @returns {void}
56          */
57         function reportUnsafeUsage(node) {
58             context.report({
59                 messageId: "unsafeOptionalChain",
60                 node
61             });
62         }
63
64         /**
65          * Reports unsafe arithmetic operation on optional chaining
66          * @param {ASTNode} node node to report
67          * @returns {void}
68          */
69         function reportUnsafeArithmetic(node) {
70             context.report({
71                 messageId: "unsafeArithmetic",
72                 node
73             });
74         }
75
76         /**
77          * Checks and reports if a node can short-circuit with `undefined` by optional chaining.
78          * @param {ASTNode} [node] node to check
79          * @param {Function} reportFunc report function
80          * @returns {void}
81          */
82         function checkUndefinedShortCircuit(node, reportFunc) {
83             if (!node) {
84                 return;
85             }
86             switch (node.type) {
87                 case "LogicalExpression":
88                     if (node.operator === "||" || node.operator === "??") {
89                         checkUndefinedShortCircuit(node.right, reportFunc);
90                     } else if (node.operator === "&&") {
91                         checkUndefinedShortCircuit(node.left, reportFunc);
92                         checkUndefinedShortCircuit(node.right, reportFunc);
93                     }
94                     break;
95                 case "SequenceExpression":
96                     checkUndefinedShortCircuit(
97                         node.expressions[node.expressions.length - 1],
98                         reportFunc
99                     );
100                     break;
101                 case "ConditionalExpression":
102                     checkUndefinedShortCircuit(node.consequent, reportFunc);
103                     checkUndefinedShortCircuit(node.alternate, reportFunc);
104                     break;
105                 case "AwaitExpression":
106                     checkUndefinedShortCircuit(node.argument, reportFunc);
107                     break;
108                 case "ChainExpression":
109                     reportFunc(node);
110                     break;
111                 default:
112                     break;
113             }
114         }
115
116         /**
117          * Checks unsafe usage of optional chaining
118          * @param {ASTNode} node node to check
119          * @returns {void}
120          */
121         function checkUnsafeUsage(node) {
122             checkUndefinedShortCircuit(node, reportUnsafeUsage);
123         }
124
125         /**
126          * Checks unsafe arithmetic operations on optional chaining
127          * @param {ASTNode} node node to check
128          * @returns {void}
129          */
130         function checkUnsafeArithmetic(node) {
131             checkUndefinedShortCircuit(node, reportUnsafeArithmetic);
132         }
133
134         return {
135             "AssignmentExpression, AssignmentPattern"(node) {
136                 if (isDestructuringPattern(node.left)) {
137                     checkUnsafeUsage(node.right);
138                 }
139             },
140             "ClassDeclaration, ClassExpression"(node) {
141                 checkUnsafeUsage(node.superClass);
142             },
143             CallExpression(node) {
144                 if (!node.optional) {
145                     checkUnsafeUsage(node.callee);
146                 }
147             },
148             NewExpression(node) {
149                 checkUnsafeUsage(node.callee);
150             },
151             VariableDeclarator(node) {
152                 if (isDestructuringPattern(node.id)) {
153                     checkUnsafeUsage(node.init);
154                 }
155             },
156             MemberExpression(node) {
157                 if (!node.optional) {
158                     checkUnsafeUsage(node.object);
159                 }
160             },
161             TaggedTemplateExpression(node) {
162                 checkUnsafeUsage(node.tag);
163             },
164             ForOfStatement(node) {
165                 checkUnsafeUsage(node.right);
166             },
167             SpreadElement(node) {
168                 if (node.parent && node.parent.type !== "ObjectExpression") {
169                     checkUnsafeUsage(node.argument);
170                 }
171             },
172             BinaryExpression(node) {
173                 if (UNSAFE_RELATIONAL_OPERATORS.has(node.operator)) {
174                     checkUnsafeUsage(node.right);
175                 }
176                 if (
177                     disallowArithmeticOperators &&
178                     UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
179                 ) {
180                     checkUnsafeArithmetic(node.right);
181                     checkUnsafeArithmetic(node.left);
182                 }
183             },
184             WithStatement(node) {
185                 checkUnsafeUsage(node.object);
186             },
187             UnaryExpression(node) {
188                 if (
189                     disallowArithmeticOperators &&
190                     UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
191                 ) {
192                     checkUnsafeArithmetic(node.argument);
193                 }
194             },
195             AssignmentExpression(node) {
196                 if (
197                     disallowArithmeticOperators &&
198                     UNSAFE_ASSIGNMENT_OPERATORS.has(node.operator)
199                 ) {
200                     checkUnsafeArithmetic(node.right);
201                 }
202             }
203         };
204     }
205 };