blob: 6bfa681afb46b9eeb094bb4a6a169334ded56b86 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
/**
* @fileoverview Disallow reassignment of function parameters.
* @author Nat Burns
* @copyright 2014 Nat Burns. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
var stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/;
module.exports = function(context) {
var props = context.options[0] && Boolean(context.options[0].props);
/**
* Checks whether or not a reference modifies its variable.
* If the `props` option is `true`, this checks whether or not the reference modifies properties of its variable also.
* @param {Reference} reference - A reference to check.
* @returns {boolean} Whether or not the reference modifies its variable.
*/
function isModifying(reference) {
if (reference.isWrite()) {
return true;
}
// Checks whether its property is modified.
if (props) {
var node = reference.identifier;
var parent = node.parent;
while (parent && !stopNodePattern.test(parent.type)) {
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;
// 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;
default:
break;
}
node = parent;
parent = parent.parent;
}
}
return false;
}
/**
* 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) {
var identifier = reference.identifier;
if (identifier &&
!reference.init &&
isModifying(reference) &&
// 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)
) {
context.report(
identifier,
"Assignment to function parameter '{{name}}'.",
{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) {
context.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
};
};
module.exports.schema = [
{
"type": "object",
"properties": {
"props": {"type": "boolean"}
},
"additionalProperties": false
}
];
|