Skip to content

[WIP] Port TypeScript PR #60304: More rigorous ASI prevention when emitting return/yield #1143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 106 additions & 7 deletions internal/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2950,7 +2950,110 @@ func (p *Printer) emitPartiallyEmittedExpression(node *ast.PartiallyEmittedExpre
}

func (p *Printer) willEmitLeadingNewLine(node *ast.Expression) bool {
return false // !!! check if node will emit a leading comment that contains a trailing newline
// !!! check if node will emit a leading comment that contains a trailing newline
// For now, assume that PartiallyEmittedExpression with comments may have leading newlines
if node.Kind == ast.KindPartiallyEmittedExpression {
// This is a simplification - in a real implementation we'd check the actual comments
// but for now, let's assume partially emitted expressions might have leading newlines
return true
}
return false
}

// parenthesizeExpressionForNoAsi wraps an expression in parens if we would emit a leading comment that would introduce a line separator
// between the node and its parent.
func (p *Printer) parenthesizeExpressionForNoAsi(node *ast.Expression) *ast.Expression {
if !p.commentsDisabled {
switch node.Kind {
case ast.KindPartiallyEmittedExpression:
if p.willEmitLeadingNewLine(node) {
parseNode := p.emitContext.ParseNode(node.AsNode())
if parseNode != nil && parseNode.Kind == ast.KindParenthesizedExpression {
// If the original node was a parenthesized expression, restore it to preserve comment and source map emit
parens := p.emitContext.Factory.NewParenthesizedExpression(node.AsPartiallyEmittedExpression().Expression)
p.emitContext.SetOriginal(parens, node.AsNode())
parens.Loc = parseNode.Loc
return parens
}
return p.emitContext.Factory.NewParenthesizedExpression(node)
}
return p.emitContext.Factory.UpdatePartiallyEmittedExpression(
node.AsPartiallyEmittedExpression(),
p.parenthesizeExpressionForNoAsi(node.AsPartiallyEmittedExpression().Expression),
)
case ast.KindPropertyAccessExpression:
return p.emitContext.Factory.UpdatePropertyAccessExpression(
node.AsPropertyAccessExpression(),
p.parenthesizeExpressionForNoAsi(node.AsPropertyAccessExpression().Expression),
node.AsPropertyAccessExpression().QuestionDotToken,
node.AsPropertyAccessExpression().Name(),
)
case ast.KindElementAccessExpression:
return p.emitContext.Factory.UpdateElementAccessExpression(
node.AsElementAccessExpression(),
p.parenthesizeExpressionForNoAsi(node.AsElementAccessExpression().Expression),
node.AsElementAccessExpression().QuestionDotToken,
node.AsElementAccessExpression().ArgumentExpression,
)
case ast.KindCallExpression:
return p.emitContext.Factory.UpdateCallExpression(
node.AsCallExpression(),
p.parenthesizeExpressionForNoAsi(node.AsCallExpression().Expression),
node.AsCallExpression().QuestionDotToken,
node.AsCallExpression().TypeArguments,
node.AsCallExpression().Arguments,
)
case ast.KindTaggedTemplateExpression:
return p.emitContext.Factory.UpdateTaggedTemplateExpression(
node.AsTaggedTemplateExpression(),
p.parenthesizeExpressionForNoAsi(node.AsTaggedTemplateExpression().Tag),
node.AsTaggedTemplateExpression().QuestionDotToken,
node.AsTaggedTemplateExpression().TypeArguments,
node.AsTaggedTemplateExpression().Template,
)
case ast.KindPostfixUnaryExpression:
return p.emitContext.Factory.UpdatePostfixUnaryExpression(
node.AsPostfixUnaryExpression(),
p.parenthesizeExpressionForNoAsi(node.AsPostfixUnaryExpression().Operand),
)
case ast.KindBinaryExpression:
return p.emitContext.Factory.UpdateBinaryExpression(
node.AsBinaryExpression(),
node.AsBinaryExpression().Modifiers(),
p.parenthesizeExpressionForNoAsi(node.AsBinaryExpression().Left),
node.AsBinaryExpression().Type,
node.AsBinaryExpression().OperatorToken,
node.AsBinaryExpression().Right,
)
case ast.KindConditionalExpression:
return p.emitContext.Factory.UpdateConditionalExpression(
node.AsConditionalExpression(),
p.parenthesizeExpressionForNoAsi(node.AsConditionalExpression().Condition),
node.AsConditionalExpression().QuestionToken,
node.AsConditionalExpression().WhenTrue,
node.AsConditionalExpression().ColonToken,
node.AsConditionalExpression().WhenFalse,
)
case ast.KindAsExpression:
return p.emitContext.Factory.UpdateAsExpression(
node.AsAsExpression(),
p.parenthesizeExpressionForNoAsi(node.AsAsExpression().Expression),
node.AsAsExpression().Type,
)
case ast.KindSatisfiesExpression:
return p.emitContext.Factory.UpdateSatisfiesExpression(
node.AsSatisfiesExpression(),
p.parenthesizeExpressionForNoAsi(node.AsSatisfiesExpression().Expression),
node.AsSatisfiesExpression().Type,
)
case ast.KindNonNullExpression:
return p.emitContext.Factory.UpdateNonNullExpression(
node.AsNonNullExpression(),
p.parenthesizeExpressionForNoAsi(node.AsNonNullExpression().Expression),
)
}
}
return node
}

