Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / require-atomic-updates.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/require-atomic-updates.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/require-atomic-updates.js
new file mode 100644 (file)
index 0000000..bdd6d81
--- /dev/null
@@ -0,0 +1,283 @@
+/**
+ * @fileoverview disallow assignments that can lead to race conditions due to usage of `await` or `yield`
+ * @author Teddy Katz
+ * @author Toru Nagashima
+ */
+"use strict";
+
+/**
+ * Make the map from identifiers to each reference.
+ * @param {escope.Scope} scope The scope to get references.
+ * @param {Map<Identifier, escope.Reference>} [outReferenceMap] The map from identifier nodes to each reference object.
+ * @returns {Map<Identifier, escope.Reference>} `referenceMap`.
+ */
+function createReferenceMap(scope, outReferenceMap = new Map()) {
+    for (const reference of scope.references) {
+        outReferenceMap.set(reference.identifier, reference);
+    }
+    for (const childScope of scope.childScopes) {
+        if (childScope.type !== "function") {
+            createReferenceMap(childScope, outReferenceMap);
+        }
+    }
+
+    return outReferenceMap;
+}
+
+/**
+ * Get `reference.writeExpr` of a given reference.
+ * If it's the read reference of MemberExpression in LHS, returns RHS in order to address `a.b = await a`
+ * @param {escope.Reference} reference The reference to get.
+ * @returns {Expression|null} The `reference.writeExpr`.
+ */
+function getWriteExpr(reference) {
+    if (reference.writeExpr) {
+        return reference.writeExpr;
+    }
+    let node = reference.identifier;
+
+    while (node) {
+        const t = node.parent.type;
+
+        if (t === "AssignmentExpression" && node.parent.left === node) {
+            return node.parent.right;
+        }
+        if (t === "MemberExpression" && node.parent.object === node) {
+            node = node.parent;
+            continue;
+        }
+
+        break;
+    }
+
+    return null;
+}
+
+/**
+ * Checks if an expression is a variable that can only be observed within the given function.
+ * @param {Variable|null} variable The variable to check
+ * @param {boolean} isMemberAccess If `true` then this is a member access.
+ * @returns {boolean} `true` if the variable is local to the given function, and is never referenced in a closure.
+ */
+function isLocalVariableWithoutEscape(variable, isMemberAccess) {
+    if (!variable) {
+        return false; // A global variable which was not defined.
+    }
+
+    // If the reference is a property access and the variable is a parameter, it handles the variable is not local.
+    if (isMemberAccess && variable.defs.some(d => d.type === "Parameter")) {
+        return false;
+    }
+
+    const functionScope = variable.scope.variableScope;
+
+    return variable.references.every(reference =>
+        reference.from.variableScope === functionScope);
+}
+
+class SegmentInfo {
+    constructor() {
+        this.info = new WeakMap();
+    }
+
+    /**
+     * Initialize the segment information.
+     * @param {PathSegment} segment The segment to initialize.
+     * @returns {void}
+     */
+    initialize(segment) {
+        const outdatedReadVariableNames = new Set();
+        const freshReadVariableNames = 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);
+            }
+        }
+
+        this.info.set(segment, { outdatedReadVariableNames, freshReadVariableNames });
+    }
+
+    /**
+     * 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.
+     * @returns {void}
+     */
+    markAsRead(segments, variableName) {
+        for (const segment of segments) {
+            const info = this.info.get(segment);
+
+            if (info) {
+                info.freshReadVariableNames.add(variableName);
+            }
+        }
+    }
+
+    /**
+     * Move `freshReadVariableNames` to `outdatedReadVariableNames`.
+     * @param {PathSegment[]} segments The segments to process.
+     * @returns {void}
+     */
+    makeOutdated(segments) {
+        for (const segment of segments) {
+            const info = this.info.get(segment);
+
+            if (info) {
+                info.freshReadVariableNames.forEach(Set.prototype.add, info.outdatedReadVariableNames);
+                info.freshReadVariableNames.clear();
+            }
+        }
+    }
+
+    /**
+     * 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.
+     * @returns {boolean} `true` if the variable is outdated on the segments.
+     */
+    isOutdated(segments, variableName) {
+        for (const segment of segments) {
+            const info = this.info.get(segment);
+
+            if (info && info.outdatedReadVariableNames.has(variableName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        type: "problem",
+
+        docs: {
+            description: "disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
+            category: "Possible Errors",
+            recommended: false,
+            url: "https://eslint.org/docs/rules/require-atomic-updates"
+        },
+
+        fixable: null,
+        schema: [],
+
+        messages: {
+            nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`."
+        }
+    },
+
+    create(context) {
+        const sourceCode = context.getSourceCode();
+        const assignmentReferences = new Map();
+        const segmentInfo = new SegmentInfo();
+        let stack = null;
+
+        return {
+            onCodePathStart(codePath) {
+                const scope = context.getScope();
+                const shouldVerify =
+                    scope.type === "function" &&
+                    (scope.block.async || scope.block.generator);
+
+                stack = {
+                    upper: stack,
+                    codePath,
+                    referenceMap: shouldVerify ? createReferenceMap(scope) : null
+                };
+            },
+            onCodePathEnd() {
+                stack = stack.upper;
+            },
+
+            // Initialize the segment information.
+            onCodePathSegmentStart(segment) {
+                segmentInfo.initialize(segment);
+            },
+
+            // Handle references to prepare verification.
+            Identifier(node) {
+                const { codePath, referenceMap } = stack;
+                const reference = referenceMap && referenceMap.get(node);
+
+                // Ignore if this is not a valid variable reference.
+                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);
+                }
+
+                /*
+                 * 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 clausure.
+                 */
+                if (writeExpr &&
+                    writeExpr.parent.right === writeExpr && // ← exclude variable declarations.
+                    !isLocalVariableWithoutEscape(variable, isMemberAccess)
+                ) {
+                    let refs = assignmentReferences.get(writeExpr);
+
+                    if (!refs) {
+                        refs = [];
+                        assignmentReferences.set(writeExpr, refs);
+                    }
+
+                    refs.push(reference);
+                }
+            },
+
+            /*
+             * Verify assignments.
+             * If the reference exists in `outdatedReadVariableNames` list, report it.
+             */
+            ":expression:exit"(node) {
+                const { codePath, referenceMap } = stack;
+
+                // referenceMap exists if this is in a resumable function scope.
+                if (!referenceMap) {
+                    return;
+                }
+
+                // Mark the read variables on this code path as outdated.
+                if (node.type === "AwaitExpression" || node.type === "YieldExpression") {
+                    segmentInfo.makeOutdated(codePath.currentSegments);
+                }
+
+                // Verify.
+                const references = assignmentReferences.get(node);
+
+                if (references) {
+                    assignmentReferences.delete(node);
+
+                    for (const reference of references) {
+                        const name = reference.identifier.name;
+
+                        if (segmentInfo.isOutdated(codePath.currentSegments, name)) {
+                            context.report({
+                                node: node.parent,
+                                messageId: "nonAtomicUpdate",
+                                data: {
+                                    value: sourceCode.getText(node.parent.left)
+                                }
+                            });
+                        }
+                    }
+                }
+            }
+        };
+    }
+};