.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-cond-assign.js
1 /**
2  * @fileoverview Rule to flag assignment in a conditional statement's test expression
3  * @author Stephen Murray <spmurrayzzz>
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 TEST_CONDITION_PARENT_TYPES = new Set(["IfStatement", "WhileStatement", "DoWhileStatement", "ForStatement", "ConditionalExpression"]);
19
20 const NODE_DESCRIPTIONS = {
21     DoWhileStatement: "a 'do...while' statement",
22     ForStatement: "a 'for' statement",
23     IfStatement: "an 'if' statement",
24     WhileStatement: "a 'while' statement"
25 };
26
27 //------------------------------------------------------------------------------
28 // Rule Definition
29 //------------------------------------------------------------------------------
30
31 module.exports = {
32     meta: {
33         type: "problem",
34
35         docs: {
36             description: "disallow assignment operators in conditional expressions",
37             category: "Possible Errors",
38             recommended: true,
39             url: "https://eslint.org/docs/rules/no-cond-assign"
40         },
41
42         schema: [
43             {
44                 enum: ["except-parens", "always"]
45             }
46         ],
47
48         messages: {
49             unexpected: "Unexpected assignment within {{type}}.",
50
51             // must match JSHint's error message
52             missing: "Expected a conditional expression and instead saw an assignment."
53         }
54     },
55
56     create(context) {
57
58         const prohibitAssign = (context.options[0] || "except-parens");
59
60         const sourceCode = context.getSourceCode();
61
62         /**
63          * Check whether an AST node is the test expression for a conditional statement.
64          * @param {!Object} node The node to test.
65          * @returns {boolean} `true` if the node is the text expression for a conditional statement; otherwise, `false`.
66          */
67         function isConditionalTestExpression(node) {
68             return node.parent &&
69                 TEST_CONDITION_PARENT_TYPES.has(node.parent.type) &&
70                 node === node.parent.test;
71         }
72
73         /**
74          * Given an AST node, perform a bottom-up search for the first ancestor that represents a conditional statement.
75          * @param {!Object} node The node to use at the start of the search.
76          * @returns {?Object} The closest ancestor node that represents a conditional statement.
77          */
78         function findConditionalAncestor(node) {
79             let currentAncestor = node;
80
81             do {
82                 if (isConditionalTestExpression(currentAncestor)) {
83                     return currentAncestor.parent;
84                 }
85             } while ((currentAncestor = currentAncestor.parent) && !astUtils.isFunction(currentAncestor));
86
87             return null;
88         }
89
90         /**
91          * Check whether the code represented by an AST node is enclosed in two sets of parentheses.
92          * @param {!Object} node The node to test.
93          * @returns {boolean} `true` if the code is enclosed in two sets of parentheses; otherwise, `false`.
94          */
95         function isParenthesisedTwice(node) {
96             const previousToken = sourceCode.getTokenBefore(node, 1),
97                 nextToken = sourceCode.getTokenAfter(node, 1);
98
99             return astUtils.isParenthesised(sourceCode, node) &&
100                 previousToken && astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
101                 astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
102         }
103
104         /**
105          * Check a conditional statement's test expression for top-level assignments that are not enclosed in parentheses.
106          * @param {!Object} node The node for the conditional statement.
107          * @returns {void}
108          */
109         function testForAssign(node) {
110             if (node.test &&
111                 (node.test.type === "AssignmentExpression") &&
112                 (node.type === "ForStatement"
113                     ? !astUtils.isParenthesised(sourceCode, node.test)
114                     : !isParenthesisedTwice(node.test)
115                 )
116             ) {
117
118                 context.report({
119                     node: node.test,
120                     messageId: "missing"
121                 });
122             }
123         }
124
125         /**
126          * Check whether an assignment expression is descended from a conditional statement's test expression.
127          * @param {!Object} node The node for the assignment expression.
128          * @returns {void}
129          */
130         function testForConditionalAncestor(node) {
131             const ancestor = findConditionalAncestor(node);
132
133             if (ancestor) {
134                 context.report({
135                     node,
136                     messageId: "unexpected",
137                     data: {
138                         type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type
139                     }
140                 });
141             }
142         }
143
144         if (prohibitAssign === "always") {
145             return {
146                 AssignmentExpression: testForConditionalAncestor
147             };
148         }
149
150         return {
151             DoWhileStatement: testForAssign,
152             ForStatement: testForAssign,
153             IfStatement: testForAssign,
154             WhileStatement: testForAssign,
155             ConditionalExpression: testForAssign
156         };
157
158     }
159 };