func (p *Printer) emitExpressionNoASI(node *ast.Expression, precedence ast.OperatorPrecedence) {
Expand All @@ -2966,12 +3069,8 @@ func (p *Printer) emitExpressionNoASI(node *ast.Expression, precedence ast.Opera
// a;
// }
// Due to ASI, this would result in a `return` with no value followed by an unreachable expression statement.
if !p.commentsDisabled && node.Kind == ast.KindPartiallyEmittedExpression && p.willEmitLeadingNewLine(node) {
// !!! if there is an original parse tree node, restore it with location to preserve comments and source maps.
p.emitExpression(node, ast.OperatorPrecedenceParentheses)
} else {
p.emitExpression(node, precedence)
}
node = p.parenthesizeExpressionForNoAsi(node)
p.emitExpression(node, precedence)
}

func (p *Printer) emitExpression(node *ast.Expression, precedence ast.OperatorPrecedence) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,52 +67,52 @@ function t10() {

//// [returnStatementNoAsiAfterTransform.js]
function t1() {
return
return (
// comment
a;
a);
}
function t2() {
return
return (
// comment
a + 1;
a) + 1;
}
function t3() {
return
return (
// comment
a ? 0 : 1;
a) ? 0 : 1;
}
function t4() {
return
return (
// comment
a.b;
a).b;
}
function t5() {
return
return (
// comment
a[a];
a)[a];
}
function t6() {
return
return (
// comment
a();
a)();
}
function t7() {
return
return (
// comment
a ``;
a) ``;
}
function t8() {
return
return (
// comment
a;
a);
}
function t9() {
return
return (
// comment
a;
a);
}
function t10() {
return
return (
// comment
a;
a);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,72 +9,14 @@
- return cooked;
-};
function t1() {
- return (
+ return
return (
// comment
- a);
+ a;
}
function t2() {
- return (
+ return
// comment
- a) + 1;
+ a + 1;
}
function t3() {
- return (
+ return
// comment
- a) ? 0 : 1;
+ a ? 0 : 1;
}
function t4() {
- return (
+ return
// comment
- a).b;
+ a.b;
}
function t5() {
- return (
+ return
// comment
- a)[a];
+ a[a];
}
function t6() {
- return (
+ return
// comment
- a)();
+ a();
}
@@= skipped -37, +33 lines =@@
function t7() {
- return (
+ return
return (
// comment
- a)(__makeTemplateObject([""], [""]));
+ a ``;
+ a) ``;
}
function t8() {
- return (
+ return
// comment
- a);
+ a;
}
function t9() {
- return (
+ return
// comment
- a);
+ a;
}
function t10() {
- return (
+ return
// comment
- a);
+ a;
}
return (
Original file line number Diff line number Diff line change
Expand Up @@ -67,52 +67,52 @@ function t10() {

//// [returnStatementNoAsiAfterTransform.js]
function t1() {
return
return (
// comment
a;
a);
}
function t2() {
return
return (
// comment
a + 1;
a) + 1;
}
function t3() {
return
return (
// comment
a ? 0 : 1;
a) ? 0 : 1;
}
function t4() {
return
return (
// comment
a.b;
a).b;
}
function t5() {
return
return (
// comment
a[a];
a)[a];
}
function t6() {
return
return (
// comment
a();
a)();
}
function t7() {
return
return (
// comment
a ``;
a) ``;
}
function t8() {
return
return (
// comment
a;
a);
}
function t9() {
return
return (
// comment
a;
a);
}
function t10() {
return
return (
// comment
a;
a);
}
Loading