.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-param-reassign.js
1 /**
2  * @fileoverview Disallow reassignment of function parameters.
3  * @author Nat Burns
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Rule Definition
9 //------------------------------------------------------------------------------
10
11 const stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/u;
12
13 module.exports = {
14     meta: {
15         type: "suggestion",
16
17         docs: {
18             description: "disallow reassigning `function` parameters",
19             category: "Best Practices",
20             recommended: false,
21             url: "https://eslint.org/docs/rules/no-param-reassign"
22         },
23
24         schema: [
25             {
26                 oneOf: [
27                     {
28                         type: "object",
29                         properties: {
30                             props: {
31                                 enum: [false]
32                             }
33                         },
34                         additionalProperties: false
35                     },
36                     {
37                         type: "object",
38                         properties: {
39                             props: {
40                                 enum: [true]
41                             },
42                             ignorePropertyModificationsFor: {
43                                 type: "array",
44                                 items: {
45                                     type: "string"
46                                 },
47                                 uniqueItems: true
48                             },
49                             ignorePropertyModificationsForRegex: {
50                                 type: "array",
51                                 items: {
52                                     type: "string"
53                                 },
54                                 uniqueItems: true
55                             }
56                         },
57                         additionalProperties: false
58                     }
59                 ]
60             }
61         ],
62
63         messages: {
64             assignmentToFunctionParam: "Assignment to function parameter '{{name}}'.",
65             assignmentToFunctionParamProp: "Assignment to property of function parameter '{{name}}'."
66         }
67     },
68
69     create(context) {
70         const props = context.options[0] && context.options[0].props;
71         const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || [];
72         const ignoredPropertyAssignmentsForRegex = context.options[0] && context.options[0].ignorePropertyModificationsForRegex || [];
73
74         /**
75          * Checks whether or not the reference modifies properties of its variable.
76          * @param {Reference} reference A reference to check.
77          * @returns {boolean} Whether or not the reference modifies properties of its variable.
78          */
79         function isModifyingProp(reference) {
80             let node = reference.identifier;
81             let parent = node.parent;
82
83             while (parent && (!stopNodePattern.test(parent.type) ||
84                     parent.type === "ForInStatement" || parent.type === "ForOfStatement")) {
85                 switch (parent.type) {
86
87                     // e.g. foo.a = 0;
88                     case "AssignmentExpression":
89                         return parent.left === node;
90
91                     // e.g. ++foo.a;
92                     case "UpdateExpression":
93                         return true;
94
95                     // e.g. delete foo.a;
96                     case "UnaryExpression":
97                         if (parent.operator === "delete") {
98                             return true;
99                         }
100                         break;
101
102                     // e.g. for (foo.a in b) {}
103                     case "ForInStatement":
104                     case "ForOfStatement":
105                         if (parent.left === node) {
106                             return true;
107                         }
108
109                         // this is a stop node for parent.right and parent.body
110                         return false;
111
112                     // EXCLUDES: e.g. cache.get(foo.a).b = 0;
113                     case "CallExpression":
114                         if (parent.callee !== node) {
115                             return false;
116                         }
117                         break;
118
119                     // EXCLUDES: e.g. cache[foo.a] = 0;
120                     case "MemberExpression":
121                         if (parent.property === node) {
122                             return false;
123                         }
124                         break;
125
126                     // EXCLUDES: e.g. ({ [foo]: a }) = bar;
127                     case "Property":
128                         if (parent.key === node) {
129                             return false;
130                         }
131
132                         break;
133
134                     // EXCLUDES: e.g. (foo ? a : b).c = bar;
135                     case "ConditionalExpression":
136                         if (parent.test === node) {
137                             return false;
138                         }
139
140                         break;
141
142                     // no default
143                 }
144
145                 node = parent;
146                 parent = node.parent;
147             }
148
149             return false;
150         }
151
152         /**
153          * Tests that an identifier name matches any of the ignored property assignments.
154          * First we test strings in ignoredPropertyAssignmentsFor.
155          * Then we instantiate and test RegExp objects from ignoredPropertyAssignmentsForRegex strings.
156          * @param {string} identifierName A string that describes the name of an identifier to
157          * ignore property assignments for.
158          * @returns {boolean} Whether the string matches an ignored property assignment regular expression or not.
159          */
160         function isIgnoredPropertyAssignment(identifierName) {
161             return ignoredPropertyAssignmentsFor.includes(identifierName) ||
162                 ignoredPropertyAssignmentsForRegex.some(ignored => new RegExp(ignored, "u").test(identifierName));
163         }
164
165         /**
166          * Reports a reference if is non initializer and writable.
167          * @param {Reference} reference A reference to check.
168          * @param {int} index The index of the reference in the references.
169          * @param {Reference[]} references The array that the reference belongs to.
170          * @returns {void}
171          */
172         function checkReference(reference, index, references) {
173             const identifier = reference.identifier;
174
175             if (identifier &&
176                 !reference.init &&
177
178                 /*
179                  * Destructuring assignments can have multiple default value,
180                  * so possibly there are multiple writeable references for the same identifier.
181                  */
182                 (index === 0 || references[index - 1].identifier !== identifier)
183             ) {
184                 if (reference.isWrite()) {
185                     context.report({
186                         node: identifier,
187                         messageId: "assignmentToFunctionParam",
188                         data: { name: identifier.name }
189                     });
190                 } else if (props && isModifyingProp(reference) && !isIgnoredPropertyAssignment(identifier.name)) {
191                     context.report({
192                         node: identifier,
193                         messageId: "assignmentToFunctionParamProp",
194                         data: { name: identifier.name }
195                     });
196                 }
197             }
198         }
199
200         /**
201          * Finds and reports references that are non initializer and writable.
202          * @param {Variable} variable A variable to check.
203          * @returns {void}
204          */
205         function checkVariable(variable) {
206             if (variable.defs[0].type === "Parameter") {
207                 variable.references.forEach(checkReference);
208             }
209         }
210
211         /**
212          * Checks parameters of a given function node.
213          * @param {ASTNode} node A function node to check.
214          * @returns {void}
215          */
216         function checkForFunction(node) {
217             context.getDeclaredVariables(node).forEach(checkVariable);
218         }
219
220         return {
221
222             // `:exit` is needed for the `node.parent` property of identifier nodes.
223             "FunctionDeclaration:exit": checkForFunction,
224             "FunctionExpression:exit": checkForFunction,
225             "ArrowFunctionExpression:exit": checkForFunction
226         };
227
228     }
229 };