diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 3b7624d02177..938516c56cfe 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -84,3 +84,22 @@ Examples: // Output (Prettier master) ``` + +- JavaScript: Fix parens logic for optional chaining expressions and closure type casts ([#5843] by [@yangsu]) + + Logic introduced in #4542 will print parens in the wrong places and produce invalid code for optional chaining expressions (with more than 2 nodes) or closure type casts that end in function calls. + + + ```js + // Input + (a?.b[c]).c(); + let value = /** @type {string} */ (this.members[0]).functionCall(); + + // Output (Prettier stable) + a(?.b[c]).c(); + let value = /** @type {string} */ this(.members[0]).functionCall(); + + // Output (Prettier master) + (a?.b[c]).c(); + let value = /** @type {string} */ (this.members[0]).functionCall(); + ``` diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 97f34aec8176..7fd02f191079 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -4926,23 +4926,16 @@ function printMemberChain(path, options, print) { groups.length >= 2 && !groups[1][0].node.comments && shouldNotWrap(groups); function printGroup(printedGroup) { - const result = []; - for (let i = 0; i < printedGroup.length; i++) { - // Checks if the next node (i.e. the parent node) needs parens - // and print accordingly - if (printedGroup[i + 1] && printedGroup[i + 1].needsParens) { - result.push( - "(", - printedGroup[i].printed, - printedGroup[i + 1].printed, - ")" - ); - i++; - } else { - result.push(printedGroup[i].printed); - } + const printed = printedGroup.map(tuple => tuple.printed); + // Checks if the last node (i.e. the parent node) needs parens and print + // accordingly + if ( + printedGroup.length > 0 && + printedGroup[printedGroup.length - 1].needsParens + ) { + return concat(["(", ...printed, ")"]); } - return concat(result); + return concat(printed); } function printIndentedGroup(groups) { diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap index 20bb0a2472c6..dfe76f942040 100644 --- a/tests/comments/__snapshots__/jsfmt.spec.js.snap +++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap @@ -339,6 +339,7 @@ let object = { // preserve parens only for type casts let assignment = /** @type {string} */ (getValue()); +let value = /** @type {string} */ (this.members[0]).functionCall(); functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); @@ -389,6 +390,7 @@ let object = { // preserve parens only for type casts let assignment = /** @type {string} */ (getValue()); +let value = /** @type {string} */ (this.members[0]).functionCall(); functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); diff --git a/tests/comments/closure-compiler-type-cast.js b/tests/comments/closure-compiler-type-cast.js index 647b2ca2e9c8..8fce7d6f21c7 100644 --- a/tests/comments/closure-compiler-type-cast.js +++ b/tests/comments/closure-compiler-type-cast.js @@ -8,6 +8,7 @@ let object = { // preserve parens only for type casts let assignment = /** @type {string} */ (getValue()); +let value = /** @type {string} */ (this.members[0]).functionCall(); functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); diff --git a/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap b/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap index 58b70e4f9834..c8b7782e10c1 100644 --- a/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap +++ b/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap @@ -25,6 +25,7 @@ delete a?.b; a?.b[3].c?.(x).d.e?.f[3].g?.(y).h; (a?.b).c(); +(a?.b[c]).c(); (a?.b)?.c.d?.e; (a ? b : c)?.d; @@ -52,6 +53,7 @@ delete a?.b; a?.b[3].c?.(x).d.e?.f[3].g?.(y).h; (a?.b).c(); +(a?.b[c]).c(); a?.b?.c.d?.e; (a ? b : c)?.d; diff --git a/tests/optional_chaining/chaining.js b/tests/optional_chaining/chaining.js index 58e3a59ad192..33b4f0e00735 100644 --- a/tests/optional_chaining/chaining.js +++ b/tests/optional_chaining/chaining.js @@ -17,6 +17,7 @@ delete a?.b; a?.b[3].c?.(x).d.e?.f[3].g?.(y).h; (a?.b).c(); +(a?.b[c]).c(); (a?.b)?.c.d?.e; (a ? b : c)?.d;