Repository: eslint/eslint (JavaScript)

Issue:
no-extra-parens false positive with await and exponentiation operator
<!--
    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**

<!--
    If you are using ESLint v6.5.0 or later, you can run ESLint with the `--env-info` flag and paste the output here.
-->

* **ESLint Version:** v6.8.0
* **Node Version:** v12.14.0
* **npm Version:** v6.13.4

**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: 2017,
    },
};
```

</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.**

[Online Demo Link](https://eslint.org/demo#eyJ0ZXh0IjoiLyogZXNsaW50IG5vLWV4dHJhLXBhcmVuczogXCJlcnJvclwiKi9cblxuYXN5bmMgZnVuY3Rpb24gZm9vICgpIHtcbiAgICAoYXdhaXQgYSkgKiogYjtcbn0iLCJvcHRpb25zIjp7InBhcnNlck9wdGlvbnMiOnsiZWNtYVZlcnNpb24iOjgsInNvdXJjZVR5cGUiOiJzY3JpcHQiLCJlY21hRmVhdHVyZXMiOnt9fSwicnVsZXMiOnt9LCJlbnYiOnt9fX0=)

<!-- Paste the source code below: -->
```js
/* eslint no-extra-parens: "error"*/

async function foo () {
    (await a) ** b;
}
```

<!-- Paste the command you used to run ESLint: -->
```bash
eslint index.js --fix
```

**What did you expect to happen?**

I believe no error.

By the spec,  [UnaryExpression](https://www.ecma-international.org/ecma-262/10.0/index.html#prod-UnaryExpression) can't be the left-hand side of an [ExponentiationExpression](https://www.ecma-international.org/ecma-262/10.0/index.html#prod-ExponentiationExpression).

`await a ** b` is parsing error for `espree`.

**What actually happened? Please include the actual, raw output from ESLint.**

Auto-fixed to:

```js
/* eslint no-extra-parens: "error"*/

async function foo () {
    await a ** b;
}
```

Message after the fix:

```
4:13  error  Parsing error: Unexpected token **
```

**Are you willing to submit a pull request to fix this bug?**

Yes. 

It would be good to verify is this really a bug in the rule, because `await a ** b` seems to be allowed in Chrome and Firefox. It's interpreted as `(await a) ** b`.






Discussion:
I'm also not sure what the correct behavior is here. It looks like Esprima and Babel's parser also allow for `await a ** b`, while TypeScript and Espree throw a parsing error.

Unfortunately, it looks like there wasn't enough interest from the team
or community to implement this change. While we wish we'd be able to
accommodate everyone's requests, we do need to prioritize. We've found
that issues failing to reach accepted status after 21 days tend to
never be accepted, and as such, we [close those issues](https://eslint.org/docs/maintainer-guide/issues#when-to-close-an-issue).
This doesn't mean the idea isn't interesting or useful, just that it's
not something the team can commit to.

Thanks for contributing to ESLint and we appreciate your understanding.

[//]: # (auto-close)

Related discussion:

https://github.com/tc39/ecma262/issues/2197
It was decided to leave the spec as-is:

https://github.com/tc39/notes/blob/master/meetings/2020-11/nov-16.md#handling-await-in-left-operands-of-exponentiation

meaning that `await a ** b` is a syntax error, so `no-extra-parens` should not report parens around `await a`.



Patch:
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 7afe7625dfcd..37f0d650bf9b 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -511,7 +511,7 @@ module.exports = {
 
             if (!shouldSkipLeft && hasExcessParens(node.left)) {
                 if (
-                    !(node.left.type === "UnaryExpression" && isExponentiation) &&
+                    !(["AwaitExpression", "UnaryExpression"].includes(node.left.type) && isExponentiation) &&
                     !astUtils.isMixedLogicalAndCoalesceExpressions(node.left, node) &&
                     (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation)) ||
                     isParenthesisedTwice(node.left)


Test patch:
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index c0d0387ec74d..6e979b3e382f 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -377,6 +377,9 @@ ruleTester.run("no-extra-parens", rule, {
         "async function a() { await (a + await b) }",
         "async function a() { (await a)() }",
         "async function a() { new (await a) }",
+        "async function a() { await (a ** b) }",
+        "async function a() { (await a) ** b }",
+
         { code: "(foo instanceof bar) instanceof baz", options: ["all", { nestedBinaryExpressions: false }] },
         { code: "(foo in bar) in baz", options: ["all", { nestedBinaryExpressions: false }] },
         { code: "(foo + bar) + baz", options: ["all", { nestedBinaryExpressions: false }] },
@@ -1187,6 +1190,8 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("async function a() { await (+a); }", "async function a() { await +a; }", "UnaryExpression", null),
         invalid("async function a() { +(await a); }", "async function a() { +await a; }", "AwaitExpression", null),
         invalid("async function a() { await ((a,b)); }", "async function a() { await (a,b); }", "SequenceExpression", null),
+        invalid("async function a() { a ** (await b); }", "async function a() { a ** await b; }", "AwaitExpression", null),
+
         invalid("(foo) instanceof bar", "foo instanceof bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
         invalid("(foo) in bar", "foo in bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
         invalid("(foo) + bar", "foo + bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),