Repository: eslint/eslint (JavaScript)

Issue:
no-extra-parens options work inconsistently
<!--
    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:** v.6.8.0
* **Node Version:** v12.13.0
* **npm Version:** v6.12.0

**What parser (default, Babel-ESLint, etc.) are you using?**

default(espree)

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js

```

</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: -->
* The `enforceForArrowConditionals: false` option doesn't allow extra parentheses more than one pair. - [demo](https://eslint.org/demo#eyJ0ZXh0IjoiLyogZXNsaW50IG5vLWV4dHJhLXBhcmVuczogW1wiZXJyb3JcIiwgXCJhbGxcIiwgeyBcImVuZm9yY2VGb3JBcnJvd0NvbmRpdGlvbmFsc1wiOiBmYWxzZSB9XSAqL1xuXG5jb25zdCBiID0gYSA9PiAxID8gMiA6IDM7XG5jb25zdCBlID0gYyA9PiAoMSA/IDIgOiAzKTtcbmNvbnN0IGYgPSBjID0+ICgoKDEgPyAyIDogMykpKTsgLy8gVW5uZWNlc3NhcnkgcGFyZW50aGVzZXMgZXhwcmVzc2lvbi5cbmNvbnN0IGcgPSBjID0+ICgoKCgxID8gMiA6IDMpKSkpOyAvLyBVbm5lY2Vzc2FyeSBwYXJlbnRoZXNlcyBleHByZXNzaW9uLiIsIm9wdGlvbnMiOnsicGFyc2VyT3B0aW9ucyI6eyJlY21hVmVyc2lvbiI6MTEsInNvdXJjZVR5cGUiOiJzY3JpcHQiLCJlY21hRmVhdHVyZXMiOnsianN4Ijp0cnVlfX0sInJ1bGVzIjp7fSwiZW52Ijp7ImJyb3dzZXIiOnRydWV9fX0=)
```js
/* eslint no-extra-parens: ["error", "all", { "enforceForArrowConditionals": false }] */

const b = a => 1 ? 2 : 3;
const e = c => (1 ? 2 : 3);
const f = c => (((1 ? 2 : 3))); // Unnecessary parentheses expression.
const g = c => ((((1 ? 2 : 3)))); // Unnecessary parentheses expression.
```
 * On the other hand, the other options allow extra parentheses without limit. - [demo](https://eslint.org/demo#eyJ0ZXh0IjoiLyogZXNsaW50IG5vLWV4dHJhLXBhcmVuczogW1wiZXJyb3JcIiwgXCJhbGxcIiwge1xuXHRcImNvbmRpdGlvbmFsQXNzaWduXCI6IGZhbHNlLFxuXHRcIm5lc3RlZEJpbmFyeUV4cHJlc3Npb25zXCI6IGZhbHNlLFxuICAgICAgICBcImlnbm9yZUpTWFwiOiBcImFsbFwiLFxuICAgICAgICBcImVuZm9yY2VGb3JTZXF1ZW5jZUV4cHJlc3Npb25zXCI6IGZhbHNlLFxuICAgICAgICBcImVuZm9yY2VGb3JOZXdJbk1lbWJlckV4cHJlc3Npb25zXCI6IGZhbHNlXG59XSAqL1xuXG53aGlsZSAoKCgoZm9vID0gYmFyKCkpKSkpIHt9XG5cbnggPSBhIHx8ICgoKCgoYiAmJiBjKSkpKSk7XG5cbmNvbnN0IGJheiA9ICgoKCg8ZGl2Lz4pKSkpO1xuXG4oKChhLCBiKSkpO1xuKChhLCBiKSk7XG4oYSwgYik7XG5cbnZhciBmb28gPSAoKChuZXcgQmFyKCkpKSkuYmF6O1xudmFyIGZvbyA9ICgobmV3IEJhcigpKSkuYmF6O1xudmFyIGZvbyA9IChuZXcgQmFyKCkpLmJhejsiLCJvcHRpb25zIjp7InBhcnNlck9wdGlvbnMiOnsiZWNtYVZlcnNpb24iOjExLCJzb3VyY2VUeXBlIjoic2NyaXB0IiwiZWNtYUZlYXR1cmVzIjp7ImpzeCI6dHJ1ZX19LCJydWxlcyI6e30sImVudiI6eyJicm93c2VyIjp0cnVlfX19)
```js
/* eslint no-extra-parens: ["error", "all", {
	"conditionalAssign": false,
	"nestedBinaryExpressions": false,
        "ignoreJSX": "all",
        "enforceForSequenceExpressions": false,
        "enforceForNewInMemberExpressions": false
}] */

while ((((foo = bar())))) {}

x = a || (((((b && c)))));

const baz = ((((<div/>))));

(((a, b)));
((a, b));
(a, b);

