diff --git a/changelog_unreleased/javascript/pr-7500.md b/changelog_unreleased/javascript/pr-7500.md new file mode 100644 index 000000000..bc6540344 --- /dev/null +++ b/changelog_unreleased/javascript/pr-7500.md @@ -0,0 +1,16 @@ +#### Fix edge cases with parentheses around optional chaining ([#7500](https://github.com/prettier/prettier/pull/7500) by [@thorn0](https://github.com/thorn0)) + + +```jsx +// Input +(foo?.bar)(); +new (foo?.bar)(); + +// Prettier stable +foo?.bar(); +new foo?.bar(); + +// Prettier master +(foo?.bar)(); +new (foo?.bar)(); +``` diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js index 928883067..5b7563de5 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -676,7 +676,9 @@ function needsParens(path, options) { case "OptionalCallExpression": if ( (parent.type === "MemberExpression" && name === "object") || - (parent.type === "CallExpression" && name === "callee") + ((parent.type === "CallExpression" || + parent.type === "NewExpression") && + name === "callee") ) { return true; } diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 6569e8916..fbe98e72b 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -1194,7 +1194,11 @@ function printPathNoParens(path, options, print, args) { // We detect calls on member lookups and possibly print them in a // special chain format. See `printMemberChain` for more info. - if (!isNew && isMemberish(n.callee)) { + if ( + !isNew && + isMemberish(n.callee) && + !path.call(path => pathNeedsParens(path, options), "callee") + ) { return printMemberChain(path, options, print); } diff --git a/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap b/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap index 7a7db0754..3a1824c9b 100644 --- a/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap +++ b/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap @@ -46,6 +46,8 @@ a?.[b?.c]?.d(); (one?.fn()); (one?.two).fn(); +(one?.two)(); +(one?.two())(); (one.two?.fn()); (one.two?.three).fn(); (one.two?.three?.fn()); @@ -84,6 +86,10 @@ a = () => ({}?.b() && a); ({}?.a().b()); ({ a: 1 }?.entries()); +new (foo?.bar)(); +new (foo?.bar())(); +new (foo?.())(); + =====================================output===================================== var street = user.address?.street; var fooValue = myForm.querySelector("input[name=foo]")?.value; @@ -125,6 +131,8 @@ a?.[b?.c]?.d(); one?.fn(); (one?.two).fn(); +(one?.two)(); +(one?.two())(); one.two?.fn(); (one.two?.three).fn(); one.two?.three?.fn(); @@ -167,6 +175,10 @@ a = () => ({}?.b() && a); ({}?.a().b()); ({ a: 1 }?.entries()); +new (foo?.bar)(); +new (foo?.bar())(); +new (foo?.())(); + ================================================================================ `; diff --git a/tests/optional_chaining/chaining.js b/tests/optional_chaining/chaining.js index a169179b2..c6b673626 100644 --- a/tests/optional_chaining/chaining.js +++ b/tests/optional_chaining/chaining.js @@ -38,6 +38,8 @@ a?.[b?.c]?.d(); (one?.fn()); (one?.two).fn(); +(one?.two)(); +(one?.two())(); (one.two?.fn()); (one.two?.three).fn(); (one.two?.three?.fn()); @@ -75,3 +77,7 @@ a = () => ({}?.b() && a); (x) => ({}?.b.b); ({}?.a().b()); ({ a: 1 }?.entries()); + +new (foo?.bar)(); +new (foo?.bar())(); +new (foo?.())();