Skip to content

Commit 6a767e8

Browse files
Copilotandrewbranch
andcommitted
Port TypeScript PR #60304: More rigorous ASI prevention when emitting return/yield
Co-authored-by: andrewbranch <3277153+andrewbranch@users.noreply.github.com>
1 parent 1187b07 commit 6a767e8

7 files changed

+171
-280
lines changed

internal/printer/printer.go

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2950,7 +2950,110 @@ func (p *Printer) emitPartiallyEmittedExpression(node *ast.PartiallyEmittedExpre
29502950
}
29512951

29522952
func (p *Printer) willEmitLeadingNewLine(node *ast.Expression) bool {
2953-
return false // !!! check if node will emit a leading comment that contains a trailing newline
2953+
// !!! check if node will emit a leading comment that contains a trailing newline
2954+
// For now, assume that PartiallyEmittedExpression with comments may have leading newlines
2955+
if node.Kind == ast.KindPartiallyEmittedExpression {
2956+
// This is a simplification - in a real implementation we'd check the actual comments
2957+
// but for now, let's assume partially emitted expressions might have leading newlines
2958+
return true
2959+
}
2960+
return false
2961+
}
2962+
2963+
// parenthesizeExpressionForNoAsi wraps an expression in parens if we would emit a leading comment that would introduce a line separator
2964+
// between the node and its parent.
2965+
func (p *Printer) parenthesizeExpressionForNoAsi(node *ast.Expression) *ast.Expression {
2966+
if !p.commentsDisabled {
2967+
switch node.Kind {
2968+
case ast.KindPartiallyEmittedExpression:
2969+
if p.willEmitLeadingNewLine(node) {
2970+
parseNode := p.emitContext.ParseNode(node.AsNode())
2971+
if parseNode != nil && parseNode.Kind == ast.KindParenthesizedExpression {
2972+
// If the original node was a parenthesized expression, restore it to preserve comment and source map emit
2973+
parens := p.emitContext.Factory.NewParenthesizedExpression(node.AsPartiallyEmittedExpression().Expression)
2974+
p.emitContext.SetOriginal(parens, node.AsNode())
2975+
parens.Loc = parseNode.Loc
2976+
return parens
2977+
}
2978+
return p.emitContext.Factory.NewParenthesizedExpression(node)
2979+
}
2980+
return p.emitContext.Factory.UpdatePartiallyEmittedExpression(
2981+
node.AsPartiallyEmittedExpression(),
2982+
p.parenthesizeExpressionForNoAsi(node.AsPartiallyEmittedExpression().Expression),
2983+
)
2984+
case ast.KindPropertyAccessExpression:
2985+
return p.emitContext.Factory.UpdatePropertyAccessExpression(
2986+
node.AsPropertyAccessExpression(),
2987+
p.parenthesizeExpressionForNoAsi(node.AsPropertyAccessExpression().Expression),
2988+
node.AsPropertyAccessExpression().QuestionDotToken,
2989+
node.AsPropertyAccessExpression().Name(),
2990+
)
2991+
case ast.KindElementAccessExpression:
2992+
return p.emitContext.Factory.UpdateElementAccessExpression(
2993+
node.AsElementAccessExpression(),
2994+
p.parenthesizeExpressionForNoAsi(node.AsElementAccessExpression().Expression),
2995+
node.AsElementAccessExpression().QuestionDotToken,
2996+
node.AsElementAccessExpression().ArgumentExpression,
2997+
)
2998+
case ast.KindCallExpression:
2999+
return p.emitContext.Factory.UpdateCallExpression(
3000+
node.AsCallExpression(),
3001+
p.parenthesizeExpressionForNoAsi(node.AsCallExpression().Expression),
3002+
node.AsCallExpression().QuestionDotToken,
3003+
node.AsCallExpression().TypeArguments,
3004+
node.AsCallExpression().Arguments,
3005+
)
3006+
case ast.KindTaggedTemplateExpression:
3007+
return p.emitContext.Factory.UpdateTaggedTemplateExpression(
3008+
node.AsTaggedTemplateExpression(),
3009+
p.parenthesizeExpressionForNoAsi(node.AsTaggedTemplateExpression().Tag),
3010+
node.AsTaggedTemplateExpression().QuestionDotToken,
3011+
node.AsTaggedTemplateExpression().TypeArguments,
3012+
node.AsTaggedTemplateExpression().Template,
3013+
)
3014+
case ast.KindPostfixUnaryExpression:
3015+
return p.emitContext.Factory.UpdatePostfixUnaryExpression(
3016+
node.AsPostfixUnaryExpression(),
3017+
p.parenthesizeExpressionForNoAsi(node.AsPostfixUnaryExpression().Operand),
3018+
)
3019+
case ast.KindBinaryExpression:
3020+
return p.emitContext.Factory.UpdateBinaryExpression(
3021+
node.AsBinaryExpression(),
3022+
node.AsBinaryExpression().Modifiers(),
3023+
p.parenthesizeExpressionForNoAsi(node.AsBinaryExpression().Left),
3024+
node.AsBinaryExpression().Type,
3025+
node.AsBinaryExpression().OperatorToken,
3026+
node.AsBinaryExpression().Right,
3027+
)
3028+
case ast.KindConditionalExpression:
3029+
return p.emitContext.Factory.UpdateConditionalExpression(
3030+
node.AsConditionalExpression(),
3031+
p.parenthesizeExpressionForNoAsi(node.AsConditionalExpression().Condition),
3032+
node.AsConditionalExpression().QuestionToken,
3033+
node.AsConditionalExpression().WhenTrue,
3034+
node.AsConditionalExpression().ColonToken,
3035+
node.AsConditionalExpression().WhenFalse,
3036+
)
3037+
case ast.KindAsExpression:
3038+
return p.emitContext.Factory.UpdateAsExpression(
3039+
node.AsAsExpression(),
3040+
p.parenthesizeExpressionForNoAsi(node.AsAsExpression().Expression),
3041+
node.AsAsExpression().Type,
3042+
)
3043+
case ast.KindSatisfiesExpression:
3044+
return p.emitContext.Factory.UpdateSatisfiesExpression(
3045+
node.AsSatisfiesExpression(),
3046+
p.parenthesizeExpressionForNoAsi(node.AsSatisfiesExpression().Expression),
3047+
node.AsSatisfiesExpression().Type,
3048+
)
3049+
case ast.KindNonNullExpression:
3050+
return p.emitContext.Factory.UpdateNonNullExpression(
3051+
node.AsNonNullExpression(),
3052+
p.parenthesizeExpressionForNoAsi(node.AsNonNullExpression().Expression),
3053+
)
3054+
}
3055+
}
3056+
return node
29543057
}
29553058

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