var foo = (((new Bar()))).baz;
var foo = ((new Bar())).baz;
var foo = (new Bar()).baz;
```

**What did you expect to happen?**

I think the options should work consistently.
By the docs, the option `enforceForArrowConditionals: false` should allow all extra parentheses without limitation in matched cases with the option.
>...
"enforceForArrowConditionals": false allows extra parentheses around ternary expressions which are the body of an arrow function
...


**What actually happened? Please include the actual, raw output from ESLint.**

  

**Are you willing to submit a pull request to fix this bug?**

Yes. If this issue is a bug.  After #12697 merged.


Discussion:
This seems like a bug to me. Thanks for reporting this :+1:



Patch:
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index e70353576bb9..a5488c3c1c6a 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -702,8 +702,7 @@ module.exports = {
                 }
 
                 if (node.body.type === "ConditionalExpression" &&
-                    IGNORE_ARROW_CONDITIONALS &&
-                    !isParenthesisedTwice(node.body)
+                    IGNORE_ARROW_CONDITIONALS
                 ) {
                     return;
                 }


Test patch:
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index 8d20ba7cd1c0..657881fb2285 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -244,12 +244,19 @@ ruleTester.run("no-extra-parens", rule, {
 
         // "functions" enables reports for function nodes only
         { code: "(0)", options: ["functions"] },
+        { code: "((0))", options: ["functions"] },
         { code: "a + (b * c)", options: ["functions"] },
+        { code: "a + ((b * c))", options: ["functions"] },
         { code: "(a)(b)", options: ["functions"] },
+        { code: "((a))(b)", options: ["functions"] },
         { code: "a, (b = c)", options: ["functions"] },
+        { code: "a, ((b = c))", options: ["functions"] },
         { code: "for(a in (0));", options: ["functions"] },
+        { code: "for(a in ((0)));", options: ["functions"] },
         { code: "var a = (b = c)", options: ["functions"] },
+        { code: "var a = ((b = c))", options: ["functions"] },
         { code: "_ => (a = 0)", options: ["functions"] },
+        { code: "_ => ((a = 0))", options: ["functions"] },
 
         // ["all", { conditionalAssign: false }] enables extra parens around conditional assignments
         { code: "while ((foo = bar())) {}", options: ["all", { conditionalAssign: false }] },
@@ -257,6 +264,8 @@ ruleTester.run("no-extra-parens", rule, {
         { code: "do; while ((foo = bar()))", options: ["all", { conditionalAssign: false }] },
         { code: "for (;(a = b););", options: ["all", { conditionalAssign: false }] },
         { code: "var a = ((b = c)) ? foo : bar;", options: ["all", { conditionalAssign: false }] },
+        { code: "while (((foo = bar()))) {}", options: ["all", { conditionalAssign: false }] },
+        { code: "var a = (((b = c))) ? foo : bar;", options: ["all", { conditionalAssign: false }] },
 
         // ["all", { nestedBinaryExpressions: false }] enables extra parens around conditional assignments
         { code: "a + (b * c)", options: ["all", { nestedBinaryExpressions: false }] },
@@ -369,6 +378,7 @@ ruleTester.run("no-extra-parens", rule, {
 
         // ["all", { ignoreJSX: "all" }]
         { code: "const Component = (<div />)", options: ["all", { ignoreJSX: "all" }] },
+        { code: "const Component = ((<div />))", options: ["all", { ignoreJSX: "all" }] },
         {
             code: [
                 "const Component = (<>",
@@ -377,6 +387,14 @@ ruleTester.run("no-extra-parens", rule, {
             ].join("\n"),
             options: ["all", { ignoreJSX: "all" }]
         },
+        {
+            code: [
+                "const Component = ((<>",
+                "  <p />",
+                "</>));"
+            ].join("\n"),
+            options: ["all", { ignoreJSX: "all" }]
+        },
         {
             code: [
                 "const Component = (<div>",
@@ -403,6 +421,7 @@ ruleTester.run("no-extra-parens", rule, {
 
         // ["all", { ignoreJSX: "single-line" }]
         { code: "const Component = (<div />);", options: ["all", { ignoreJSX: "single-line" }] },
+        { code: "const Component = ((<div />));", options: ["all", { ignoreJSX: "single-line" }] },
         {
             code: [
                 "const Component = (",
@@ -430,6 +449,16 @@ ruleTester.run("no-extra-parens", rule, {
             ].join("\n"),
             options: ["all", { ignoreJSX: "multi-line" }]
         },
+        {
+            code: [
+                "const Component = ((",
+                "<div>",
+                "  <p />",
+                "</div>",
+                "));"
+            ].join("\n"),
+            options: ["all", { ignoreJSX: "multi-line" }]
+        },
         {
             code: [
                 "const Component = (<div>",
@@ -459,6 +488,7 @@ ruleTester.run("no-extra-parens", rule, {
         // ["all", { enforceForArrowConditionals: false }]
         { code: "var a = b => 1 ? 2 : 3", options: ["all", { enforceForArrowConditionals: false }] },
         { code: "var a = (b) => (1 ? 2 : 3)", options: ["all", { enforceForArrowConditionals: false }] },
+        { code: "var a = (b) => ((1 ? 2 : 3))", options: ["all", { enforceForArrowConditionals: false }] },
 
         // ["all", { enforceForSequenceExpressions: false }]
         { code: "(a, b)", options: ["all", { enforceForSequenceExpressions: false }] },
@@ -466,6 +496,7 @@ ruleTester.run("no-extra-parens", rule, {
         { code: "(foo(), bar());", options: ["all", { enforceForSequenceExpressions: false }] },
         { code: "((foo(), bar()));", options: ["all", { enforceForSequenceExpressions: false }] },
         { code: "if((a, b)){}", options: ["all", { enforceForSequenceExpressions: false }] },
+        { code: "if(((a, b))){}", options: ["all", { enforceForSequenceExpressions: false }] },
         { code: "while ((val = foo(), val < 10));", options: ["all", { enforceForSequenceExpressions: false }] },
 
         // ["all", { enforceForNewInMemberExpressions: false }]
@@ -1122,12 +1153,10 @@ ruleTester.run("no-extra-parens", rule, {
                 }
             ]
         },
-
-        // ["all", { enforceForArrowConditionals: false }]
         {
             code: "var a = (b) => ((1 ? 2 : 3))",
             output: "var a = (b) => (1 ? 2 : 3)",
-            options: ["all", { enforceForArrowConditionals: false }],
+            options: ["all", { enforceForArrowConditionals: true }],
             errors: [
                 {
                     messageId: "unexpected"