Skip to content

Commit

Permalink
no-array-for-each: Improve parameter reassign detection (#1823)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed May 25, 2022
1 parent 330d1dd commit 56df468
Show file tree
Hide file tree
Showing 4 changed files with 836 additions and 9 deletions.
34 changes: 25 additions & 9 deletions rules/no-array-for-each.js
Expand Up @@ -317,18 +317,34 @@ function isFunctionParametersSafeToFix(callbackFunction, {context, scope, callEx
return true;
}

// TODO[@fisker]: Improve `./utils/is-left-hand-side.js` with similar logic
function isAssignmentLeftHandSide(node) {
const {parent} = node;

switch (parent.type) {
case 'AssignmentExpression':
return parent.left === node;
case 'UpdateExpression':
return parent.argument === node;
case 'Property':
return parent.value === node && isAssignmentLeftHandSide(parent);
case 'AssignmentPattern':
return parent.left === node && isAssignmentLeftHandSide(parent);
case 'ArrayPattern':
return parent.elements.includes(node) && isAssignmentLeftHandSide(parent);
case 'ObjectPattern':
return parent.properties.includes(node) && isAssignmentLeftHandSide(parent);
default:
return false;
}
}

function isFunctionParameterVariableReassigned(callbackFunction, context) {
return context.getDeclaredVariables(callbackFunction)
.filter(variable => variable.defs[0].type === 'Parameter')
.some(variable => {
const {references} = variable;
return references.some(reference => {
const node = reference.identifier;
const {parent} = node;
return parent.type === 'UpdateExpression'
|| (parent.type === 'AssignmentExpression' && parent.left === node);
});
});
.some(variable =>
variable.references.some(reference => isAssignmentLeftHandSide(reference.identifier)),
);
}

function isFixable(callExpression, {scope, functionInfo, allIdentifiers, context}) {
Expand Down
61 changes: 61 additions & 0 deletions test/no-array-for-each.mjs
Expand Up @@ -441,6 +441,67 @@ test.snapshot({
// Arrow function body
'array.forEach((arrayInArray) => arrayInArray.forEach(element => bar(element)));',
'array.forEach((arrayInArray) => arrayInArray?.forEach(element => bar(element)));',

// Destructuring assign
...[
'({element} = bar)',
'({element: a} = bar)',
'({a: element} = bar)',
'({[element]: a} = bar)',
'({[a]: element} = bar)',
'({element = a} = bar)',
'({a = element} = bar)',
'[element] = bar',
'[element = a] = bar',
'[a = element] = bar',
'({deep: {element}} = bar)',
'({deep: {element: a}} = bar)',
'({deep: {a: element}} = bar)',
'({deep: {[element]: a}} = bar)',
'({deep: {[a]: element}} = bar)',
'({deep: {element = a}} = bar)',
'({deep: {a = element}} = bar)',
'({deep: [element]} = bar)',
'({deep: [element = a]} = bar)',
'({deep: [a = element]} = bar)',
'[{element}] = bar',
'[{element: a}] = bar',
'[{a: element}] = bar',
'[{[element]: a}] = bar',
'[{[a]: element}] = bar',
'[{element = a}] = bar',
'[{a = element}] = bar',
'[[element]] = bar',
'[[element = a]] = bar',
'[[a = element]] = bar',
].map(code => outdent`
foo.forEach(element => {
${code};
});
`),
outdent`
foo.forEach(element => {
[
bar = ((element) => {
[element] = array;
})(element)
] = baz;
});
`,
outdent`
foo.forEach(element => {
[
bar = ((element = array) => element)(element)
] = baz;
});
`,
outdent`
foo.forEach(element => {
[
bar = (([element] = array) => element)(element)
] = baz;
});
`,
],
});

Expand Down

0 comments on commit 56df468

Please sign in to comment.