diff --git a/changelog_unreleased/javascript/19913.md b/changelog_unreleased/javascript/19913.md new file mode 100644 index 000000000..9da5bba7c --- /dev/null +++ b/changelog_unreleased/javascript/19913.md @@ -0,0 +1,24 @@ +#### Keep necessary parentheses in `yield` expression (#19913 by @giaBaoJS) + + +```jsx +// Input +function* f() { + yield (/* + */ a); +} + +// Prettier stable +function* f() { + yield /* + */ a; +} + +// Prettier main +function* f() { + yield ( + /* + */ a + ); +} +``` diff --git a/src/language-js/parentheses/parent-needs-parentheses.js b/src/language-js/parentheses/parent-needs-parentheses.js index fb42c5ca3..d3037388f 100644 --- a/src/language-js/parentheses/parent-needs-parentheses.js +++ b/src/language-js/parentheses/parent-needs-parentheses.js @@ -27,7 +27,8 @@ function parentNeedsParentheses(path, options, needsParentheses) { switch (parent.type) { case "ReturnStatement": case "ThrowStatement": - if (willReturnOrThrowStatementBreak(path, options)) { + case "YieldExpression": + if (willArgumentBreakAndAddParentheses(path, options)) { return false; } break; @@ -128,14 +129,17 @@ function parentNeedsParentheses(path, options, needsParentheses) { } } -function willReturnOrThrowStatementBreak(path, options) { +function willArgumentBreakAndAddParentheses(path, options) { const { key, parent } = path; - if (!(key === "argument" && isReturnOrThrowStatement(parent))) { + if (!( + key === "argument" && + (isReturnOrThrowStatement(parent) || parent.type === "YieldExpression") + )) { return false; } /* - When `ReturnStatement` or `ThrowStatement` breaks, parentheses will be added around it's argument. + When `ReturnStatement`, `ThrowStatement`, or `YieldExpression` breaks, parentheses will be added around it's argument. So don't need add parentheses again. But we can't know how the argument printed, so only matches cases that will break for sure */ diff --git a/src/language-js/print/estree.js b/src/language-js/print/estree.js index e0bfd9986..08dcc52e6 100644 --- a/src/language-js/print/estree.js +++ b/src/language-js/print/estree.js @@ -57,6 +57,7 @@ import { printRestElement, printSpreadElement } from "./rest-element.js"; import { printReturnStatement, printThrowStatement, + printYieldExpression, } from "./return-statement.js"; import { printSequenceExpression } from "./sequence-expression.js"; import { printSwitchCase, printSwitchStatement } from "./switch-statement.js"; @@ -156,10 +157,8 @@ function printEstree(path, options, print, args) { case "ArrowFunctionExpression": return printArrowFunction(path, options, print, args); case "YieldExpression": - return [ - `yield${node.delegate ? "*" : ""}`, - node.argument ? [" ", print("argument")] : "", - ]; + return printYieldExpression(path, options, print); + case "AwaitExpression": return printAwaitExpression(path, options, print); diff --git a/src/language-js/print/return-statement.js b/src/language-js/print/return-statement.js index 660dd4f4e..242d6906c 100644 --- a/src/language-js/print/return-statement.js +++ b/src/language-js/print/return-statement.js @@ -13,7 +13,7 @@ import { printSemicolon } from "./miscellaneous.js"; @import {Doc} from "../../document/index.js"; */ -function printReturnOrThrowArgument(path, options, print) { +function printArgument(path, options, print) { const { node } = path; const argumentDoc = print(); @@ -45,19 +45,24 @@ function printReturnOrThrowStatement(path, options, print) { return [ node.type === "ThrowStatement" ? "throw" : "return", node.argument - ? [ - " ", - path.call( - () => printReturnOrThrowArgument(path, options, print), - "argument", - ), - ] + ? [" ", path.call(() => printArgument(path, options, print), "argument")] : "", printSemicolon(options), ]; } +function printYieldExpression(path, options, print) { + const { node } = path; + return [ + `yield${node.delegate ? "*" : ""}`, + node.argument + ? [" ", path.call(() => printArgument(path, options, print), "argument")] + : "", + ]; +} + export { printReturnOrThrowStatement as printReturnStatement, printReturnOrThrowStatement as printThrowStatement, + printYieldExpression, }; diff --git a/tests/format/js/yield/__snapshots__/format.test.js.snap b/tests/format/js/yield/__snapshots__/format.test.js.snap index 53d408139..6bd1e1f42 100644 --- a/tests/format/js/yield/__snapshots__/format.test.js.snap +++ b/tests/format/js/yield/__snapshots__/format.test.js.snap @@ -21,6 +21,91 @@ function* f() { ================================================================================ `; +exports[`comment.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "typescript"] + printWidth: 80 (default) | +=====================================input====================================== +function* f() { + // Line terminators are not allowed between \`yield\` and its argument, so the + // parentheses must be kept. + yield (/* + */ a); + + foo( + yield (/* + */ a) + ); + + yield (// comment + a); + + yield (/* + */ a + b); + + yield (/* + */ a, b); + + yield (/* comment */ + a); + + // Safe, the comment is printed on one line. + yield (/* comment */ a); + + // \`yield*\` is not restricted, but it is printed the same way. + yield* (/* + */ a); +} + +=====================================output===================================== +function* f() { + // Line terminators are not allowed between \`yield\` and its argument, so the + // parentheses must be kept. + yield ( + /* + */ a + ); + + foo( + yield ( + /* + */ a + ), + ); + + yield ( + // comment + a + ); + + yield ( + /* + */ a + b + ); + + yield ( + /* + */ a, b + ); + + yield ( + /* comment */ + a + ); + + // Safe, the comment is printed on one line. + yield /* comment */ a; + + // \`yield*\` is not restricted, but it is printed the same way. + yield* ( + /* + */ a + ); +} + +================================================================================ +`; + exports[`conditional.js format 1`] = ` ====================================options===================================== parsers: ["babel", "typescript"] diff --git a/tests/format/js/yield/comment.js b/tests/format/js/yield/comment.js new file mode 100644 index 000000000..a789a211f --- /dev/null +++ b/tests/format/js/yield/comment.js @@ -0,0 +1,30 @@ +function* f() { + // Line terminators are not allowed between `yield` and its argument, so the + // parentheses must be kept. + yield (/* + */ a); + + foo( + yield (/* + */ a) + ); + + yield (// comment + a); + + yield (/* + */ a + b); + + yield (/* + */ a, b); + + yield (/* comment */ + a); + + // Safe, the comment is printed on one line. + yield (/* comment */ a); + + // `yield*` is not restricted, but it is printed the same way. + yield* (/* + */ a); +}