2 * @fileoverview Disallow reassignment of function parameters.
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const stopNodePattern
= /(?:Statement|Declaration|Function(?:Expression)?|Program)$/u;
18 description
: "disallow reassigning `function` parameters",
19 category
: "Best Practices",
21 url
: "https://eslint.org/docs/rules/no-param-reassign"
34 additionalProperties
: false
42 ignorePropertyModificationsFor
: {
49 ignorePropertyModificationsForRegex
: {
57 additionalProperties
: false
64 assignmentToFunctionParam
: "Assignment to function parameter '{{name}}'.",
65 assignmentToFunctionParamProp
: "Assignment to property of function parameter '{{name}}'."
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
|| [];
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.
79 function isModifyingProp(reference
) {
80 let node
= reference
.identifier
;
81 let parent
= node
.parent
;
83 while (parent
&& (!stopNodePattern
.test(parent
.type
) ||
84 parent
.type
=== "ForInStatement" || parent
.type
=== "ForOfStatement")) {
85 switch (parent
.type
) {
88 case "AssignmentExpression":
89 return parent
.left
=== node
;
92 case "UpdateExpression":
96 case "UnaryExpression":
97 if (parent
.operator
=== "delete") {
102 // e.g. for (foo.a in b) {}
103 case "ForInStatement":
104 case "ForOfStatement":
105 if (parent
.left
=== node
) {
109 // this is a stop node for parent.right and parent.body
112 // EXCLUDES: e.g. cache.get(foo.a).b = 0;
113 case "CallExpression":
114 if (parent
.callee
!== node
) {
119 // EXCLUDES: e.g. cache[foo.a] = 0;
120 case "MemberExpression":
121 if (parent
.property
=== node
) {
126 // EXCLUDES: e.g. ({ [foo]: a }) = bar;
128 if (parent
.key
=== node
) {
134 // EXCLUDES: e.g. (foo ? a : b).c = bar;
135 case "ConditionalExpression":
136 if (parent
.test
=== node
) {
146 parent
= node
.parent
;
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.
160 function isIgnoredPropertyAssignment(identifierName
) {
161 return ignoredPropertyAssignmentsFor
.includes(identifierName
) ||
162 ignoredPropertyAssignmentsForRegex
.some(ignored
=> new RegExp(ignored
, "u").test(identifierName
));
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.
172 function checkReference(reference
, index
, references
) {
173 const identifier
= reference
.identifier
;
179 * Destructuring assignments can have multiple default value,
180 * so possibly there are multiple writeable references for the same identifier.
182 (index
=== 0 || references
[index
- 1].identifier
!== identifier
)
184 if (reference
.isWrite()) {
187 messageId
: "assignmentToFunctionParam",
188 data
: { name
: identifier
.name
}
190 } else if (props
&& isModifyingProp(reference
) && !isIgnoredPropertyAssignment(identifier
.name
)) {
193 messageId
: "assignmentToFunctionParamProp",
194 data
: { name
: identifier
.name
}
201 * Finds and reports references that are non initializer and writable.
202 * @param {Variable} variable A variable to check.
205 function checkVariable(variable
) {
206 if (variable
.defs
[0].type
=== "Parameter") {
207 variable
.references
.forEach(checkReference
);
212 * Checks parameters of a given function node.
213 * @param {ASTNode} node A function node to check.
216 function checkForFunction(node
) {
217 context
.getDeclaredVariables(node
).forEach(checkVariable
);
222 // `:exit` is needed for the `node.parent` property of identifier nodes.
223 "FunctionDeclaration:exit": checkForFunction
,
224 "FunctionExpression:exit": checkForFunction
,
225 "ArrowFunctionExpression:exit": checkForFunction