Skip to content

Commit f072810

Browse files
Implement more rigorous ASI prevention for return/yield statements
Co-authored-by: RyanCavanaugh <[email protected]>
1 parent 48b8ee8 commit f072810

File tree

1 file changed

+148
-5
lines changed

1 file changed

+148
-5
lines changed

internal/printer/printer.go

Lines changed: 148 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2964,12 +2964,155 @@ func (p *Printer) emitExpressionNoASI(node *ast.Expression, precedence ast.Opera
29642964
// a;
29652965
// }
29662966
// Due to ASI, this would result in a `return` with no value followed by an unreachable expression statement.
2967-
if !p.commentsDisabled && node.Kind == ast.KindPartiallyEmittedExpression && p.willEmitLeadingNewLine(node) {
2968-
// !!! if there is an original parse tree node, restore it with location to preserve comments and source maps.
2969-
p.emitExpression(node, ast.OperatorPrecedenceParentheses)
2970-
} else {
2971-
p.emitExpression(node, precedence)
2967+
p.emitExpression(p.parenthesizeExpressionForNoAsi(node), precedence)
2968+
}
2969+
2970+
// parenthesizeExpressionForNoAsi walks down the left side of expressions to check if any
2971+
// PartiallyEmittedExpression might introduce ASI, and returns an expression that ensures
2972+
// proper parenthesization to prevent ASI issues.
2973+
func (p *Printer) parenthesizeExpressionForNoAsi(node *ast.Expression) *ast.Expression {
2974+
if p.commentsDisabled {
2975+
return node
29722976
}
2977+
2978+
switch node.Kind {
2979+
case ast.KindPartiallyEmittedExpression:
2980+
pee := node.AsPartiallyEmittedExpression()
2981+
if p.willEmitLeadingNewLine(node) {
2982+
// !!! if there is an original parse tree node, restore it with location to preserve comments and source maps.
2983+
// Emit with parentheses precedence to force wrapping
2984+
return p.createParenthesizedExpressionForNoAsi(node)
2985+
}
2986+
// Recursively check the inner expression
2987+
innerParenthesized := p.parenthesizeExpressionForNoAsi(pee.Expression)
2988+
if innerParenthesized != pee.Expression {
2989+
// Need to create a new PartiallyEmittedExpression with the parenthesized inner expression
2990+
return p.updatePartiallyEmittedExpression(pee, innerParenthesized)
2991+
}
2992+
2993+
case ast.KindPropertyAccessExpression:
2994+
pae := node.AsPropertyAccessExpression()
2995+
exprParenthesized := p.parenthesizeExpressionForNoAsi(pae.Expression)
2996+
if exprParenthesized != pae.Expression {
2997+
return p.updatePropertyAccessExpression(pae, exprParenthesized)
2998+
}
2999+
3000+
case ast.KindElementAccessExpression:
3001+
eae := node.AsElementAccessExpression()
3002+
exprParenthesized := p.parenthesizeExpressionForNoAsi(eae.Expression)
3003+
if exprParenthesized != eae.Expression {
3004+
return p.updateElementAccessExpression(eae, exprParenthesized)
3005+
}
3006+
3007+
case ast.KindCallExpression:
3008+
ce := node.AsCallExpression()
3009+
exprParenthesized := p.parenthesizeExpressionForNoAsi(ce.Expression)
3010+
if exprParenthesized != ce.Expression {
3011+
return p.updateCallExpression(ce, exprParenthesized)
3012+
}
3013+
3014+
case ast.KindTaggedTemplateExpression:
3015+
tte := node.AsTaggedTemplateExpression()
3016+
tagParenthesized := p.parenthesizeExpressionForNoAsi(tte.Tag)
3017+
if tagParenthesized != tte.Tag {
3018+
return p.updateTaggedTemplateExpression(tte, tagParenthesized)
3019+
}
3020+
3021+
case ast.KindPostfixUnaryExpression:
3022+
pue := node.AsPostfixUnaryExpression()
3023+
operandParenthesized := p.parenthesizeExpressionForNoAsi(pue.Operand)
3024+
if operandParenthesized != pue.Operand {
3025+
return p.updatePostfixUnaryExpression(pue, operandParenthesized)
3026+
}
3027+
3028+
case ast.KindBinaryExpression:
3029+
be := node.AsBinaryExpression()
3030+
leftParenthesized := p.parenthesizeExpressionForNoAsi(be.Left)
3031+
if leftParenthesized != be.Left {
3032+
return p.updateBinaryExpression(be, leftParenthesized)
3033+
}
3034+
3035+
case ast.KindConditionalExpression:
3036+
ce := node.AsConditionalExpression()
3037+
conditionParenthesized := p.parenthesizeExpressionForNoAsi(ce.Condition)
3038+
if conditionParenthesized != ce.Condition {
3039+
return p.updateConditionalExpression(ce, conditionParenthesized)
3040+
}
3041+
3042+
case ast.KindAsExpression:
3043+
ae := node.AsAsExpression()
3044+
exprParenthesized := p.parenthesizeExpressionForNoAsi(ae.Expression)
3045+
if exprParenthesized != ae.Expression {
3046+
return p.updateAsExpression(ae, exprParenthesized)
3047+
}
3048+
3049+
case ast.KindSatisfiesExpression:
3050+
se := node.AsSatisfiesExpression()
3051+
exprParenthesized := p.parenthesizeExpressionForNoAsi(se.Expression)
3052+
if exprParenthesized != se.Expression {
3053+
return p.updateSatisfiesExpression(se, exprParenthesized)
3054+
}
3055+
3056+
case ast.KindNonNullExpression:
3057+
nne := node.AsNonNullExpression()
3058+
exprParenthesized := p.parenthesizeExpressionForNoAsi(nne.Expression)
3059+
if exprParenthesized != nne.Expression {
3060+
return p.updateNonNullExpression(nne, exprParenthesized)
3061+
}
3062+
}
3063+
3064+
return node
3065+
}
3066+
3067+
// Helper functions to create/update nodes with parenthesized sub-expressions
3068+
3069+
func (p *Printer) createParenthesizedExpressionForNoAsi(node *ast.Expression) *ast.Expression {
3070+
// Create a parenthesized expression to force wrapping
3071+
return p.emitContext.Factory.NewParenthesizedExpression(node)
3072+
}
3073+
3074+
func (p *Printer) updatePartiallyEmittedExpression(node *ast.PartiallyEmittedExpression, expression *ast.Expression) *ast.Expression {
3075+
return p.emitContext.Factory.UpdatePartiallyEmittedExpression(node, expression)
3076+
}
3077+
3078+
func (p *Printer) updatePropertyAccessExpression(node *ast.PropertyAccessExpression, expression *ast.Expression) *ast.Expression {
3079+
return p.emitContext.Factory.UpdatePropertyAccessExpression(node, expression, node.QuestionDotToken, node.Name())
3080+
}
3081+
3082+
func (p *Printer) updateElementAccessExpression(node *ast.ElementAccessExpression, expression *ast.Expression) *ast.Expression {
3083+
return p.emitContext.Factory.UpdateElementAccessExpression(node, expression, node.QuestionDotToken, node.ArgumentExpression)
3084+
}
3085+
3086+
func (p *Printer) updateCallExpression(node *ast.CallExpression, expression *ast.Expression) *ast.Expression {
3087+
return p.emitContext.Factory.UpdateCallExpression(node, expression, node.QuestionDotToken, node.TypeArguments, node.Arguments)
3088+
}
3089+
3090+
func (p *Printer) updateTaggedTemplateExpression(node *ast.TaggedTemplateExpression, tag *ast.Expression) *ast.Expression {
3091+
return p.emitContext.Factory.UpdateTaggedTemplateExpression(node, tag, node.QuestionDotToken, node.TypeArguments, node.Template)
3092+
}
3093+
3094+
func (p *Printer) updatePostfixUnaryExpression(node *ast.PostfixUnaryExpression, operand *ast.Expression) *ast.Expression {
3095+
return p.emitContext.Factory.UpdatePostfixUnaryExpression(node, operand)
3096+
}
3097+
3098+
func (p *Printer) updateBinaryExpression(node *ast.BinaryExpression, left *ast.Expression) *ast.Expression {
3099+
return p.emitContext.Factory.UpdateBinaryExpression(node, node.Modifiers(), left, node.Type, node.OperatorToken, node.Right)
3100+
}
3101+
3102+
func (p *Printer) updateConditionalExpression(node *ast.ConditionalExpression, condition *ast.Expression) *ast.Expression {
3103+
return p.emitContext.Factory.UpdateConditionalExpression(node, condition, node.QuestionToken, node.WhenTrue, node.ColonToken, node.WhenFalse)
3104+
}
3105+
3106+
func (p *Printer) updateAsExpression(node *ast.AsExpression, expression *ast.Expression) *ast.Expression {
3107+
return p.emitContext.Factory.UpdateAsExpression(node, expression, node.Type)
3108+
}
3109+
3110+
func (p *Printer) updateSatisfiesExpression(node *ast.SatisfiesExpression, expression *ast.Expression) *ast.Expression {
3111+
return p.emitContext.Factory.UpdateSatisfiesExpression(node, expression, node.Type)
3112+
}
3113+
3114+
func (p *Printer) updateNonNullExpression(node *ast.NonNullExpression, expression *ast.Expression) *ast.Expression {
3115+
return p.emitContext.Factory.UpdateNonNullExpression(node, expression)
29733116
}
29743117

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

0 commit comments

Comments
 (0)