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