Repository: eslint/eslint (JavaScript)

Issue:
no-var should not fix variables named 'let'
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for bug reports. If you are here for another reason, please see below:

    1. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
    2. To request a rule change: https://eslint.org/docs/developer-guide/contributing/rule-changes
    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
    4. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**Tell us about your environment**

* **ESLint Version:** 5.16.0
* **Node Version:** 10.16.0
* **npm Version:** 6.9.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: {
    ecmaVersion: 6,
  },
  rules: {
    "no-var": "error",
  }
};
```

</details>

**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**

<!-- Paste the source code below: -->
```js
// this is valid code in non-strict mode

function foo() {
  var let;
}

function bar() {
  var { let } = {};
}

```

<!-- Paste the command you used to run ESLint: -->
```bash
eslint index.js --fix
```

**What did you expect to happen?**

No autofix, because it produces SyntaxError.

**What actually happened? Please include the actual, raw output from ESLint.**

```js
function foo() {
  let let; // SyntaxError
}

function bar() {
  let { let } = {}; // SyntaxError
}
```

Off topic: ESLint parser doesn't treat `let { let } = {}` as SyntaxError (probably an Acorn bug)

**Are you willing to submit a pull request to fix this bug?**
yes


Discussion:
Hey @mdjermanovic , thanks for the issue.

However, if you enable this `no-var` rule, you're intending to target an ES6 environment. If you're going to run your code at ES5 environment, you should disable this rule.
@g-plane 

The example is still a valid code in ES6 and beyond in non-strict mode, 

`let` is a valid identifier. It isn't a reserved word, it's just explicitly forbidden in `let` and `const` declarations.
I confirmed it in the spec: https://www.ecma-international.org/ecma-262/9.0/#sec-let-and-const-declarations-static-semantics-early-errors
> Off topic: ESLint parser doesn't treat let { let } = {} as SyntaxError (probably an Acorn bug)

@mdjermanovic Would you mind reporting that to https://github.com/acornjs/acorn so that it's tracked somewhere?
Sure, reported in [acorn #837](https://github.com/acornjs/acorn/issues/837) now.



Patch:
diff --git a/lib/rules/no-var.js b/lib/rules/no-var.js
index e9f191b39f2a..a3c750f9011b 100644
--- a/lib/rules/no-var.js
+++ b/lib/rules/no-var.js
@@ -174,6 +174,17 @@ function hasReferenceInTDZ(node) {
     };
 }
 
+/**
+ * Checks whether a given variable has name that is allowed for 'var' declarations,
+ * but disallowed for `let` declarations.
+ *
+ * @param {eslint-scope.Variable} variable The variable to check.
+ * @returns {boolean} `true` if the variable has a disallowed name.
+ */
+function hasNameDisallowedForLetDeclarations(variable) {
+    return variable.name === "let";
+}
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -223,6 +234,7 @@ module.exports = {
          * - A variable might be used before it is assigned within a loop.
          * - A variable might be used in TDZ.
          * - A variable is declared in statement position (e.g. a single-line `IfStatement`)
+         * - A variable has name that is disallowed for `let` declarations.
          *
          * ## A variable is declared on a SwitchCase node.
          *
@@ -271,7 +283,8 @@ module.exports = {
                 node.declarations.some(hasSelfReferenceInTDZ) ||
                 variables.some(isGlobal) ||
                 variables.some(isRedeclared) ||
-                variables.some(isUsedFromOutsideOf(scopeNode))
+                variables.some(isUsedFromOutsideOf(scopeNode)) ||
+                variables.some(hasNameDisallowedForLetDeclarations)
             ) {
                 return false;
             }


Test patch:
diff --git a/tests/lib/rules/no-var.js b/tests/lib/rules/no-var.js
index b47bf9097e9a..d7576bfe3ce5 100644
--- a/tests/lib/rules/no-var.js
+++ b/tests/lib/rules/no-var.js
@@ -307,6 +307,18 @@ ruleTester.run("no-var", rule, {
             parser: require.resolve("../../fixtures/parsers/typescript-parsers/declare-var"),
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: ["Unexpected var, use let or const instead."]
+        },
+
+        // https://github.com/eslint/eslint/issues/11830
+        {
+            code: "function foo() { var let; }",
+            output: null,
+            errors: ["Unexpected var, use let or const instead."]
+        },
+        {
+            code: "function foo() { var { let } = {}; }",
+            output: null,
+            errors: ["Unexpected var, use let or const instead."]
         }
     ]
 });