2 * @fileoverview Disallow reassignment of function parameters.
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const stopNodePattern
= /(?:Statement|Declaration|Function(?:Expression)?|Program)$/u;
18 description
: "disallow reassigning `function` parameters",
20 url
: "https://eslint.org/docs/rules/no-param-reassign"
33 additionalProperties
: false
41 ignorePropertyModificationsFor
: {
48 ignorePropertyModificationsForRegex
: {
56 additionalProperties
: false
63 assignmentToFunctionParam
: "Assignment to function parameter '{{name}}'.",
64 assignmentToFunctionParamProp
: "Assignment to property of function parameter '{{name}}'."
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
|| [];
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.
78 function isModifyingProp(reference
) {
79 let node
= reference
.identifier
;
80 let parent
= node
.parent
;
82 while (parent
&& (!stopNodePattern
.test(parent
.type
) ||
83 parent
.type
=== "ForInStatement" || parent
.type
=== "ForOfStatement")) {
84 switch (parent
.type
) {
87 case "AssignmentExpression":
88 return parent
.left
=== node
;
91 case "UpdateExpression":
95 case "UnaryExpression":
96 if (parent
.operator
=== "delete") {
101 // e.g. for (foo.a in b) {}
102 case "ForInStatement":
103 case "ForOfStatement":
104 if (parent
.left
=== node
) {
108 // this is a stop node for parent.right and parent.body
111 // EXCLUDES: e.g. cache.get(foo.a).b = 0;
112 case "CallExpression":
113 if (parent
.callee
!== node
) {
118 // EXCLUDES: e.g. cache[foo.a] = 0;
119 case "MemberExpression":
120 if (parent
.property
=== node
) {
125 // EXCLUDES: e.g. ({ [foo]: a }) = bar;
127 if (parent
.key
=== node
) {
133 // EXCLUDES: e.g. (foo ? a : b).c = bar;
134 case "ConditionalExpression":
135 if (parent
.test
=== node
) {
145 parent
= node
.parent
;
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.
159 function isIgnoredPropertyAssignment(identifierName
) {
160 return ignoredPropertyAssignmentsFor
.includes(identifierName
) ||
161 ignoredPropertyAssignmentsForRegex
.some(ignored
=> new RegExp(ignored
, "u").test(identifierName
));
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.
171 function checkReference(reference
, index
, references
) {
172 const identifier
= reference
.identifier
;
178 * Destructuring assignments can have multiple default value,
179 * so possibly there are multiple writeable references for the same identifier.
181 (index
=== 0 || references
[index
- 1].identifier
!== identifier
)
183 if (reference
.isWrite()) {
186 messageId
: "assignmentToFunctionParam",
187 data
: { name
: identifier
.name
}
189 } else if (props
&& isModifyingProp(reference
) && !isIgnoredPropertyAssignment(identifier
.name
)) {
192 messageId
: "assignmentToFunctionParamProp",
193 data
: { name
: identifier
.name
}
200 * Finds and reports references that are non initializer and writable.
201 * @param {Variable} variable A variable to check.
204 function checkVariable(variable
) {
205 if (variable
.defs
[0].type
=== "Parameter") {
206 variable
.references
.forEach(checkReference
);
211 * Checks parameters of a given function node.
212 * @param {ASTNode} node A function node to check.
215 function checkForFunction(node
) {
216 context
.getDeclaredVariables(node
).forEach(checkVariable
);
221 // `:exit` is needed for the `node.parent` property of identifier nodes.
222 "FunctionDeclaration:exit": checkForFunction
,
223 "FunctionExpression:exit": checkForFunction
,
224 "ArrowFunctionExpression:exit": checkForFunction