Permalink
Cannot retrieve contributors at this time
230 lines (198 sloc)
8.42 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/eslint/lib/rules/no-param-reassign.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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 | |
}; | |
} | |
}; |