Repository: eslint/eslint (js)

Issue:
False negatives with sourceType: module and ecmaVersion <= 5
**Tell us about your environment**

* **ESLint Version:**
v4.12.1
* **Node Version:**
8.4.0
* **npm Version:**
5.3.0

**What parser (default, Babel-ESLint, etc.) are you using?**
default

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
module.exports = {
    "parserOptions": {
        "sourceType": "module",
        "ecmaVersion": 5,
    }
}
```

</details>

Running against this file, ecmaVersion is ignored (could also be '3'):
 
```js
// es6.js
const x = () => `hi`;
```

**What did you expect to happen?**

Either, I would see an error like the below, rejecting an unsupported config:


```bash
> eslint -c withoutModule.config.js es6.js
Incompatible config: sourceType: module cannot be used with ecmaVersion <= 5
```

or the (admittedly niche*) use-case of ES6 modules with ES5 syntax would be supported, and I'd see errors:

```bash
> eslint -c withoutModule.config.js es6.js
es6.js
  1:1  error  Parsing error: The keyword 'const' is reserved

✖ 1 problem (1 error, 0 warnings)
```

which I get correctly with this config:

```js
// withoutModule.config.js
module.exports = {
    "parserOptions": {
        "ecmaVersion": 5,
    }
}
```

*niche: I was using rollup with some old ES5 code, and only wanted to modify the code to add imports. I tried the above, and thought "cool, it's working", but actually it was silently ignoring the ecmaVersion.

**What actually happened? Please include the actual, raw output from ESLint.**

No errors are reported:

```bash
> eslint es6.js
> echo $?
0
```




Pull request:
Breaking: upgrade espree to 6.0.0-alpha.0 (fixes #9687)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[x] Other, please explain:

<!--
    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 updates espree to v6.0.0-alpha.0, which fixes https://github.com/eslint/eslint/issues/9687.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular


Patch:
diff --git a/lib/rules/no-loop-func.js b/lib/rules/no-loop-func.js
index e6063806a..f4531f3a7 100644
--- a/lib/rules/no-loop-func.js
+++ b/lib/rules/no-loop-func.js
@@ -157,13 +157,17 @@ module.exports = {
         type: "suggestion",
 
         docs: {
-            description: "disallow `function` declarations and expressions inside loop statements",
+            description: "disallow function declarations that contain unsafe references inside loop statements",
             category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-loop-func"
         },
 
-        schema: []
+        schema: [],
+
+        messages: {
+            unsafeRefs: "Function declared in a loop contains unsafe references to variable(s) {{ varNames }}."
+        }
     },
 
     create(context) {
@@ -185,11 +189,14 @@ module.exports = {
             }
 
             const references = context.getScope().through;
-
-            if (references.length > 0 &&
-                !references.every(isSafe.bind(null, loopNode))
-            ) {
-                context.report({ node, message: "Don't make functions within a loop." });
+            const unsafeRefs = references.filter(r => !isSafe(loopNode, r)).map(r => r.identifier.name);
+
+            if (unsafeRefs.length > 0) {
+                context.report({
+                    node,
+                    messageId: "unsafeRefs",
+                    data: { varNames: `'${unsafeRefs.join("', '")}'` }
+                });
             }
         }
 
diff --git a/lib/rules/no-var.js b/lib/rules/no-var.js
index 83681a7aa..0978de634 100644
--- a/lib/rules/no-var.js
+++ b/lib/rules/no-var.js
@@ -305,17 +305,16 @@ module.exports = {
          * @returns {void}
          */
         function report(node) {
-            const varToken = sourceCode.getFirstToken(node);
-
             context.report({
                 node,
                 message: "Unexpected var, use let or const instead.",
 
                 fix(fixer) {
-                    if (canFix(node)) {
-                        return fixer.replaceText(varToken, "let");
-                    }
-                    return null;
+                    const varToken = sourceCode.getFirstToken(node, { filter: t => t.value === "var" });
+
+                    return canFix(node)
+                        ? fixer.replaceText(varToken, "let")
+                        : null;
                 }
             });
         }
diff --git a/package.json b/package.json
index d478057ab..e47757ee4 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
     "eslint-scope": "^4.0.3",
     "eslint-utils": "^1.3.1",
     "eslint-visitor-keys": "^1.0.0",
-    "espree": "^5.0.1",
+    "espree": "^6.0.0-alpha.0",
     "esquery": "^1.0.1",
     "esutils": "^2.0.2",
     "file-entry-cache": "^5.0.1",


Test patch:
diff --git a/tests/fixtures/parsers/typescript-parsers/declare-var.js b/tests/fixtures/parsers/typescript-parsers/declare-var.js
new file mode 100644
index 000000000..68736259e
--- /dev/null
+++ b/tests/fixtures/parsers/typescript-parsers/declare-var.js
@@ -0,0 +1,215 @@
+"use strict";
+
+/*
+ * Parsed on astexplorer.net using @typescript-eslint/parser@1.4.2
+ *
+ * Source:
+ * declare var foo = 2;
+ */
+
+exports.parse = () => ({
+  "type": "Program",
+  "body": [
+    {
+      "type": "VariableDeclaration",
+      "declarations": [
+        {
+          "type": "VariableDeclarator",
+          "id": {
+            "type": "Identifier",
+            "name": "foo",
+            "range": [
+              12,
+              15
+            ],
+            "loc": {
+              "start": {
+                "line": 1,
+                "column": 12
+              },
+              "end": {
+                "line": 1,
+                "column": 15
+              }
+            }
+          },
+          "init": {
+            "type": "Literal",
+            "value": 2,
+            "raw": "2",
+            "range": [
+              18,
+              19
+            ],
+            "loc": {
+              "start": {
+                "line": 1,
+                "column": 18
+              },
+              "end": {
+                "line": 1,
+                "column": 19
+              }
+            }
+          },
+          "range": [
+            12,
+            19
+          ],
+          "loc": {
+            "start": {
+              "line": 1,
+              "column": 12
+            },
+            "end": {
+              "line": 1,
+              "column": 19
+            }
+          }
+        }
+      ],
+      "kind": "var",
+      "range": [
+        0,
+        20
+      ],
+      "loc": {
+        "start": {
+          "line": 1,
+          "column": 0
+        },
+        "end": {
+          "line": 1,
+          "column": 20
+        }
+      },
+      "declare": true
+    }
+  ],
+  "sourceType": "module",
+  "range": [
+    0,
+    20
+  ],
+  "loc": {
+    "start": {
+      "line": 1,
+      "column": 0
+    },
+    "end": {
+      "line": 1,
+      "column": 20
+    }
+  },
+  "tokens": [
+    {
+      "type": "Identifier",
+      "value": "declare",
+      "range": [
+        0,
+        7
+      ],
+      "loc": {
+        "start": {
+          "line": 1,
+          "column": 0
+        },
+        "end": {
+          "line": 1,
+          "column": 7
+        }
+      }
+    },
+    {
+      "type": "Keyword",
+      "value": "var",
+      "range": [
+        8,
+        11
+      ],
+      "loc": {
+        "start": {
+          "line": 1,
+          "column": 8
+        },
+        "end": {
+          "line": 1,
+          "column": 11
+        }
+      }
+    },
+    {
+      "type": "Identifier",
+      "value": "foo",
+      "range": [
+        12,
+        15
+      ],
+      "loc": {
+        "start": {
+          "line": 1,
+          "column": 12
+        },
+        "end": {
+          "line": 1,
+          "column": 15
+        }
+      }
+    },
+    {
+      "type": "Punctuator",
+      "value": "=",
+      "range": [
+        16,
+        17
+      ],
+      "loc": {
+        "start": {
+          "line": 1,
+          "column": 16
+        },
+        "end": {
+          "line": 1,
+          "column": 17
+        }
+      }
+    },
+    {
+      "type": "Numeric",
+      "value": "2",
+      "range": [
+        18,
+        19
+      ],
+      "loc": {
+        "start": {
+          "line": 1,
+          "column": 18
+        },
+        "end": {
+          "line": 1,
+          "column": 19
+        }
+      }
+    },
+    {
+      "type": "Punctuator",
+      "value": ";",
+      "range": [
+        19,
+        20
+      ],
+      "loc": {
+        "start": {
+          "line": 1,
+          "column": 19
+        },
+        "end": {
+          "line": 1,
+          "column": 20
+        }
+      }
+    }
+  ],
+  "comments": []
+});
diff --git a/tests/lib/rules/no-loop-func.js b/tests/lib/rules/no-loop-func.js
index 08ccd1c5d..843cdaa8a 100644
--- a/tests/lib/rules/no-loop-func.js
+++ b/tests/lib/rules/no-loop-func.js
@@ -16,8 +16,7 @@ const rule = require("../../../lib/rules/no-loop-func"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester(),
-    expectedErrorMessage = "Don't make functions within a loop.";
+const ruleTester = new RuleTester();
 
 ruleTester.run("no-loop-func", rule, {
     valid: [
@@ -117,117 +116,121 @@ ruleTester.run("no-loop-func", rule, {
     invalid: [
         {
             code: "for (var i=0; i<l; i++) { (function() { i; }) }",
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
+        },
+        {
+            code: "for (var i=0; i<l; i++) { for (var j=0; j<m; j++) { (function() { i+j; }) } }",
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i', 'j'" }, type: "FunctionExpression" }]
         },
         {
             code: "for (var i in {}) { (function() { i; }) }",
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
         },
         {
             code: "for (var i of {}) { (function() { i; }) }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
         },
         {
             code: "for (var i=0; i < l; i++) { (() => { i; }) }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "ArrowFunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "ArrowFunctionExpression" }]
         },
         {
             code: "for (var i=0; i < l; i++) { var a = function() { i; } }",
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
         },
         {
             code: "for (var i=0; i < l; i++) { function a() { i; }; a(); }",
-            errors: [{ message: expectedErrorMessage, type: "FunctionDeclaration" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionDeclaration" }]
         },
         {
             code: "for (var i=0; (function() { i; })(), i<l; i++) { }",
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
         },
         {
             code: "for (var i=0; i<l; (function() { i; })(), i++) { }",
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
         },
         {
             code: "while(i) { (function() { i; }) }",
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
         },
         {
             code: "do { (function() { i; }) } while (i)",
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "FunctionExpression" }]
         },
 
         // Warns functions which are using modified variables.
         {
             code: "let a; for (let i=0; i<l; i++) { a = 1; (function() { a; });}",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "let a; for (let i in {}) { (function() { a; }); a = 1; }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "let a; for (let i of {}) { (function() { a; }); } a = 1; ",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "let a; for (let i=0; i<l; i++) { (function() { (function() { a; }); }); a = 1; }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "let a; for (let i in {}) { a = 1; function foo() { (function() { a; }); } }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionDeclaration" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionDeclaration" }]
         },
         {
             code: "let a; for (let i of {}) { (() => { (function() { a; }); }); } a = 1;",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "ArrowFunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "ArrowFunctionExpression" }]
         },
         {
             code: "for (var i = 0; i < 10; ++i) { for (let x in xs.filter(x => x != i)) {  } }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "ArrowFunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'i'" }, type: "ArrowFunctionExpression" }]
         },
         {
             code: "for (let x of xs) { let a; for (let y of ys) { a = 1; (function() { a; }); } }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "for (var x of xs) { for (let y of ys) { (function() { x; }); } }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'x'" }, type: "FunctionExpression" }]
         },
         {
             code: "for (var x of xs) { (function() { x; }); }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'x'" }, type: "FunctionExpression" }]
         },
         {
             code: "var a; for (let x of xs) { a = 1; (function() { a; }); }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "var a; for (let x of xs) { (function() { a; }); a = 1; }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "let a; function foo() { a = 10; } for (let x of xs) { (function() { a; }); } foo();",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         },
         {
             code: "let a; function foo() { a = 10; for (let x of xs) { (function() { a; }); } } foo();",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: expectedErrorMessage, type: "FunctionExpression" }]
+            errors: [{ messageId: "unsafeRefs", data: { varNames: "'a'" }, type: "FunctionExpression" }]
         }
     ]
 });
diff --git a/tests/lib/rules/no-var.js b/tests/lib/rules/no-var.js
index 98b3be5e5..beffe2749 100644
--- a/tests/lib/rules/no-var.js
+++ b/tests/lib/rules/no-var.js
@@ -298,6 +298,15 @@ ruleTester.run("no-var", rule, {
             output: "let foo = 1",
             parserOptions: { sourceType: "module" },
             errors: ["Unexpected var, use let or const instead."]
+        },
+
+        // https://github.com/eslint/eslint/issues/11594
+        {
+            code: "declare var foo = 2;",
+            output: "declare let foo = 2;",
+            parser: require.resolve("../../fixtures/parsers/typescript-parsers/declare-var"),
+            parserOptions: { sourceType: "module" },
+            errors: ["Unexpected var, use let or const instead."]
         }
     ]
 });