Repository: eslint/eslint
Pull request #14577 (2021-05-08 00:34:10)

Upgrade: eslintrc for ModuleResolver fix
This incorporates https://github.com/eslint/eslintrc/pull/34, which
allows fixing `CLIEngine`'s `ModuleResolver` import.

<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[x] Other, please explain: Core dependency upgrade

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)

This upgrades `@eslint/eslintrc` to v0.4.1, which I just released with https://github.com/eslint/eslintrc/pull/34. This allows us to fix `CLIEngine`'s `ModuleResolver` import.

#### Is there anything you'd like reviewers to focus on?



Patch:
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index b1befaa04..ca298f9c3 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -27,16 +27,11 @@ const {
         naming,
         CascadingConfigArrayFactory,
         IgnorePattern,
-        getUsedExtractedConfigs
+        getUsedExtractedConfigs,
+        ModuleResolver
     }
 } = require("@eslint/eslintrc");
 
-/*
- * For some reason, ModuleResolver must be included via filepath instead of by
- * API exports in order to work properly. That's why this is separated out onto
- * its own require() statement.
- */
-const ModuleResolver = require("@eslint/eslintrc/lib/shared/relative-module-resolver");
 const { FileEnumerator } = require("./file-enumerator");
 
 const { Linter } = require("../linter");
diff --git a/lib/rules/require-atomic-updates.js b/lib/rules/require-atomic-updates.js
index c552f1bd8..b3df90742 100644
--- a/lib/rules/require-atomic-updates.js
+++ b/lib/rules/require-atomic-updates.js
@@ -13,6 +13,10 @@
  */
 function createReferenceMap(scope, outReferenceMap = new Map()) {
     for (const reference of scope.references) {
+        if (reference.resolved === null) {
+            continue;
+        }
+
         outReferenceMap.set(reference.identifier, reference);
     }
     for (const childScope of scope.childScopes) {
@@ -86,42 +90,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 +134,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 +143,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,14 +218,13 @@ 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);
                 }
 
                 /*
@@ -245,7 +248,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;
@@ -267,9 +270,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/package.json b/package.json
index 4f87ef640..2e26bf1c7 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
   "bugs": "https://github.com/eslint/eslint/issues/",
   "dependencies": {
     "@babel/code-frame": "7.12.11",
-    "@eslint/eslintrc": "^0.4.0",
+    "@eslint/eslintrc": "^0.4.1",
     "ajv": "^6.10.0",
     "chalk": "^4.0.0",
     "cross-spawn": "^7.0.2",
@@ -84,7 +84,6 @@
   "devDependencies": {
     "@babel/core": "^7.4.3",
     "@babel/preset-env": "^7.4.3",
-    "acorn": "^7.2.0",
     "babel-loader": "^8.0.5",
     "chai": "^4.0.1",
     "cheerio": "^0.22.0",


Test patch:
diff --git a/tests/lib/rules/no-redeclare.js b/tests/lib/rules/no-redeclare.js
index f6293b7e1..f89d6853f 100644
--- a/tests/lib/rules/no-redeclare.js
+++ b/tests/lib/rules/no-redeclare.js
@@ -9,7 +9,6 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const path = require("path");
 const rule = require("../../../lib/rules/no-redeclare");
 const { RuleTester } = require("../../../lib/rule-tester");
 
@@ -17,7 +16,6 @@ const { RuleTester } = require("../../../lib/rule-tester");
 // Tests
 //------------------------------------------------------------------------------
 
-const looseParserPath = path.resolve(__dirname, "../../tools/loose-parser.js");
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-redeclare", rule, {
@@ -205,112 +203,6 @@ ruleTester.run("no-redeclare", rule, {
             ]
         },
 
-        // let/const
-        {
-            code: "let a; let a;",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "let a; let a;",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015, sourceType: "module" },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "let a; let a;",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "let a; const a = 0;",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "const a = 0; const a = 0;",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "if (test) { let a; let a; }",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "switch (test) { case 0: let a; let a; }",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "for (let a, a;;);",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "for (let [a, a] in xs);",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "for (let [a, a] of xs);",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "function f() { let a; let a; }",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "function f(a) { let a; }",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-        {
-            code: "function f() { if (test) { let a; let a; } }",
-            parser: looseParserPath,
-            parserOptions: { ecmaVersion: 2015 },
-            errors: [
-                { message: "'a' is already defined.", type: "Identifier" }
-            ]
-        },
-
         // Comments and built-ins.
         {
             code: "/*globals Array */",
diff --git a/tests/lib/rules/require-atomic-updates.js b/tests/lib/rules/require-atomic-updates.js
index cd51e38ff..bd3738ac9 100644
--- a/tests/lib/rules/require-atomic-updates.js
+++ b/tests/lib/rules/require-atomic-updates.js
@@ -53,6 +53,7 @@ ruleTester.run("require-atomic-updates", rule, {
         "let foo; async function x() { foo = condition ? foo : await bar; }",
         "async function x() { let foo; bar(() => { let foo; blah(foo); }); foo += await result; }",
         "let foo; async function x() { foo = foo + 1; await bar; }",
+        "async function x() { foo += await bar; }",
 
 
         /*
@@ -162,6 +163,52 @@ ruleTester.run("require-atomic-updates", rule, {
                 count -= 1
                 return
             }
+        `,
+
+        // https://github.com/eslint/eslint/issues/14208
+        `
+            async function foo(e) {
+            }
+
+            async function run() {
+              const input = [];
+              const props = [];
+
+              for(const entry of input) {
+                const prop = props.find(a => a.id === entry.id) || null;
+                await foo(entry);
+              }
+
+              for(const entry of input) {
+                const prop = props.find(a => a.id === entry.id) || null;
+              }
+
+              for(const entry2 of input) {
+                const prop = props.find(a => a.id === entry2.id) || null;
+              }
+            }
+        `,
+
+        `
+            async function run() {
+              {
+                let entry;
+                await entry;
+              }
+              {
+                let entry;
+                () => entry;
+
+                entry = 1;
+              }
+            }
+        `,
+
+        `
+            async function run() {
+                await a;
+                b = 1;
+            }
         `
     ],
 
@@ -251,7 +298,7 @@ ruleTester.run("require-atomic-updates", rule, {
             errors: [COMPUTED_PROPERTY_ERROR, STATIC_PROPERTY_ERROR]
         },
         {
-            code: "async function x() { foo += await bar; }",
+            code: "let foo = ''; async function x() { foo += await bar; }",
             errors: [VARIABLE_ERROR]
         },
         {
diff --git a/tests/tools/loose-parser.js b/tests/tools/loose-parser.js
deleted file mode 100644
index 98e97cc36..000000000
--- a/tests/tools/loose-parser.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * @fileoverview Define a custom parser to ignore recoverable syntax errors.
- * @author Toru Nagashima <https://github.com/mysticatea>
- *
- * no-redeclare rule uses this parser to check redeclarations.
- */
-"use strict";
-
-const acorn = require("acorn");
-const espree = require("espree/lib/espree");
-
-/**
- * Define the parser which ignores recoverable errors.
- * @returns {(parser:acorn.Parser) => acorn.Parser} The function that defines loose parser.
- */
-function loose() {
-    return Parser => class LooseParser extends Parser {
-        raiseRecoverable() { // eslint-disable-line class-methods-use-this
-            // ignore
-        }
-    };
-}
-
-const LooseEspree = acorn.Parser.extend(espree(), loose());
-
-module.exports = {
-    parse(code, options) {
-        return new LooseEspree(options, code).parse();
-    }
-};