Skip to content

Commit 56df468

Browse files
authored
no-array-for-each: Improve parameter reassign detection (#1823)
1 parent 330d1dd commit 56df468

File tree

4 files changed

+836
-9
lines changed

4 files changed

+836
-9
lines changed

rules/no-array-for-each.js

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -317,18 +317,34 @@ function isFunctionParametersSafeToFix(callbackFunction, {context, scope, callEx
317317
return true;
318318
}
319319

320+
// TODO[@fisker]: Improve `./utils/is-left-hand-side.js` with similar logic
321+
function isAssignmentLeftHandSide(node) {
322+
const {parent} = node;
323+
324+
switch (parent.type) {
325+
case 'AssignmentExpression':
326+
return parent.left === node;
327+
case 'UpdateExpression':
328+
return parent.argument === node;
329+
case 'Property':
330+
return parent.value === node && isAssignmentLeftHandSide(parent);
331+
case 'AssignmentPattern':
332+
return parent.left === node && isAssignmentLeftHandSide(parent);
333+
case 'ArrayPattern':
334+
return parent.elements.includes(node) && isAssignmentLeftHandSide(parent);
335+
case 'ObjectPattern':
336+
return parent.properties.includes(node) && isAssignmentLeftHandSide(parent);
337+
default:
338+
return false;
339+
}
340+
}
341+
320342
function isFunctionParameterVariableReassigned(callbackFunction, context) {
321343
return context.getDeclaredVariables(callbackFunction)
322344
.filter(variable => variable.defs[0].type === 'Parameter')
323-
.some(variable => {
324-
const {references} = variable;
325-
return references.some(reference => {
326-
const node = reference.identifier;
327-
const {parent} = node;
328-
return parent.type === 'UpdateExpression'
329-
|| (parent.type === 'AssignmentExpression' && parent.left === node);
330-
});
331-
});
345+
.some(variable =>
346+
variable.references.some(reference => isAssignmentLeftHandSide(reference.identifier)),
347+
);
332348
}
333349

334350
function isFixable(callExpression, {scope, functionInfo, allIdentifiers, context}) {

test/no-array-for-each.mjs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,67 @@ test.snapshot({
441441
// Arrow function body
442442
'array.forEach((arrayInArray) => arrayInArray.forEach(element => bar(element)));',
443443
'array.forEach((arrayInArray) => arrayInArray?.forEach(element => bar(element)));',
444+
445+
// Destructuring assign
446+
...[
447+
'({element} = bar)',
448+
'({element: a} = bar)',
449+
'({a: element} = bar)',
450+
'({[element]: a} = bar)',
451+
'({[a]: element} = bar)',
452+
'({element = a} = bar)',
453+
'({a = element} = bar)',
454+
'[element] = bar',
455+
'[element = a] = bar',
456+
'[a = element] = bar',
457+
'({deep: {element}} = bar)',
458+
'({deep: {element: a}} = bar)',
459+
'({deep: {a: element}} = bar)',
460+
'({deep: {[element]: a}} = bar)',
461+
'({deep: {[a]: element}} = bar)',
462+
'({deep: {element = a}} = bar)',
463+
'({deep: {a = element}} = bar)',
464+
'({deep: [element]} = bar)',
465+
'({deep: [element = a]} = bar)',
466+
'({deep: [a = element]} = bar)',
467+
'[{element}] = bar',
468+
'[{element: a}] = bar',
469+
'[{a: element}] = bar',
470+
'[{[element]: a}] = bar',
471+
'[{[a]: element}] = bar',
472+
'[{element = a}] = bar',
473+
'[{a = element}] = bar',
474+
'[[element]] = bar',
475+
'[[element = a]] = bar',
476+
'[[a = element]] = bar',
477+
].map(code => outdent`
478+
foo.forEach(element => {
479+
${code};
480+
});
481+
`),
482+
outdent`
483+
foo.forEach(element => {
484+
[
485+
bar = ((element) => {
486+
[element] = array;
487+
})(element)
488+
] = baz;
489+
});
490+
`,
491+
outdent`
492+
foo.forEach(element => {
493+
[
494+
bar = ((element = array) => element)(element)
495+
] = baz;
496+
});
497+
`,
498+
outdent`
499+
foo.forEach(element => {
500+
[
501+
bar = (([element] = array) => element)(element)
502+
] = baz;
503+
});
504+
`,
444505
],
445506
});
446507

0 commit comments

Comments
 (0)