/**
							 | 
						|
								 * @fileoverview Disallow reassignment of function parameters.
							 | 
						|
								 * @author Nat Burns
							 | 
						|
								 */
							 | 
						|
								"use strict";
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Rule Definition
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								const stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/u;
							 | 
						|
								
							 | 
						|
								/** @type {import('../shared/types').Rule} */
							 | 
						|
								module.exports = {
							 | 
						|
								    meta: {
							 | 
						|
								        type: "suggestion",
							 | 
						|
								
							 | 
						|
								        docs: {
							 | 
						|
								            description: "Disallow reassigning `function` parameters",
							 | 
						|
								            recommended: false,
							 | 
						|
								            url: "https://eslint.org/docs/latest/rules/no-param-reassign"
							 | 
						|
								        },
							 | 
						|
								
							 | 
						|
								        schema: [
							 | 
						|
								            {
							 | 
						|
								                oneOf: [
							 | 
						|
								                    {
							 | 
						|
								                        type: "object",
							 | 
						|
								                        properties: {
							 | 
						|
								                            props: {
							 | 
						|
								                                enum: [false]
							 | 
						|
								                            }
							 | 
						|
								                        },
							 | 
						|
								                        additionalProperties: false
							 | 
						|
								                    },
							 | 
						|
								                    {
							 | 
						|
								                        type: "object",
							 | 
						|
								                        properties: {
							 | 
						|
								                            props: {
							 | 
						|
								                                enum: [true]
							 | 
						|
								                            },
							 | 
						|
								                            ignorePropertyModificationsFor: {
							 | 
						|
								                                type: "array",
							 | 
						|
								                                items: {
							 | 
						|
								                                    type: "string"
							 | 
						|
								                                },
							 | 
						|
								                                uniqueItems: true
							 | 
						|
								                            },
							 | 
						|
								                            ignorePropertyModificationsForRegex: {
							 | 
						|
								                                type: "array",
							 | 
						|
								                                items: {
							 | 
						|
								                                    type: "string"
							 | 
						|
								                                },
							 | 
						|
								                                uniqueItems: true
							 | 
						|
								                            }
							 | 
						|
								                        },
							 | 
						|
								                        additionalProperties: false
							 | 
						|
								                    }
							 | 
						|
								                ]
							 | 
						|
								            }
							 | 
						|
								        ],
							 | 
						|
								
							 | 
						|
								        messages: {
							 | 
						|
								            assignmentToFunctionParam: "Assignment to function parameter '{{name}}'.",
							 | 
						|
								            assignmentToFunctionParamProp: "Assignment to property of function parameter '{{name}}'."
							 | 
						|
								        }
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    create(context) {
							 | 
						|
								        const props = context.options[0] && context.options[0].props;
							 | 
						|
								        const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || [];
							 | 
						|
								        const ignoredPropertyAssignmentsForRegex = context.options[0] && context.options[0].ignorePropertyModificationsForRegex || [];
							 | 
						|
								        const sourceCode = context.sourceCode;
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Checks whether or not the reference modifies properties of its variable.
							 | 
						|
								         * @param {Reference} reference A reference to check.
							 | 
						|
								         * @returns {boolean} Whether or not the reference modifies properties of its variable.
							 | 
						|
								         */
							 | 
						|
								        function isModifyingProp(reference) {
							 | 
						|
								            let node = reference.identifier;
							 | 
						|
								            let parent = node.parent;
							 | 
						|
								
							 | 
						|
								            while (parent && (!stopNodePattern.test(parent.type) ||
							 | 
						|
								                    parent.type === "ForInStatement" || parent.type === "ForOfStatement")) {
							 | 
						|
								                switch (parent.type) {
							 | 
						|
								
							 | 
						|
								                    // e.g. foo.a = 0;
							 | 
						|
								                    case "AssignmentExpression":
							 | 
						|
								                        return parent.left === node;
							 | 
						|
								
							 | 
						|
								                    // e.g. ++foo.a;
							 | 
						|
								                    case "UpdateExpression":
							 | 
						|
								                        return true;
							 | 
						|
								
							 | 
						|
								                    // e.g. delete foo.a;
							 | 
						|
								                    case "UnaryExpression":
							 | 
						|
								                        if (parent.operator === "delete") {
							 | 
						|
								                            return true;
							 | 
						|
								                        }
							 | 
						|
								                        break;
							 | 
						|
								
							 | 
						|
								                    // e.g. for (foo.a in b) {}
							 | 
						|
								                    case "ForInStatement":
							 | 
						|
								                    case "ForOfStatement":
							 | 
						|
								                        if (parent.left === node) {
							 | 
						|
								                            return true;
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        // this is a stop node for parent.right and parent.body
							 | 
						|
								                        return false;
							 | 
						|
								
							 | 
						|
								                    // EXCLUDES: e.g. cache.get(foo.a).b = 0;
							 | 
						|
								                    case "CallExpression":
							 | 
						|
								                        if (parent.callee !== node) {
							 | 
						|
								                            return false;
							 | 
						|
								                        }
							 | 
						|
								                        break;
							 | 
						|
								
							 | 
						|
								                    // EXCLUDES: e.g. cache[foo.a] = 0;
							 | 
						|
								                    case "MemberExpression":
							 | 
						|
								                        if (parent.property === node) {
							 | 
						|
								                            return false;
							 | 
						|
								                        }
							 | 
						|
								                        break;
							 | 
						|
								
							 | 
						|
								                    // EXCLUDES: e.g. ({ [foo]: a }) = bar;
							 | 
						|
								                    case "Property":
							 | 
						|
								                        if (parent.key === node) {
							 | 
						|
								                            return false;
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        break;
							 | 
						|
								
							 | 
						|
								                    // EXCLUDES: e.g. (foo ? a : b).c = bar;
							 | 
						|
								                    case "ConditionalExpression":
							 | 
						|
								                        if (parent.test === node) {
							 | 
						|
								                            return false;
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        break;
							 | 
						|
								
							 | 
						|
								                    // no default
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                node = parent;
							 | 
						|
								                parent = node.parent;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Tests that an identifier name matches any of the ignored property assignments.
							 | 
						|
								         * First we test strings in ignoredPropertyAssignmentsFor.
							 | 
						|
								         * Then we instantiate and test RegExp objects from ignoredPropertyAssignmentsForRegex strings.
							 | 
						|
								         * @param {string} identifierName A string that describes the name of an identifier to
							 | 
						|
								         * ignore property assignments for.
							 | 
						|
								         * @returns {boolean} Whether the string matches an ignored property assignment regular expression or not.
							 | 
						|
								         */
							 | 
						|
								        function isIgnoredPropertyAssignment(identifierName) {
							 | 
						|
								            return ignoredPropertyAssignmentsFor.includes(identifierName) ||
							 | 
						|
								                ignoredPropertyAssignmentsForRegex.some(ignored => new RegExp(ignored, "u").test(identifierName));
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Reports a reference if is non initializer and writable.
							 | 
						|
								         * @param {Reference} reference A reference to check.
							 | 
						|
								         * @param {int} index The index of the reference in the references.
							 | 
						|
								         * @param {Reference[]} references The array that the reference belongs to.
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function checkReference(reference, index, references) {
							 | 
						|
								            const identifier = reference.identifier;
							 | 
						|
								
							 | 
						|
								            if (identifier &&
							 | 
						|
								                !reference.init &&
							 | 
						|
								
							 | 
						|
								                /*
							 | 
						|
								                 * Destructuring assignments can have multiple default value,
							 | 
						|
								                 * so possibly there are multiple writeable references for the same identifier.
							 | 
						|
								                 */
							 | 
						|
								                (index === 0 || references[index - 1].identifier !== identifier)
							 | 
						|
								            ) {
							 | 
						|
								                if (reference.isWrite()) {
							 | 
						|
								                    context.report({
							 | 
						|
								                        node: identifier,
							 | 
						|
								                        messageId: "assignmentToFunctionParam",
							 | 
						|
								                        data: { name: identifier.name }
							 | 
						|
								                    });
							 | 
						|
								                } else if (props && isModifyingProp(reference) && !isIgnoredPropertyAssignment(identifier.name)) {
							 | 
						|
								                    context.report({
							 | 
						|
								                        node: identifier,
							 | 
						|
								                        messageId: "assignmentToFunctionParamProp",
							 | 
						|
								                        data: { name: identifier.name }
							 | 
						|
								                    });
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Finds and reports references that are non initializer and writable.
							 | 
						|
								         * @param {Variable} variable A variable to check.
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function checkVariable(variable) {
							 | 
						|
								            if (variable.defs[0].type === "Parameter") {
							 | 
						|
								                variable.references.forEach(checkReference);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Checks parameters of a given function node.
							 | 
						|
								         * @param {ASTNode} node A function node to check.
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function checkForFunction(node) {
							 | 
						|
								            sourceCode.getDeclaredVariables(node).forEach(checkVariable);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        return {
							 | 
						|
								
							 | 
						|
								            // `:exit` is needed for the `node.parent` property of identifier nodes.
							 | 
						|
								            "FunctionDeclaration:exit": checkForFunction,
							 | 
						|
								            "FunctionExpression:exit": checkForFunction,
							 | 
						|
								            "ArrowFunctionExpression:exit": checkForFunction
							 | 
						|
								        };
							 | 
						|
								
							 | 
						|
								    }
							 | 
						|
								};
							 |