mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-11 00:55:01 -06:00
Fix parenthesizer rules for manually constructed binary expressions with ?? and ||/&& mix (#62311)
This commit is contained in:
parent
3f5c77f1f9
commit
4f94cb2aa4
@ -112,6 +112,16 @@ export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRul
|
||||
return parenthesizerRule;
|
||||
}
|
||||
|
||||
function mixingBinaryOperatorsRequiresParentheses(a: SyntaxKind, b: SyntaxKind) {
|
||||
if (a === SyntaxKind.QuestionQuestionToken) {
|
||||
return b === SyntaxKind.AmpersandAmpersandToken || b === SyntaxKind.BarBarToken;
|
||||
}
|
||||
if (b === SyntaxKind.QuestionQuestionToken) {
|
||||
return a === SyntaxKind.AmpersandAmpersandToken || a === SyntaxKind.BarBarToken;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the operand to a BinaryExpression needs to be parenthesized.
|
||||
*
|
||||
@ -121,6 +131,10 @@ export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRul
|
||||
* BinaryExpression.
|
||||
*/
|
||||
function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression | undefined) {
|
||||
const emittedOperand = skipPartiallyEmittedExpressions(operand);
|
||||
if (isBinaryExpression(emittedOperand) && mixingBinaryOperatorsRequiresParentheses(binaryOperator, emittedOperand.operatorToken.kind)) {
|
||||
return true;
|
||||
}
|
||||
// If the operand has lower precedence, then it needs to be parenthesized to preserve the
|
||||
// intent of the expression. For example, if the operand is `a + b` and the operator is
|
||||
// `*`, then we need to parenthesize the operand to preserve the intended order of
|
||||
@ -140,7 +154,6 @@ export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRul
|
||||
// the intended order of operations: `(a ** b) ** c`
|
||||
const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator);
|
||||
const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator);
|
||||
const emittedOperand = skipPartiallyEmittedExpressions(operand);
|
||||
if (!isLeftSideOfBinary && operand.kind === SyntaxKind.ArrowFunction && binaryOperatorPrecedence > OperatorPrecedence.Assignment) {
|
||||
// We need to parenthesize arrow functions on the right side to avoid it being
|
||||
// parsed as parenthesized expression: `a && (() => {})`
|
||||
|
||||
@ -423,5 +423,311 @@ describe("unittests:: PrinterAPI", () => {
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryBarBarExpressionWithLeftBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.BarBarToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryAmpersandAmpersandExpressionWithLeftBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryBarBarExpressionWithRightBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.BarBarToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryAmpersandAmpersandExpressionWithRightBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithLeftBinaryBarBarExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.BarBarToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithLeftBinaryAmpersandAmpersandExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithRightBinaryBarBarExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.BarBarToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithRightBinaryAmpersandAmpersandExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithLeftBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithRightBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryCommaExpressionWithLeftBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.CommaToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryCommaExpressionWithRightBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.CommaToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryEqualsEqualsExpressionWithLeftBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryEqualsEqualsExpressionWithRightBinaryQuestionQuestionExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithLeftBinaryCommaExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.CommaToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithRightBinaryCommaExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.CommaToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithLeftBinaryEqualsEqualsExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
|
||||
ts.factory.createIdentifier("b"),
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
|
||||
printsCorrectly("binaryQuestionQuestionExpressionWithRightBinaryEqualsEqualsExpression", {}, printer =>
|
||||
printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
ts.factory.createExpressionStatement(
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("a"),
|
||||
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
|
||||
ts.factory.createBinaryExpression(
|
||||
ts.factory.createIdentifier("b"),
|
||||
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
|
||||
ts.factory.createIdentifier("c"),
|
||||
),
|
||||
),
|
||||
),
|
||||
ts.createSourceFile("source.ts", "", ts.ScriptTarget.ESNext),
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1 @@
|
||||
(a ?? b) && c;
|
||||
@ -0,0 +1 @@
|
||||
a && (b ?? c);
|
||||
@ -0,0 +1 @@
|
||||
(a ?? b) || c;
|
||||
@ -0,0 +1 @@
|
||||
a || (b ?? c);
|
||||
@ -0,0 +1 @@
|
||||
a ?? b, c;
|
||||
@ -0,0 +1 @@
|
||||
a, b ?? c;
|
||||
@ -0,0 +1 @@
|
||||
(a ?? b) == c;
|
||||
@ -0,0 +1 @@
|
||||
a == (b ?? c);
|
||||
@ -0,0 +1 @@
|
||||
(a && b) ?? c;
|
||||
@ -0,0 +1 @@
|
||||
(a || b) ?? c;
|
||||
@ -0,0 +1 @@
|
||||
(a, b) ?? c;
|
||||
@ -0,0 +1 @@
|
||||
a == b ?? c;
|
||||
@ -0,0 +1 @@
|
||||
a ?? b ?? c;
|
||||
@ -0,0 +1 @@
|
||||
a ?? (b && c);
|
||||
@ -0,0 +1 @@
|
||||
a ?? (b || c);
|
||||
@ -0,0 +1 @@
|
||||
a ?? (b, c);
|
||||
@ -0,0 +1 @@
|
||||
a ?? b == c;
|
||||
@ -0,0 +1 @@
|
||||
a ?? (b ?? c);
|
||||
Loading…
x
Reference in New Issue
Block a user