From 34786b65f73e738c4531ef4c690f1ab1f179c6cc Mon Sep 17 00:00:00 2001 From: Patrick Ahmetovic Date: Sat, 10 Apr 2021 17:21:38 +0200 Subject: [PATCH] Rework & store variables in segmentInfo instead of variable names --- lib/rules/require-atomic-updates.js | 46 +++++++++++------------ tests/lib/rules/require-atomic-updates.js | 15 ++++++++ 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/lib/rules/require-atomic-updates.js b/lib/rules/require-atomic-updates.js index a77b01c11642..a8e5007de172 100644 --- a/lib/rules/require-atomic-updates.js +++ b/lib/rules/require-atomic-updates.js @@ -86,42 +86,42 @@ class SegmentInfo { * @returns {void} */ initialize(segment) { - const outdatedReadVariableNames = new Set(); - const freshReadVariableNames = new Set(); + const outdatedReadVariables = new Set(); + const freshReadVariables = new Set(); for (const prevSegment of segment.prevSegments) { const info = this.info.get(prevSegment); if (info) { - info.outdatedReadVariableNames.forEach(Set.prototype.add, outdatedReadVariableNames); - info.freshReadVariableNames.forEach(Set.prototype.add, freshReadVariableNames); + info.outdatedReadVariables.forEach(Set.prototype.add, outdatedReadVariables); + info.freshReadVariables.forEach(Set.prototype.add, freshReadVariables); } } - this.info.set(segment, { outdatedReadVariableNames, freshReadVariableNames }); + this.info.set(segment, { outdatedReadVariables, freshReadVariables }); } /** * Mark a given variable as read on given segments. * @param {PathSegment[]} segments The segments that it read the variable on. - * @param {string} variableName The variable name to be read. + * @param {Variable} variable The variable to be read. * @returns {void} */ - markAsRead(segments, variableName) { + markAsRead(segments, variable) { for (const segment of segments) { const info = this.info.get(segment); if (info) { - info.freshReadVariableNames.add(variableName); + info.freshReadVariables.add(variable); // If a variable is freshly read again, then it's no more out-dated. - info.outdatedReadVariableNames.delete(variableName); + info.outdatedReadVariables.delete(variable); } } } /** - * Move `freshReadVariableNames` to `outdatedReadVariableNames`. + * Move `freshReadVariables` to `outdatedReadVariables`. * @param {PathSegment[]} segments The segments to process. * @returns {void} */ @@ -130,8 +130,8 @@ class SegmentInfo { const info = this.info.get(segment); if (info) { - info.freshReadVariableNames.forEach(Set.prototype.add, info.outdatedReadVariableNames); - info.freshReadVariableNames.clear(); + info.freshReadVariables.forEach(Set.prototype.add, info.outdatedReadVariables); + info.freshReadVariables.clear(); } } } @@ -139,14 +139,14 @@ class SegmentInfo { /** * Check if a given variable is outdated on the current segments. * @param {PathSegment[]} segments The current segments. - * @param {string} variableName The variable name to check. + * @param {Variable} variable The variable to check. * @returns {boolean} `true` if the variable is outdated on the segments. */ - isOutdated(segments, variableName) { + isOutdated(segments, variable) { for (const segment of segments) { const info = this.info.get(segment); - if (info && info.outdatedReadVariableNames.has(variableName)) { + if (info && info.outdatedReadVariables.has(variable)) { return true; } } @@ -214,26 +214,22 @@ module.exports = { if (!reference) { return; } - const name = reference.identifier.name; const variable = reference.resolved; const writeExpr = getWriteExpr(reference); const isMemberAccess = reference.identifier.parent.type === "MemberExpression"; // Add a fresh read variable. if (reference.isRead() && !(writeExpr && writeExpr.parent.operator === "=")) { - segmentInfo.markAsRead(codePath.currentSegments, name); + segmentInfo.markAsRead(codePath.currentSegments, variable); } - const isVariableDeclaration = isLocalVariableWithoutEscape(variable, isMemberAccess) || - node.parent.type === "VariableDeclarator"; - /* * Register the variable to verify after ESLint traversed the `writeExpr` node * if this reference is an assignment to a variable which is referred from other closure. */ if (writeExpr && - writeExpr.parent.right === writeExpr && - !isVariableDeclaration + writeExpr.parent.right === writeExpr && // ← exclude variable declarations. + !isLocalVariableWithoutEscape(variable, isMemberAccess) ) { let refs = assignmentReferences.get(writeExpr); @@ -248,7 +244,7 @@ module.exports = { /* * Verify assignments. - * If the reference exists in `outdatedReadVariableNames` list, report it. + * If the reference exists in `outdatedReadVariables` list, report it. */ ":expression:exit"(node) { const { codePath, referenceMap } = stack; @@ -270,9 +266,9 @@ module.exports = { assignmentReferences.delete(node); for (const reference of references) { - const name = reference.identifier.name; + const variable = reference.resolved; - if (segmentInfo.isOutdated(codePath.currentSegments, name)) { + if (segmentInfo.isOutdated(codePath.currentSegments, variable)) { context.report({ node: node.parent, messageId: "nonAtomicUpdate", diff --git a/tests/lib/rules/require-atomic-updates.js b/tests/lib/rules/require-atomic-updates.js index 4fd6bc268935..fbfd3ec483eb 100644 --- a/tests/lib/rules/require-atomic-updates.js +++ b/tests/lib/rules/require-atomic-updates.js @@ -186,6 +186,21 @@ ruleTester.run("require-atomic-updates", rule, { const prop = props.find(a => a.id === entry2.id) || null; } } + `, + + ` + async function run() { + { + let entry; + await entry; + } + { + let entry; + () => entry; + + entry = 1; + } + } ` ],