Skip to content

Commit 0fbc06e

Browse files
authored
Port "Fixed nullish coalesce operator's precedence" (#1824)
1 parent 054871a commit 0fbc06e

15 files changed

+43
-124
lines changed

internal/ast/precedence.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,6 @@ const (
4343
// ShortCircuitExpression
4444
// ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression
4545
OperatorPrecedenceConditional
46-
// ShortCircuitExpression:
47-
// LogicalORExpression
48-
// CoalesceExpression
49-
// CoalesceExpression:
50-
// CoalesceExpressionHead `??` BitwiseORExpression
51-
// CoalesceExpressionHead:
52-
// CoalesceExpression
53-
// BitwiseORExpression
54-
OperatorPrecedenceCoalesce
5546
// LogicalORExpression:
5647
// LogicalANDExpression
5748
// LogicalORExpression `||` LogicalANDExpression
@@ -181,6 +172,15 @@ const (
181172
OperatorPrecedenceLowest = OperatorPrecedenceComma
182173
OperatorPrecedenceHighest = OperatorPrecedenceParentheses
183174
OperatorPrecedenceDisallowComma = OperatorPrecedenceYield
175+
// ShortCircuitExpression:
176+
// LogicalORExpression
177+
// CoalesceExpression
178+
// CoalesceExpression:
179+
// CoalesceExpressionHead `??` BitwiseORExpression
180+
// CoalesceExpressionHead:
181+
// CoalesceExpression
182+
// BitwiseORExpression
183+
OperatorPrecedenceCoalesce = OperatorPrecedenceLogicalOR
184184
// -1 is lower than all other precedences. Returning it will cause binary expression
185185
// parsing to stop.
186186
OperatorPrecedenceInvalid OperatorPrecedence = -1

internal/checker/checker.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12409,11 +12409,22 @@ func (c *Checker) getSyntacticTruthySemantics(node *ast.Node) PredicateSemantics
1240912409
}
1241012410

1241112411
func (c *Checker) checkNullishCoalesceOperands(left *ast.Node, right *ast.Node) {
12412-
if ast.IsBinaryExpression(left) && ast.IsLogicalBinaryOperator(left.AsBinaryExpression().OperatorToken.Kind) {
12413-
c.grammarErrorOnNode(left, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(left.AsBinaryExpression().OperatorToken.Kind), scanner.TokenToString(ast.KindQuestionQuestionToken))
12414-
}
12415-
if ast.IsBinaryExpression(right) && ast.IsLogicalBinaryOperator(right.AsBinaryExpression().OperatorToken.Kind) {
12416-
c.grammarErrorOnNode(right, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(right.AsBinaryExpression().OperatorToken.Kind), scanner.TokenToString(ast.KindQuestionQuestionToken))
12412+
if ast.IsBinaryExpression(left.Parent.Parent) {
12413+
grandparentLeft := left.Parent.Parent.AsBinaryExpression().Left
12414+
grandparentOperatorToken := left.Parent.Parent.AsBinaryExpression().OperatorToken
12415+
if ast.IsBinaryExpression(grandparentLeft) && grandparentOperatorToken.Kind == ast.KindBarBarToken {
12416+
c.grammarErrorOnNode(grandparentLeft, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(ast.KindQuestionQuestionToken), scanner.TokenToString(grandparentOperatorToken.Kind))
12417+
}
12418+
} else if ast.IsBinaryExpression(left) {
12419+
operatorToken := left.AsBinaryExpression().OperatorToken
12420+
if operatorToken.Kind == ast.KindBarBarToken || operatorToken.Kind == ast.KindAmpersandAmpersandToken {
12421+
c.grammarErrorOnNode(left, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(operatorToken.Kind), scanner.TokenToString(ast.KindQuestionQuestionToken))
12422+
}
12423+
} else if ast.IsBinaryExpression(right) {
12424+
operatorToken := right.AsBinaryExpression().OperatorToken
12425+
if operatorToken.Kind == ast.KindAmpersandAmpersandToken {
12426+
c.grammarErrorOnNode(right, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(ast.KindQuestionQuestionToken), scanner.TokenToString(operatorToken.Kind))
12427+
}
1241712428
}
1241812429
c.checkNullishCoalesceOperandLeft(left)
1241912430
c.checkNullishCoalesceOperandRight(right)

internal/printer/printer.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,14 +2716,6 @@ func (p *Printer) getBinaryExpressionPrecedence(node *ast.BinaryExpression) (lef
27162716
case ast.OperatorPrecedenceAssignment:
27172717
// assignment is right-associative
27182718
leftPrec = ast.OperatorPrecedenceLeftHandSide
2719-
case ast.OperatorPrecedenceCoalesce:
2720-
// allow coalesce on the left, but short circuit to BitwiseOR
2721-
if isBinaryOperation(node.Left, ast.KindQuestionQuestionToken) {
2722-
leftPrec = ast.OperatorPrecedenceCoalesce
2723-
} else {
2724-
leftPrec = ast.OperatorPrecedenceBitwiseOR
2725-
}
2726-
rightPrec = ast.OperatorPrecedenceBitwiseOR
27272719
case ast.OperatorPrecedenceLogicalOR:
27282720
rightPrec = ast.OperatorPrecedenceLogicalAND
27292721
case ast.OperatorPrecedenceLogicalAND:

testdata/baselines/reference/submodule/conformance/nullishCoalescingOperator5.errors.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
nullishCoalescingOperator5.ts(6,6): error TS5076: '||' and '??' operations cannot be mixed without parentheses.
1+
nullishCoalescingOperator5.ts(6,1): error TS5076: '??' and '||' operations cannot be mixed without parentheses.
22
nullishCoalescingOperator5.ts(9,1): error TS5076: '||' and '??' operations cannot be mixed without parentheses.
3-
nullishCoalescingOperator5.ts(12,6): error TS5076: '&&' and '??' operations cannot be mixed without parentheses.
3+
nullishCoalescingOperator5.ts(12,6): error TS5076: '??' and '&&' operations cannot be mixed without parentheses.
44
nullishCoalescingOperator5.ts(15,1): error TS5076: '&&' and '??' operations cannot be mixed without parentheses.
55

66

@@ -11,8 +11,8 @@ nullishCoalescingOperator5.ts(15,1): error TS5076: '&&' and '??' operations cann
1111

1212
// should be a syntax error
1313
a ?? b || c;
14-
~~~~~~
15-
!!! error TS5076: '||' and '??' operations cannot be mixed without parentheses.
14+
~~~~~~
15+
!!! error TS5076: '??' and '||' operations cannot be mixed without parentheses.
1616

1717
// should be a syntax error
1818
a || b ?? c;
@@ -22,7 +22,7 @@ nullishCoalescingOperator5.ts(15,1): error TS5076: '&&' and '??' operations cann
2222
// should be a syntax error
2323
a ?? b && c;
2424
~~~~~~
25-
!!! error TS5076: '&&' and '??' operations cannot be mixed without parentheses.
25+
!!! error TS5076: '??' and '&&' operations cannot be mixed without parentheses.
2626

2727
// should be a syntax error
2828
a && b ?? c;

testdata/baselines/reference/submodule/conformance/nullishCoalescingOperator5.errors.txt.diff

Lines changed: 0 additions & 31 deletions
This file was deleted.

testdata/baselines/reference/submodule/conformance/nullishCoalescingOperator5.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ a && (b ?? c);
4545
//// [nullishCoalescingOperator5.js]
4646
var _a, _b, _c, _d;
4747
// should be a syntax error
48-
a !== null && a !== void 0 ? a : b || c;
48+
(a !== null && a !== void 0 ? a : b) || c;
4949
// should be a syntax error
5050
(_a = a || b) !== null && _a !== void 0 ? _a : c;
5151
// should be a syntax error

testdata/baselines/reference/submodule/conformance/nullishCoalescingOperator5.js.diff

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,4 @@
77
-"use strict";
88
var _a, _b, _c, _d;
99
// should be a syntax error
10-
-(a !== null && a !== void 0 ? a : b) || c;
11-
+a !== null && a !== void 0 ? a : b || c;
12-
// should be a syntax error
13-
(_a = a || b) !== null && _a !== void 0 ? _a : c;
14-
// should be a syntax error
10+
(a !== null && a !== void 0 ? a : b) || c;

testdata/baselines/reference/submodule/conformance/nullishCoalescingOperator5.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ declare const c: string | undefined
1313
// should be a syntax error
1414
a ?? b || c;
1515
>a ?? b || c : string | undefined
16+
>a ?? b : string | undefined
1617
>a : string | undefined
17-
>b || c : string | undefined
1818
>b : string | undefined
1919
>c : string | undefined
2020

testdata/baselines/reference/submodule/conformance/nullishCoalescingOperator5.types.diff

Lines changed: 0 additions & 11 deletions
This file was deleted.

testdata/baselines/reference/submodule/conformance/plainJSGrammarErrors.errors.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ plainJSGrammarErrors.js(92,33): error TS2566: A rest element cannot have a prope
5858
plainJSGrammarErrors.js(93,42): error TS1186: A rest element cannot have an initializer.
5959
plainJSGrammarErrors.js(96,4): error TS1123: Variable declaration list cannot be empty.
6060
plainJSGrammarErrors.js(97,9): error TS5076: '||' and '??' operations cannot be mixed without parentheses.
61-
plainJSGrammarErrors.js(98,14): error TS5076: '||' and '??' operations cannot be mixed without parentheses.
61+
plainJSGrammarErrors.js(98,9): error TS5076: '??' and '||' operations cannot be mixed without parentheses.
6262
plainJSGrammarErrors.js(100,3): error TS1200: Line terminator not permitted before arrow.
6363
plainJSGrammarErrors.js(102,4): error TS1358: Tagged template expressions are not permitted in an optional chain.
6464
plainJSGrammarErrors.js(104,6): error TS1171: A comma expression is not allowed in a computed property name.
@@ -320,8 +320,8 @@ plainJSGrammarErrors.js(205,36): error TS1325: Argument of dynamic import cannot
320320
~~~~~~
321321
!!! error TS5076: '||' and '??' operations cannot be mixed without parentheses.
322322
var x = 2 ?? 3 || 4
323-
~~~~~~
324-
!!! error TS5076: '||' and '??' operations cannot be mixed without parentheses.
323+
~~~~~~
324+
!!! error TS5076: '??' and '||' operations cannot be mixed without parentheses.
325325
const arr = x
326326
=> x + 1
327327
~~

0 commit comments

Comments
 (0)