diff --git a/docs/rules/no-param-reassign.md b/docs/rules/no-param-reassign.md index 51c81a8c093..f88d211e22b 100644 --- a/docs/rules/no-param-reassign.md +++ b/docs/rules/no-param-reassign.md @@ -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: @@ -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: @@ -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: @@ -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) {} +} ``` diff --git a/lib/rules/no-param-reassign.js b/lib/rules/no-param-reassign.js index 880ff93cb53..9b8c828d2c2 100644 --- a/lib/rules/no-param-reassign.js +++ b/lib/rules/no-param-reassign.js @@ -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; @@ -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) { diff --git a/tests/lib/rules/no-param-reassign.js b/tests/lib/rules/no-param-reassign.js index dfff7f26ad1..fec1e4168f5 100644 --- a/tests/lib/rules/no-param-reassign.js +++ b/tests/lib/rules/no-param-reassign.js @@ -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 } }, @@ -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"] }] }, { @@ -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 } } ], @@ -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; }", @@ -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 }], @@ -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'." }] } ] });