Skip to content

Commit 4f94cb2

Browse files
authored
Fix parenthesizer rules for manually constructed binary expressions with ?? and ||/&& mix (#62311)
1 parent 3f5c77f commit 4f94cb2

File tree

20 files changed

+338
-1
lines changed

20 files changed

+338
-1
lines changed

src/compiler/factory/parenthesizerRules.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRul
112112
return parenthesizerRule;
113113
}
114114

115+
function mixingBinaryOperatorsRequiresParentheses(a: SyntaxKind, b: SyntaxKind) {
116+
if (a === SyntaxKind.QuestionQuestionToken) {
117+
return b === SyntaxKind.AmpersandAmpersandToken || b === SyntaxKind.BarBarToken;
118+
}
119+
if (b === SyntaxKind.QuestionQuestionToken) {
120+
return a === SyntaxKind.AmpersandAmpersandToken || a === SyntaxKind.BarBarToken;
121+
}
122+
return false;
123+
}
124+
115125
/**
116126
* Determines whether the operand to a BinaryExpression needs to be parenthesized.
117127
*
@@ -121,6 +131,10 @@ export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRul
121131
* BinaryExpression.
122132
*/
123133
function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression | undefined) {
134+
const emittedOperand = skipPartiallyEmittedExpressions(operand);
135+
if (isBinaryExpression(emittedOperand) && mixingBinaryOperatorsRequiresParentheses(binaryOperator, emittedOperand.operatorToken.kind)) {
136+
return true;
137+
}
124138
// If the operand has lower precedence, then it needs to be parenthesized to preserve the
125139
// intent of the expression. For example, if the operand is `a + b` and the operator is
126140
// `*`, then we need to parenthesize the operand to preserve the intended order of
@@ -140,7 +154,6 @@ export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRul
140154
// the intended order of operations: `(a ** b) ** c`
141155
const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator);
142156
const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator);
143-
const emittedOperand = skipPartiallyEmittedExpressions(operand);
144157
if (!isLeftSideOfBinary && operand.kind === SyntaxKind.ArrowFunction && binaryOperatorPrecedence > OperatorPrecedence.Assignment) {
145158
// We need to parenthesize arrow functions on the right side to avoid it being
146159
// parsed as parenthesized expression: `a && (() => {})`

src/testRunner/unittests/printer.ts

Lines changed: 306 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(a ?? b) && c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a && (b ?? c);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(a ?? b) || c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a || (b ?? c);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a ?? b, c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a, b ?? c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(a ?? b) == c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a == (b ?? c);

0 commit comments

Comments
 (0)