.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-unused-expressions.js
1 /**
2  * @fileoverview Flag expressions in statement position that do not side effect
3  * @author Michael Ficarra
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Rule Definition
9 //------------------------------------------------------------------------------
10
11 /**
12  * Returns `true`.
13  * @returns {boolean} `true`.
14  */
15 function alwaysTrue() {
16     return true;
17 }
18
19 /**
20  * Returns `false`.
21  * @returns {boolean} `false`.
22  */
23 function alwaysFalse() {
24     return false;
25 }
26
27 module.exports = {
28     meta: {
29         type: "suggestion",
30
31         docs: {
32             description: "disallow unused expressions",
33             category: "Best Practices",
34             recommended: false,
35             url: "https://eslint.org/docs/rules/no-unused-expressions"
36         },
37
38         schema: [
39             {
40                 type: "object",
41                 properties: {
42                     allowShortCircuit: {
43                         type: "boolean",
44                         default: false
45                     },
46                     allowTernary: {
47                         type: "boolean",
48                         default: false
49                     },
50                     allowTaggedTemplates: {
51                         type: "boolean",
52                         default: false
53                     },
54                     enforceForJSX: {
55                         type: "boolean",
56                         default: false
57                     }
58                 },
59                 additionalProperties: false
60             }
61         ],
62
63         messages: {
64             unusedExpression: "Expected an assignment or function call and instead saw an expression."
65         }
66     },
67
68     create(context) {
69         const config = context.options[0] || {},
70             allowShortCircuit = config.allowShortCircuit || false,
71             allowTernary = config.allowTernary || false,
72             allowTaggedTemplates = config.allowTaggedTemplates || false,
73             enforceForJSX = config.enforceForJSX || false;
74
75         // eslint-disable-next-line jsdoc/require-description
76         /**
77          * @param {ASTNode} node any node
78          * @returns {boolean} whether the given node structurally represents a directive
79          */
80         function looksLikeDirective(node) {
81             return node.type === "ExpressionStatement" &&
82                 node.expression.type === "Literal" && typeof node.expression.value === "string";
83         }
84
85         // eslint-disable-next-line jsdoc/require-description
86         /**
87          * @param {Function} predicate ([a] -> Boolean) the function used to make the determination
88          * @param {a[]} list the input list
89          * @returns {a[]} the leading sequence of members in the given list that pass the given predicate
90          */
91         function takeWhile(predicate, list) {
92             for (let i = 0; i < list.length; ++i) {
93                 if (!predicate(list[i])) {
94                     return list.slice(0, i);
95                 }
96             }
97             return list.slice();
98         }
99
100         // eslint-disable-next-line jsdoc/require-description
101         /**
102          * @param {ASTNode} node a Program or BlockStatement node
103          * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body
104          */
105         function directives(node) {
106             return takeWhile(looksLikeDirective, node.body);
107         }
108
109         // eslint-disable-next-line jsdoc/require-description
110         /**
111          * @param {ASTNode} node any node
112          * @param {ASTNode[]} ancestors the given node's ancestors
113          * @returns {boolean} whether the given node is considered a directive in its current position
114          */
115         function isDirective(node, ancestors) {
116             const parent = ancestors[ancestors.length - 1],
117                 grandparent = ancestors[ancestors.length - 2];
118
119             return (parent.type === "Program" || parent.type === "BlockStatement" &&
120                     (/Function/u.test(grandparent.type))) &&
121                     directives(parent).indexOf(node) >= 0;
122         }
123
124         /**
125          * The member functions return `true` if the type has no side-effects.
126          * Unknown nodes are handled as `false`, then this rule ignores those.
127          */
128         const Checker = Object.assign(Object.create(null), {
129             isDisallowed(node) {
130                 return (Checker[node.type] || alwaysFalse)(node);
131             },
132
133             ArrayExpression: alwaysTrue,
134             ArrowFunctionExpression: alwaysTrue,
135             BinaryExpression: alwaysTrue,
136             ChainExpression(node) {
137                 return Checker.isDisallowed(node.expression);
138             },
139             ClassExpression: alwaysTrue,
140             ConditionalExpression(node) {
141                 if (allowTernary) {
142                     return Checker.isDisallowed(node.consequent) || Checker.isDisallowed(node.alternate);
143                 }
144                 return true;
145             },
146             FunctionExpression: alwaysTrue,
147             Identifier: alwaysTrue,
148             JSXElement() {
149                 return enforceForJSX;
150             },
151             JSXFragment() {
152                 return enforceForJSX;
153             },
154             Literal: alwaysTrue,
155             LogicalExpression(node) {
156                 if (allowShortCircuit) {
157                     return Checker.isDisallowed(node.right);
158                 }
159                 return true;
160             },
161             MemberExpression: alwaysTrue,
162             MetaProperty: alwaysTrue,
163             ObjectExpression: alwaysTrue,
164             SequenceExpression: alwaysTrue,
165             TaggedTemplateExpression() {
166                 return !allowTaggedTemplates;
167             },
168             TemplateLiteral: alwaysTrue,
169             ThisExpression: alwaysTrue,
170             UnaryExpression(node) {
171                 return node.operator !== "void" && node.operator !== "delete";
172             }
173         });
174
175         return {
176             ExpressionStatement(node) {
177                 if (Checker.isDisallowed(node.expression) && !isDirective(node, context.getAncestors())) {
178                     context.report({ node, messageId: "unusedExpression" });
179                 }
180             }
181         };
182     }
183 };