29773076
func (p *Printer) emitExpression(node *ast.Expression, precedence ast.OperatorPrecedence) {

testdata/baselines/reference/submodule/conformance/returnStatementNoAsiAfterTransform(target=es5).js

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,52 +67,52 @@ function t10() {
6767

6868
//// [returnStatementNoAsiAfterTransform.js]
6969
function t1() {
70-
return
70+
return (
7171
// comment
72-
a;
72+
a);
7373
}
7474
function t2() {
75-
return
75+
return (
7676
// comment
77-
a + 1;
77+
a) + 1;
7878
}
7979
function t3() {
80-
return
80+
return (
8181
// comment
82-
a ? 0 : 1;
82+
a) ? 0 : 1;
8383
}
8484
function t4() {
85-
return
85+
return (
8686
// comment
87-
a.b;
87+
a).b;
8888
}
8989
function t5() {
90-
return
90+
return (
9191
// comment
92-
a[a];
92+
a)[a];
9393
}
9494
function t6() {
95-
return
95+
return (
9696
// comment
97-
a();
97+
a)();
9898
}
9999
function t7() {
100-
return
100+
return (
101101
// comment
102-
a ``;
102+
a) ``;
103103
}
104104
function t8() {
105-
return
105+
return (
106106
// comment
107-
a;
107+
a);
108108
}
109109
function t9() {
110-
return
110+
return (
111111
// comment
112-
a;
112+
a);
113113
}
114114
function t10() {
115-
return
115+
return (
116116
// comment
117-
a;
117+
a);
118118
}

testdata/baselines/reference/submodule/conformance/returnStatementNoAsiAfterTransform(target=es5).js.diff

Lines changed: 5 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,72 +9,14 @@
99
- return cooked;
1010
-};
1111
function t1() {
12-
- return (
13-
+ return
12+
return (
1413
// comment
15-
- a);
16-
+ a;
17-
}
18-
function t2() {
19-
- return (
20-
+ return
21-
// comment
22-
- a) + 1;
23-
+ a + 1;
24-
}
25-
function t3() {
26-
- return (
27-
+ return
28-
// comment
29-
- a) ? 0 : 1;
30-
+ a ? 0 : 1;
31-
}
32-
function t4() {
33-
- return (
34-
+ return
35-
// comment
36-
- a).b;
37-
+ a.b;
38-
}
39-
function t5() {
40-
- return (
41-
+ return
42-
// comment
43-
- a)[a];
44-
+ a[a];
45-
}
46-
function t6() {
47-
- return (
48-
+ return
49-
// comment
50-
- a)();
51-
+ a();
52-
}
14+
@@= skipped -37, +33 lines =@@
5315
function t7() {
54-
- return (
55-
+ return
16+
return (
5617
// comment
5718
- a)(__makeTemplateObject([""], [""]));
58-
+ a ``;
19+
+ a) ``;
5920
}
6021
function t8() {
61-
- return (
62-
+ return
63-
// comment
64-
- a);
65-
+ a;
66-
}
67-
function t9() {
68-
- return (
69-
+ return
70-
// comment
71-
- a);
72-
+ a;
73-
}
74-
function t10() {
75-
- return (
76-
+ return
77-
// comment
78-
- a);
79-
+ a;
80-
}
22+
return (

testdata/baselines/reference/submodule/conformance/returnStatementNoAsiAfterTransform(target=esnext).js

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,52 +67,52 @@ function t10() {
6767

6868
//// [returnStatementNoAsiAfterTransform.js]
6969
function t1() {
70-
return
70+
return (
7171
// comment
72-
a;
72+
a);
7373
}
7474
function t2() {
75-
return
75+
return (
7676
// comment
77-
a + 1;
77+
a) + 1;
7878
}
7979
function t3() {
80-
return
80+
return (
8181
// comment
82-
a ? 0 : 1;
82+
a) ? 0 : 1;
8383
}
8484
function t4() {
85-
return
85+
return (
8686
// comment
87-
a.b;
87+
a).b;
8888
}
8989
function t5() {
90-
return
90+
return (
9191
// comment
92-
a[a];
92+
a)[a];
9393
}
9494
function t6() {
95-
return
95+
return (
9696
// comment
97-
a();
97+
a)();
9898
}
9999
function t7() {
100-
return
100+
return (
101101
// comment
102-
a ``;
102+
a) ``;
103103
}
104104
function t8() {
105-
return
105+
return (
106106
// comment
107-
a;
107+
a);
108108
}
109109
function t9() {
110-
return
110+
return (
111111
// comment
112-
a;
112+
a);
113113
}
114114
function t10() {
115-
return
115+
return (
116116
// comment
117-
a;
117+
a);
118118
}

0 commit comments

Comments
 (0)