@@ -2517,3 +2517,71 @@ func TestPartiallyEmittedExpression(t *testing.T) {
25172517 .expression
25182518 .expression;` )
25192519}
2520+
2521+ func TestParenthesizeBinaryExpressionMixingNullishCoalescing (t * testing.T ) {
2522+ t .Parallel ()
2523+
2524+ tests := []struct {
2525+ title string
2526+ innerOp ast.Kind
2527+ outerOp ast.Kind
2528+ side string
2529+ output string
2530+ }{
2531+ // inner ?? on left side of || or &&
2532+ {title : "BarBarWithLeftQuestionQuestion" , innerOp : ast .KindQuestionQuestionToken , outerOp : ast .KindBarBarToken , side : "left" , output : "(a ?? b) || c;" },
2533+ {title : "AmpersandAmpersandWithLeftQuestionQuestion" , innerOp : ast .KindQuestionQuestionToken , outerOp : ast .KindAmpersandAmpersandToken , side : "left" , output : "(a ?? b) && c;" },
2534+ // inner ?? on right side of || or &&
2535+ {title : "BarBarWithRightQuestionQuestion" , innerOp : ast .KindQuestionQuestionToken , outerOp : ast .KindBarBarToken , side : "right" , output : "a || (b ?? c);" },
2536+ {title : "AmpersandAmpersandWithRightQuestionQuestion" , innerOp : ast .KindQuestionQuestionToken , outerOp : ast .KindAmpersandAmpersandToken , side : "right" , output : "a && (b ?? c);" },
2537+ // inner || or && on left side of ??
2538+ {title : "QuestionQuestionWithLeftBarBar" , innerOp : ast .KindBarBarToken , outerOp : ast .KindQuestionQuestionToken , side : "left" , output : "(a || b) ?? c;" },
2539+ {title : "QuestionQuestionWithLeftAmpersandAmpersand" , innerOp : ast .KindAmpersandAmpersandToken , outerOp : ast .KindQuestionQuestionToken , side : "left" , output : "(a && b) ?? c;" },
2540+ // inner || or && on right side of ??
2541+ {title : "QuestionQuestionWithRightBarBar" , innerOp : ast .KindBarBarToken , outerOp : ast .KindQuestionQuestionToken , side : "right" , output : "a ?? (b || c);" },
2542+ {title : "QuestionQuestionWithRightAmpersandAmpersand" , innerOp : ast .KindAmpersandAmpersandToken , outerOp : ast .KindQuestionQuestionToken , side : "right" , output : "a ?? (b && c);" },
2543+ }
2544+
2545+ for _ , tt := range tests {
2546+ t .Run (tt .title , func (t * testing.T ) {
2547+ t .Parallel ()
2548+ var factory ast.NodeFactory
2549+ innerExpr := factory .NewBinaryExpression (
2550+ nil , /*modifiers*/
2551+ factory .NewIdentifier ("a" ),
2552+ nil , /*typeNode*/
2553+ factory .NewToken (tt .innerOp ),
2554+ factory .NewIdentifier ("b" ),
2555+ )
2556+ var outerExpr * ast.Node
2557+ if tt .side == "left" {
2558+ outerExpr = factory .NewBinaryExpression (
2559+ nil , /*modifiers*/
2560+ innerExpr , /*left: (a innerOp b)*/
2561+ nil , /*typeNode*/
2562+ factory .NewToken (tt .outerOp ),
2563+ factory .NewIdentifier ("c" ),
2564+ )
2565+ } else {
2566+ outerExpr = factory .NewBinaryExpression (
2567+ nil , /*modifiers*/
2568+ factory .NewIdentifier ("a" ),
2569+ nil , /*typeNode*/
2570+ factory .NewToken (tt .outerOp ),
2571+ innerExpr , /*right: (b innerOp c)*/
2572+ )
2573+ // adjust identifiers for right side
2574+ innerExpr .AsBinaryExpression ().Left = factory .NewIdentifier ("b" )
2575+ innerExpr .AsBinaryExpression ().Right = factory .NewIdentifier ("c" )
2576+ }
2577+ file := factory .NewSourceFile (ast.SourceFileParseOptions {FileName : "/file.ts" , Path : "/file.ts" }, "" , factory .NewNodeList (
2578+ []* ast.Node {
2579+ factory .NewExpressionStatement (outerExpr ),
2580+ },
2581+ ), factory .NewToken (ast .KindEndOfFile ))
2582+
2583+ parsetestutil .MarkSyntheticRecursive (file )
2584+ emittestutil .CheckEmit (t , nil , file .AsSourceFile (), tt .output )
2585+ })
2586+ }
2587+ }
0 commit comments