diff --git a/changelog_unreleased/javascript/18720.md b/changelog_unreleased/javascript/18720.md new file mode 100644 index 000000000..d94d86b3d --- /dev/null +++ b/changelog_unreleased/javascript/18720.md @@ -0,0 +1,13 @@ +#### Fix missing parentheses around optional chaining (#18720 by @fisker) + + +```jsx +// Input +(a?.b()).c(); + +// Prettier stable +a?.b().c(); + +// Prettier main +(a?.b()).c(); +``` diff --git a/src/language-js/parentheses/needs-parentheses.js b/src/language-js/parentheses/needs-parentheses.js index 4e42d44b5..503524e83 100644 --- a/src/language-js/parentheses/needs-parentheses.js +++ b/src/language-js/parentheses/needs-parentheses.js @@ -848,7 +848,6 @@ function needsParentheses(path, options) { while (object) { switch (object.type) { case "CallExpression": - case "OptionalCallExpression": return true; case "MemberExpression": case "OptionalMemberExpression": diff --git a/src/language-js/print/member-chain.js b/src/language-js/print/member-chain.js index 26b0d2eae..6b132b370 100644 --- a/src/language-js/print/member-chain.js +++ b/src/language-js/print/member-chain.js @@ -93,7 +93,8 @@ function printMemberChain(path, options, print) { if ( isCallExpression(node) && - (isMemberish(node.callee) || isCallExpression(node.callee)) + (isMemberish(node.callee) || isCallExpression(node.callee)) && + !needsParentheses(path, options) ) { const hasTrailingEmptyLine = shouldInsertEmptyLineAfter(node); printedNodes.unshift({ @@ -113,10 +114,9 @@ function printMemberChain(path, options, print) { ], }); path.call(rec, "callee"); - } else if (isMemberish(node)) { + } else if (isMemberish(node) && !needsParentheses(path, options)) { printedNodes.unshift({ node, - needsParens: needsParentheses(path, options), printed: printComments( path, isMemberExpression(node) @@ -126,10 +126,12 @@ function printMemberChain(path, options, print) { ), }); path.call(rec, "object"); - } else if (node.type === "TSNonNullExpression") { + } else if ( + node.type === "TSNonNullExpression" && + !needsParentheses(path, options) + ) { printedNodes.unshift({ node, - needsParens: needsParentheses(path, options), printed: printComments(path, "!", options), }); path.call(rec, "expression"); diff --git a/tests/format/js/chain-expression/__snapshots__/format.test.js.snap b/tests/format/js/chain-expression/__snapshots__/format.test.js.snap index 8454b9d8b..4f8c17ed4 100644 --- a/tests/format/js/chain-expression/__snapshots__/format.test.js.snap +++ b/tests/format/js/chain-expression/__snapshots__/format.test.js.snap @@ -30,7 +30,7 @@ foo(a?.b); // In optional chaining (a?.b())?.(); -// FIXME: (a?.b()).c?.(); +(a?.b()).c?.(); =====================================output===================================== // Member expressions @@ -57,7 +57,7 @@ foo(a?.b); // In optional chaining a?.b()?.(); -// FIXME: (a?.b()).c?.(); +(a?.b()).c?.(); ================================================================================ `; @@ -264,7 +264,7 @@ new (a[b?.c]) (); new ((a?.b).c) (); new (a[b?.()]) (); new ((a?.b).c) (); -// FIXME: new ((a?.()).b) (); +new ((a?.()).b) (); // Call expressions new (a?.()) (); @@ -290,7 +290,7 @@ new a[b?.c](); new (a?.b).c(); new a[b?.()](); new (a?.b).c(); -// FIXME: new ((a?.()).b) (); +new (a?.()).b(); // Call expressions new (a?.())(); diff --git a/tests/format/js/chain-expression/call-expression.js b/tests/format/js/chain-expression/call-expression.js index 055a9ea26..6d9902959 100644 --- a/tests/format/js/chain-expression/call-expression.js +++ b/tests/format/js/chain-expression/call-expression.js @@ -22,4 +22,4 @@ foo(a?.b); // In optional chaining (a?.b())?.(); -// FIXME: (a?.b()).c?.(); +(a?.b()).c?.(); diff --git a/tests/format/js/chain-expression/new-expression.js b/tests/format/js/chain-expression/new-expression.js index 8a5d38dcd..416801bf6 100644 --- a/tests/format/js/chain-expression/new-expression.js +++ b/tests/format/js/chain-expression/new-expression.js @@ -6,7 +6,7 @@ new (a[b?.c]) (); new ((a?.b).c) (); new (a[b?.()]) (); new ((a?.b).c) (); -// FIXME: new ((a?.()).b) (); +new ((a?.()).b) (); // Call expressions new (a?.()) (); diff --git a/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap b/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap index 7286488bb..114f54757 100644 --- a/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap +++ b/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap @@ -24,7 +24,7 @@ printWidth: 80 (a[b!?.c]) (); ((a?.b).c)! (); ((a?.b).c!) (); -// FIXME: ((a?.b!).c) (); +((a?.b!).c) (); ((a!?.b).c) (); (a[b?.()])! (); (a[b?.()]!) (); @@ -33,8 +33,8 @@ printWidth: 80 (a![b?.()]) (); ((a?.b).c)! (); ((a?.b).c!) (); -// FIXME: ((a?.b)!.c) (); -// FIXME: ((a?.b!).c) (); +((a?.b)!.c) (); +((a?.b!).c) (); ((a!?.b).c) (); ((a?.()).b)! (); ((a?.()).b!) (); @@ -75,7 +75,7 @@ foo((a?.b)!); (a?.b()!)?.(); (a?.b())!?.(); (a?.b!()).c?.(); -// FIXME: (a?.b())!.c?.(); +(a?.b())!.c?.(); =====================================output===================================== // Member expressions @@ -96,7 +96,7 @@ a[b?.c!](); a[b!?.c](); (a?.b).c!(); (a?.b).c!(); -// FIXME: ((a?.b!).c) (); +(a?.b!).c(); (a!?.b).c(); a[b?.()]!(); a[b?.()]!(); @@ -105,8 +105,8 @@ a[b!?.()](); a![b?.()](); (a?.b).c!(); (a?.b).c!(); -// FIXME: ((a?.b)!.c) (); -// FIXME: ((a?.b!).c) (); +(a?.b)!.c(); +(a?.b!).c(); (a!?.b).c(); (a?.()).b!(); (a?.()).b!(); @@ -147,7 +147,7 @@ foo((a?.b)!); a?.b()!?.(); (a?.b())!?.(); (a?.b!()).c?.(); -// FIXME: (a?.b())!.c?.(); +(a?.b())!.c?.(); ================================================================================ `; @@ -385,15 +385,15 @@ printWidth: 80 | printWidth =====================================input====================================== // Member expressions -// FIXME: new (a?.b)! (); -// FIXME: new (a?.b!) (); +new (a?.b)! (); +new (a?.b!) (); new (a!?.b) (); -// FIXME: new (a.b?.c)! (); -// FIXME: new (a.b?.c!) (); +new (a.b?.c)! (); +new (a.b?.c!) (); new (a.b!?.c) (); new (a!.b?.c) (); -// FIXME: new (a?.b.c)! (); -// FIXME: new (a?.b.c!) (); +new (a?.b.c)! (); +new (a?.b.c!) (); new (a?.b!.c) (); new (a!?.b.c) (); new (a[b?.c])! (); @@ -414,24 +414,24 @@ new ((a?.b).c!) (); new ((a?.b)!.c) (); new ((a?.b!).c) (); new ((a!?.b).c) (); -// FIXME: new ((a?.()).b)! (); -// FIXME: new ((a?.()).b!) (); -// FIXME: new ((a?.())!.b) (); -// FIXME: new ((a?.()!).b) (); -// FIXME: new ((a!?.()).b) (); +new ((a?.()).b)! (); +new ((a?.()).b!) (); +new ((a?.())!.b) (); +new ((a?.()!).b) (); +new ((a!?.()).b) (); // Call expressions -// FIXME: new (a?.())! (); -// FIXME: new (a?.()!) (); +new (a?.())! (); +new (a?.()!) (); new (a!?.()) (); -// FIXME: new (a.b.c?.())! (); -// FIXME: new (a.b.c?.()!) (); +new (a.b.c?.())! (); +new (a.b.c?.()!) (); new (a.b.c!?.()) (); -// FIXME: new (a.b?.c())! (); -// FIXME: new (a.b?.c()!) (); +new (a.b?.c())! (); +new (a.b?.c()!) (); new (a.b!?.c()) (); -// FIXME: new (a?.b.c())! (); -// FIXME: new (a?.b.c()!) (); +new (a?.b.c())! (); +new (a?.b.c()!) (); new (a?.b!.c()) (); new (a(b?.c))! (); new (a(b?.c)!) (); @@ -455,15 +455,15 @@ new ((a?.b)!?.c)(); =====================================output===================================== // Member expressions -// FIXME: new (a?.b)! (); -// FIXME: new (a?.b!) (); +new (a?.b)!(); +new (a?.b!)(); new (a!?.b)(); -// FIXME: new (a.b?.c)! (); -// FIXME: new (a.b?.c!) (); +new (a.b?.c)!(); +new (a.b?.c!)(); new (a.b!?.c)(); new (a!.b?.c)(); -// FIXME: new (a?.b.c)! (); -// FIXME: new (a?.b.c!) (); +new (a?.b.c)!(); +new (a?.b.c!)(); new (a?.b!.c)(); new (a!?.b.c)(); new a[b?.c]!(); @@ -484,24 +484,24 @@ new (a?.b).c!(); new (a?.b)!.c(); new (a?.b!).c(); new (a!?.b).c(); -// FIXME: new ((a?.()).b)! (); -// FIXME: new ((a?.()).b!) (); -// FIXME: new ((a?.())!.b) (); -// FIXME: new ((a?.()!).b) (); -// FIXME: new ((a!?.()).b) (); +new (a?.()).b!(); +new (a?.()).b!(); +new (a?.())!.b(); +new (a?.()!).b(); +new (a!?.()).b(); // Call expressions -// FIXME: new (a?.())! (); -// FIXME: new (a?.()!) (); +new (a?.())!(); +new (a?.()!)(); new (a!?.())(); -// FIXME: new (a.b.c?.())! (); -// FIXME: new (a.b.c?.()!) (); +new (a.b.c?.())!(); +new (a.b.c?.()!)(); new (a.b.c!?.())(); -// FIXME: new (a.b?.c())! (); -// FIXME: new (a.b?.c()!) (); +new (a.b?.c())!(); +new (a.b?.c()!)(); new (a.b!?.c())(); -// FIXME: new (a?.b.c())! (); -// FIXME: new (a?.b.c()!) (); +new (a?.b.c())!(); +new (a?.b.c()!)(); new (a?.b!.c())(); new (a(b?.c)!)(); new (a(b?.c)!)(); diff --git a/tests/format/typescript/chain-expression/call-expression.ts b/tests/format/typescript/chain-expression/call-expression.ts index 631d22257..a74a477cb 100644 --- a/tests/format/typescript/chain-expression/call-expression.ts +++ b/tests/format/typescript/chain-expression/call-expression.ts @@ -16,7 +16,7 @@ (a[b!?.c]) (); ((a?.b).c)! (); ((a?.b).c!) (); -// FIXME: ((a?.b!).c) (); +((a?.b!).c) (); ((a!?.b).c) (); (a[b?.()])! (); (a[b?.()]!) (); @@ -25,8 +25,8 @@ (a![b?.()]) (); ((a?.b).c)! (); ((a?.b).c!) (); -// FIXME: ((a?.b)!.c) (); -// FIXME: ((a?.b!).c) (); +((a?.b)!.c) (); +((a?.b!).c) (); ((a!?.b).c) (); ((a?.()).b)! (); ((a?.()).b!) (); @@ -67,4 +67,4 @@ foo((a?.b)!); (a?.b()!)?.(); (a?.b())!?.(); (a?.b!()).c?.(); -// FIXME: (a?.b())!.c?.(); +(a?.b())!.c?.(); diff --git a/tests/format/typescript/chain-expression/new-expression.ts b/tests/format/typescript/chain-expression/new-expression.ts index b38364646..275c4735e 100644 --- a/tests/format/typescript/chain-expression/new-expression.ts +++ b/tests/format/typescript/chain-expression/new-expression.ts @@ -1,13 +1,13 @@ // Member expressions -// FIXME: new (a?.b)! (); -// FIXME: new (a?.b!) (); +new (a?.b)! (); +new (a?.b!) (); new (a!?.b) (); -// FIXME: new (a.b?.c)! (); -// FIXME: new (a.b?.c!) (); +new (a.b?.c)! (); +new (a.b?.c!) (); new (a.b!?.c) (); new (a!.b?.c) (); -// FIXME: new (a?.b.c)! (); -// FIXME: new (a?.b.c!) (); +new (a?.b.c)! (); +new (a?.b.c!) (); new (a?.b!.c) (); new (a!?.b.c) (); new (a[b?.c])! (); @@ -28,24 +28,24 @@ new ((a?.b).c!) (); new ((a?.b)!.c) (); new ((a?.b!).c) (); new ((a!?.b).c) (); -// FIXME: new ((a?.()).b)! (); -// FIXME: new ((a?.()).b!) (); -// FIXME: new ((a?.())!.b) (); -// FIXME: new ((a?.()!).b) (); -// FIXME: new ((a!?.()).b) (); +new ((a?.()).b)! (); +new ((a?.()).b!) (); +new ((a?.())!.b) (); +new ((a?.()!).b) (); +new ((a!?.()).b) (); // Call expressions -// FIXME: new (a?.())! (); -// FIXME: new (a?.()!) (); +new (a?.())! (); +new (a?.()!) (); new (a!?.()) (); -// FIXME: new (a.b.c?.())! (); -// FIXME: new (a.b.c?.()!) (); +new (a.b.c?.())! (); +new (a.b.c?.()!) (); new (a.b.c!?.()) (); -// FIXME: new (a.b?.c())! (); -// FIXME: new (a.b?.c()!) (); +new (a.b?.c())! (); +new (a.b?.c()!) (); new (a.b!?.c()) (); -// FIXME: new (a?.b.c())! (); -// FIXME: new (a?.b.c()!) (); +new (a?.b.c())! (); +new (a?.b.c()!) (); new (a?.b!.c()) (); new (a(b?.c))! (); new (a(b?.c)!) ();