Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-mixed-operators.js
1 /**
2  * @fileoverview Rule to disallow mixed binary operators.
3  * @author Toru Nagashima
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils.js");
13
14 //------------------------------------------------------------------------------
15 // Helpers
16 //------------------------------------------------------------------------------
17
18 const ARITHMETIC_OPERATORS = ["+", "-", "*", "/", "%", "**"];
19 const BITWISE_OPERATORS = ["&", "|", "^", "~", "<<", ">>", ">>>"];
20 const COMPARISON_OPERATORS = ["==", "!=", "===", "!==", ">", ">=", "<", "<="];
21 const LOGICAL_OPERATORS = ["&&", "||"];
22 const RELATIONAL_OPERATORS = ["in", "instanceof"];
23 const TERNARY_OPERATOR = ["?:"];
24 const ALL_OPERATORS = [].concat(
25     ARITHMETIC_OPERATORS,
26     BITWISE_OPERATORS,
27     COMPARISON_OPERATORS,
28     LOGICAL_OPERATORS,
29     RELATIONAL_OPERATORS,
30     TERNARY_OPERATOR
31 );
32 const DEFAULT_GROUPS = [
33     ARITHMETIC_OPERATORS,
34     BITWISE_OPERATORS,
35     COMPARISON_OPERATORS,
36     LOGICAL_OPERATORS,
37     RELATIONAL_OPERATORS
38 ];
39 const TARGET_NODE_TYPE = /^(?:Binary|Logical|Conditional)Expression$/u;
40
41 /**
42  * Normalizes options.
43  * @param {Object|undefined} options A options object to normalize.
44  * @returns {Object} Normalized option object.
45  */
46 function normalizeOptions(options = {}) {
47     const hasGroups = options.groups && options.groups.length > 0;
48     const groups = hasGroups ? options.groups : DEFAULT_GROUPS;
49     const allowSamePrecedence = options.allowSamePrecedence !== false;
50
51     return {
52         groups,
53         allowSamePrecedence
54     };
55 }
56
57 /**
58  * Checks whether any group which includes both given operator exists or not.
59  * @param {Array.<string[]>} groups A list of groups to check.
60  * @param {string} left An operator.
61  * @param {string} right Another operator.
62  * @returns {boolean} `true` if such group existed.
63  */
64 function includesBothInAGroup(groups, left, right) {
65     return groups.some(group => group.indexOf(left) !== -1 && group.indexOf(right) !== -1);
66 }
67
68 /**
69  * Checks whether the given node is a conditional expression and returns the test node else the left node.
70  * @param {ASTNode} node A node which can be a BinaryExpression or a LogicalExpression node.
71  * This parent node can be BinaryExpression, LogicalExpression
72  *      , or a ConditionalExpression node
73  * @returns {ASTNode} node the appropriate node(left or test).
74  */
75 function getChildNode(node) {
76     return node.type === "ConditionalExpression" ? node.test : node.left;
77 }
78
79 //------------------------------------------------------------------------------
80 // Rule Definition
81 //------------------------------------------------------------------------------
82
83 module.exports = {
84     meta: {
85         type: "suggestion",
86
87         docs: {
88             description: "disallow mixed binary operators",
89             category: "Stylistic Issues",
90             recommended: false,
91             url: "https://eslint.org/docs/rules/no-mixed-operators"
92         },
93
94         schema: [
95             {
96                 type: "object",
97                 properties: {
98                     groups: {
99                         type: "array",
100                         items: {
101                             type: "array",
102                             items: { enum: ALL_OPERATORS },
103                             minItems: 2,
104                             uniqueItems: true
105                         },
106                         uniqueItems: true
107                     },
108                     allowSamePrecedence: {
109                         type: "boolean",
110                         default: true
111                     }
112                 },
113                 additionalProperties: false
114             }
115         ]
116     },
117
118     create(context) {
119         const sourceCode = context.getSourceCode();
120         const options = normalizeOptions(context.options[0]);
121
122         /**
123          * Checks whether a given node should be ignored by options or not.
124          * @param {ASTNode} node A node to check. This is a BinaryExpression
125          *      node or a LogicalExpression node. This parent node is one of
126          *      them, too.
127          * @returns {boolean} `true` if the node should be ignored.
128          */
129         function shouldIgnore(node) {
130             const a = node;
131             const b = node.parent;
132
133             return (
134                 !includesBothInAGroup(options.groups, a.operator, b.type === "ConditionalExpression" ? "?:" : b.operator) ||
135                 (
136                     options.allowSamePrecedence &&
137                     astUtils.getPrecedence(a) === astUtils.getPrecedence(b)
138                 )
139             );
140         }
141
142         /**
143          * Checks whether the operator of a given node is mixed with parent
144          * node's operator or not.
145          * @param {ASTNode} node A node to check. This is a BinaryExpression
146          *      node or a LogicalExpression node. This parent node is one of
147          *      them, too.
148          * @returns {boolean} `true` if the node was mixed.
149          */
150         function isMixedWithParent(node) {
151
152             return (
153                 node.operator !== node.parent.operator &&
154                 !astUtils.isParenthesised(sourceCode, node)
155             );
156         }
157
158         /**
159          * Checks whether the operator of a given node is mixed with a
160          * conditional expression.
161          * @param {ASTNode} node A node to check. This is a conditional
162          *      expression node
163          * @returns {boolean} `true` if the node was mixed.
164          */
165         function isMixedWithConditionalParent(node) {
166             return !astUtils.isParenthesised(sourceCode, node) && !astUtils.isParenthesised(sourceCode, node.test);
167         }
168
169         /**
170          * Gets the operator token of a given node.
171          * @param {ASTNode} node A node to check. This is a BinaryExpression
172          *      node or a LogicalExpression node.
173          * @returns {Token} The operator token of the node.
174          */
175         function getOperatorToken(node) {
176             return sourceCode.getTokenAfter(getChildNode(node), astUtils.isNotClosingParenToken);
177         }
178
179         /**
180          * Reports both the operator of a given node and the operator of the
181          * parent node.
182          * @param {ASTNode} node A node to check. This is a BinaryExpression
183          *      node or a LogicalExpression node. This parent node is one of
184          *      them, too.
185          * @returns {void}
186          */
187         function reportBothOperators(node) {
188             const parent = node.parent;
189             const left = (getChildNode(parent) === node) ? node : parent;
190             const right = (getChildNode(parent) !== node) ? node : parent;
191             const message =
192                 "Unexpected mix of '{{leftOperator}}' and '{{rightOperator}}'.";
193             const data = {
194                 leftOperator: left.operator || "?:",
195                 rightOperator: right.operator || "?:"
196             };
197
198             context.report({
199                 node: left,
200                 loc: getOperatorToken(left).loc,
201                 message,
202                 data
203             });
204             context.report({
205                 node: right,
206                 loc: getOperatorToken(right).loc,
207                 message,
208                 data
209             });
210         }
211
212         /**
213          * Checks between the operator of this node and the operator of the
214          * parent node.
215          * @param {ASTNode} node A node to check.
216          * @returns {void}
217          */
218         function check(node) {
219             if (TARGET_NODE_TYPE.test(node.parent.type)) {
220                 if (node.parent.type === "ConditionalExpression" && !shouldIgnore(node) && isMixedWithConditionalParent(node.parent)) {
221                     reportBothOperators(node);
222                 } else {
223                     if (TARGET_NODE_TYPE.test(node.parent.type) &&
224                         isMixedWithParent(node) &&
225                         !shouldIgnore(node)
226                     ) {
227                         reportBothOperators(node);
228                     }
229                 }
230             }
231
232         }
233
234         return {
235             BinaryExpression: check,
236             LogicalExpression: check
237
238         };
239     }
240 };