Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update: Add for-in and for-of checks for props in no-param-reassign #11941

Merged
merged 1 commit into from Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/rules/no-param-reassign.md
Expand Up @@ -20,6 +20,14 @@ function foo(bar) {
function foo(bar) {
bar++;
}

function foo(bar) {
for (bar in baz) {}
}

function foo(bar) {
for (bar of baz) {}
}
```

Examples of **correct** code for this rule:
Expand Down Expand Up @@ -54,6 +62,14 @@ function foo(bar) {
function foo(bar) {
bar.aaa++;
}

function foo(bar) {
for (bar.aaa in baz) {}
}

function foo(bar) {
for (bar.aaa of baz) {}
}
```

Examples of **incorrect** code for the `{ "props": true }` option:
Expand All @@ -72,6 +88,14 @@ function foo(bar) {
function foo(bar) {
bar.aaa++;
}

function foo(bar) {
for (bar.aaa in baz) {}
}

function foo(bar) {
for (bar.aaa of baz) {}
}
```

Examples of **correct** code for the `{ "props": true }` option with `"ignorePropertyModificationsFor"` set:
Expand All @@ -90,6 +114,14 @@ function foo(bar) {
function foo(bar) {
bar.aaa++;
}

function foo(bar) {
for (bar.aaa in baz) {}
}

function foo(bar) {
for (bar.aaa of baz) {}
}
```


Expand Down
13 changes: 12 additions & 1 deletion lib/rules/no-param-reassign.js
Expand Up @@ -67,7 +67,8 @@ module.exports = {
let node = reference.identifier;
let parent = node.parent;

while (parent && !stopNodePattern.test(parent.type)) {
while (parent && (!stopNodePattern.test(parent.type) ||
parent.type === "ForInStatement" || parent.type === "ForOfStatement")) {
switch (parent.type) {

// e.g. foo.a = 0;
Expand All @@ -85,6 +86,16 @@ module.exports = {
}
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) {
Expand Down
58 changes: 58 additions & 0 deletions tests/lib/rules/no-param-reassign.js
Expand Up @@ -21,7 +21,11 @@ ruleTester.run("no-param-reassign", rule, {

valid: [
"function foo(a) { var b = a; }",
"function foo(a) { for (b in a); }",
{ code: "function foo(a) { for (b of a); }", parserOptions: { ecmaVersion: 6 } },
"function foo(a) { a.prop = 'value'; }",
"function foo(a) { for (a.prop in obj); }",
{ code: "function foo(a) { for (a.prop of arr); }", parserOptions: { ecmaVersion: 6 } },
"function foo(a) { (function() { var a = 12; a++; })(); }",
"function foo() { someGlobal = 13; }",
{ code: "function foo() { someGlobal = 13; }", globals: { someGlobal: false } },
Expand All @@ -37,6 +41,8 @@ ruleTester.run("no-param-reassign", rule, {
{ code: "function foo(a) { a.b = 0; }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
{ code: "function foo(a) { ++a.b; }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
{ code: "function foo(a) { delete a.b; }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
{ code: "function foo(a) { for (a.b in obj); }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
{ code: "function foo(a) { for (a.b of arr); }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }], parserOptions: { ecmaVersion: 6 } },
{ code: "function foo(a, z) { a.b = 0; x.y = 0; }", options: [{ props: true, ignorePropertyModificationsFor: ["a", "x"] }] },
{ code: "function foo(a) { a.b.c = 0;}", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
{
Expand All @@ -53,6 +59,33 @@ ruleTester.run("no-param-reassign", rule, {
code: "function foo(a) { ({...a.b} = obj); }",
options: [{ props: false }],
parserOptions: { ecmaVersion: 2018 }
},
{
code: "function foo(a) { for (obj[a.b] in obj); }",
options: [{ props: true }]
},
{
code: "function foo(a) { for (obj[a.b] of arr); }",
options: [{ props: true }],
parserOptions: { ecmaVersion: 6 }
},
{
code: "function foo(a) { for (bar in a.b); }",
options: [{ props: true }]
},
{
code: "function foo(a) { for (bar of a.b); }",
options: [{ props: true }],
parserOptions: { ecmaVersion: 6 }
},
{
code: "function foo(a) { for (bar in baz) a.b; }",
options: [{ props: true }]
},
{
code: "function foo(a) { for (bar of baz) a.b; }",
options: [{ props: true }],
parserOptions: { ecmaVersion: 6 }
}
],

Expand All @@ -68,6 +101,8 @@ ruleTester.run("no-param-reassign", rule, {
{ code: "function foo([, {bar}]) { bar = 13; }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },
{ code: "function foo(bar) { ({bar} = {}); }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },
{ code: "function foo(bar) { ({x: [, bar = 0]} = {}); }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },
{ code: "function foo(bar) { for (bar in baz); }", errors: [{ message: "Assignment to function parameter 'bar'." }] },
{ code: "function foo(bar) { for (bar of baz); }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },

{
code: "function foo(bar) { bar.a = 0; }",
Expand All @@ -89,6 +124,17 @@ ruleTester.run("no-param-reassign", rule, {
options: [{ props: true }],
errors: [{ message: "Assignment to property of function parameter 'bar'." }]
},
{
code: "function foo(bar) { for (bar.a in {}); }",
options: [{ props: true }],
errors: [{ message: "Assignment to property of function parameter 'bar'." }]
},
{
code: "function foo(bar) { for (bar.a of []); }",
options: [{ props: true }],
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Assignment to property of function parameter 'bar'." }]
},
{
code: "function foo(bar) { (bar ? bar : [])[0] = 1; }",
options: [{ props: true }],
Expand Down Expand Up @@ -139,6 +185,18 @@ ruleTester.run("no-param-reassign", rule, {
options: [{ props: true }],
parserOptions: { ecmaVersion: 2018 },
errors: [{ message: "Assignment to property of function parameter 'a'." }]
},
{
code: "function foo(a) { for ({bar: a.b} in {}); }",
options: [{ props: true }],
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Assignment to property of function parameter 'a'." }]
},
{
code: "function foo(a) { for ([a.b] of []); }",
options: [{ props: true }],
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Assignment to property of function parameter 'a'." }]
}
]
});