Skip to content

Commit fe087f2

Browse files
stereotype441Commit Queue
authored andcommitted
Add parser support for switch expressions.
Bug: dart-lang/sdk#50035 Change-Id: I0c58664d44312f5e9d61d24e34e7cd26dbee3a9c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/271740 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
1 parent 669856c commit fe087f2

File tree

42 files changed

+1480
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1480
-0
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,16 +454,31 @@ class ForwardingListener implements Listener {
454454
listener?.beginSwitchBlock(token);
455455
}
456456

457+
@override
458+
void beginSwitchExpressionBlock(Token token) {
459+
listener?.beginSwitchExpressionBlock(token);
460+
}
461+
457462
@override
458463
void beginSwitchCase(int labelCount, int expressionCount, Token firstToken) {
459464
listener?.beginSwitchCase(labelCount, expressionCount, firstToken);
460465
}
461466

467+
@override
468+
void beginSwitchExpressionCase() {
469+
listener?.beginSwitchExpressionCase();
470+
}
471+
462472
@override
463473
void beginSwitchStatement(Token token) {
464474
listener?.beginSwitchStatement(token);
465475
}
466476

477+
@override
478+
void beginSwitchExpression(Token token) {
479+
listener?.beginSwitchExpression(token);
480+
}
481+
467482
@override
468483
void handleThenControlFlow(Token token) {
469484
listener?.handleThenControlFlow(token);
@@ -1146,6 +1161,12 @@ class ForwardingListener implements Listener {
11461161
listener?.endSwitchBlock(caseCount, beginToken, endToken);
11471162
}
11481163

1164+
@override
1165+
void endSwitchExpressionBlock(
1166+
int caseCount, Token beginToken, Token endToken) {
1167+
listener?.endSwitchExpressionBlock(caseCount, beginToken, endToken);
1168+
}
1169+
11491170
@override
11501171
void endSwitchCase(
11511172
int labelCount,
@@ -1159,11 +1180,21 @@ class ForwardingListener implements Listener {
11591180
colonAfterDefault, statementCount, firstToken, endToken);
11601181
}
11611182

1183+
@override
1184+
void endSwitchExpressionCase(Token? when, Token arrow, Token endToken) {
1185+
listener?.endSwitchExpressionCase(when, arrow, endToken);
1186+
}
1187+
11621188
@override
11631189
void endSwitchStatement(Token switchKeyword, Token endToken) {
11641190
listener?.endSwitchStatement(switchKeyword, endToken);
11651191
}
11661192

1193+
@override
1194+
void endSwitchExpression(Token switchKeyword, Token endToken) {
1195+
listener?.endSwitchExpression(switchKeyword, endToken);
1196+
}
1197+
11671198
@override
11681199
void endThenStatement(Token token) {
11691200
listener?.endThenStatement(token);

pkg/_fe_analyzer_shared/lib/src/parser/listener.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,12 +1254,25 @@ class Listener implements UnescapeErrorListener {
12541254
logEvent("SwitchStatement");
12551255
}
12561256

1257+
void beginSwitchExpression(Token token) {}
1258+
1259+
void endSwitchExpression(Token switchKeyword, Token endToken) {
1260+
logEvent("SwitchExpression");
1261+
}
1262+
12571263
void beginSwitchBlock(Token token) {}
12581264

12591265
void endSwitchBlock(int caseCount, Token beginToken, Token endToken) {
12601266
logEvent("SwitchBlock");
12611267
}
12621268

1269+
void beginSwitchExpressionBlock(Token token) {}
1270+
1271+
void endSwitchExpressionBlock(
1272+
int caseCount, Token beginToken, Token endToken) {
1273+
logEvent("SwitchExpressionBlock");
1274+
}
1275+
12631276
void beginLiteralSymbol(Token token) {}
12641277

12651278
void endLiteralSymbol(Token hashToken, int identifierCount) {
@@ -1950,6 +1963,12 @@ class Listener implements UnescapeErrorListener {
19501963
logEvent("SwitchCase");
19511964
}
19521965

1966+
void beginSwitchExpressionCase() {}
1967+
1968+
void endSwitchExpressionCase(Token? when, Token arrow, Token endToken) {
1969+
logEvent("SwitchExpressionCase");
1970+
}
1971+
19531972
void handleThisExpression(Token token, IdentifierContext context) {
19541973
logEvent("ThisExpression");
19551974
}

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3958,6 +3958,17 @@ class Parser {
39583958
return rewriteAndRecover(token, message, newToken);
39593959
}
39603960

3961+
/// If the next token is a function arrow (`=>`), return it. Otherwise report
3962+
/// an error, insert a synthetic function arrow, and return the inserted
3963+
/// function arrow.
3964+
Token ensureFunctionArrow(Token token) {
3965+
Token next = token.next!;
3966+
if (optional('=>', next)) return next;
3967+
codes.Message message = codes.templateExpectedButGot.withArguments('=>');
3968+
Token newToken = new SyntheticToken(TokenType.FUNCTION, next.charOffset);
3969+
return rewriteAndRecover(token, message, newToken);
3970+
}
3971+
39613972
/// If the token after [token] is a not literal string,
39623973
/// then insert a synthetic literal string.
39633974
/// Call `parseLiteralString` and return the result.
@@ -6106,6 +6117,8 @@ class Parser {
61066117
// Fall through to the recovery code.
61076118
} else if (identical(value, "assert")) {
61086119
return parseAssert(token, Assert.Expression);
6120+
} else if (allowPatterns && identical(value, "switch")) {
6121+
return parseSwitchExpression(token);
61096122
} else if (token.next!.isIdentifier) {
61106123
return parseSendOrFunctionLiteral(token, context);
61116124
} else if (identical(value, "return")) {
@@ -9821,6 +9834,73 @@ class Parser {
98219834
keyword, equals, semicolon);
98229835
return semicolon;
98239836
}
9837+
9838+
/// switchExpression ::= 'switch' '(' expression ')' '{'
9839+
/// switchExpressionCase ( ',' switchExpressionCase )*
9840+
/// ','? '}'
9841+
/// switchExpressionCase ::= guardedPattern '=>' expression
9842+
Token parseSwitchExpression(Token token) {
9843+
Token switchKeyword = token.next!;
9844+
assert(optional('switch', switchKeyword));
9845+
listener.beginSwitchExpression(switchKeyword);
9846+
token = ensureParenthesizedCondition(switchKeyword, allowCase: false);
9847+
Token beginSwitch =
9848+
token = ensureBlock(token, /* template = */ null, 'switch expression');
9849+
listener.beginSwitchExpressionBlock(beginSwitch);
9850+
Token next = token.next!;
9851+
int caseCount = 0;
9852+
if (!optional('}', next)) {
9853+
while (true) {
9854+
listener.beginSwitchExpressionCase();
9855+
token = parsePattern(token, isRefutableContext: true);
9856+
Token? when;
9857+
next = token.next!;
9858+
if (optional('when', next)) {
9859+
when = token = next;
9860+
token = parseExpression(token);
9861+
}
9862+
Token arrow = token = ensureFunctionArrow(token);
9863+
token = parseExpression(token);
9864+
listener.endSwitchExpressionCase(when, arrow, token);
9865+
++caseCount;
9866+
next = token.next!;
9867+
9868+
Token? comma;
9869+
if (optional(',', next)) {
9870+
comma = token = next;
9871+
next = token.next!;
9872+
}
9873+
if (optional('}', next)) {
9874+
break;
9875+
}
9876+
9877+
if (comma == null) {
9878+
// TODO(paulberry): test this error recovery logic
9879+
// Recovery
9880+
if (looksLikePatternStart(next)) {
9881+
// If this looks like the start of a pattern, then report an error,
9882+
// insert the comma, and continue parsing.
9883+
SyntheticToken comma =
9884+
new SyntheticToken(TokenType.COMMA, next.offset);
9885+
codes.Message message =
9886+
codes.templateExpectedButGot.withArguments(',');
9887+
token = rewriteAndRecover(token, message, comma);
9888+
} else {
9889+
reportRecoverableError(
9890+
next, codes.templateExpectedButGot.withArguments('}'));
9891+
// Scanner guarantees a closing curly bracket
9892+
next = beginSwitch.endGroup!;
9893+
break;
9894+
}
9895+
}
9896+
}
9897+
}
9898+
listener.endSwitchExpressionBlock(caseCount, beginSwitch, next);
9899+
token = next;
9900+
assert(token.isEof || optional('}', token));
9901+
listener.endSwitchExpression(switchKeyword, token);
9902+
return token;
9903+
}
98249904
}
98259905

98269906
// TODO(ahe): Remove when analyzer supports generalized function syntax.

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3040,6 +3040,61 @@ class AstBuilder extends StackListener {
30403040
push(members2);
30413041
}
30423042

3043+
@override
3044+
void endSwitchExpression(Token switchKeyword, Token endToken) {
3045+
assert(optional('switch', switchKeyword));
3046+
debugEvent("SwitchExpression");
3047+
3048+
var rightBracket = pop() as Token;
3049+
var cases = pop() as List<SwitchExpressionCaseImpl>;
3050+
var leftBracket = pop() as Token;
3051+
var condition = pop() as _ParenthesizedCondition;
3052+
push(
3053+
SwitchExpressionImpl(
3054+
switchKeyword: switchKeyword,
3055+
leftParenthesis: condition.leftParenthesis,
3056+
expression: condition.expression,
3057+
rightParenthesis: condition.rightParenthesis,
3058+
leftBracket: leftBracket,
3059+
cases: cases,
3060+
rightBracket: rightBracket,
3061+
),
3062+
);
3063+
}
3064+
3065+
@override
3066+
void endSwitchExpressionBlock(
3067+
int caseCount, Token leftBracket, Token rightBracket) {
3068+
assert(optional('{', leftBracket));
3069+
assert(optional('}', rightBracket));
3070+
debugEvent("SwitchExpressionBlock");
3071+
3072+
var cases = popTypedList2<SwitchExpressionCaseImpl>(caseCount);
3073+
3074+
push(leftBracket);
3075+
push(cases);
3076+
push(rightBracket);
3077+
}
3078+
3079+
@override
3080+
void endSwitchExpressionCase(Token? when, Token arrow, Token endToken) {
3081+
debugEvent("SwitchExpressionCase");
3082+
var expression = pop() as ExpressionImpl;
3083+
WhenClauseImpl? whenClause;
3084+
if (when != null) {
3085+
var expression = pop() as ExpressionImpl;
3086+
whenClause = WhenClauseImpl(whenKeyword: when, expression: expression);
3087+
}
3088+
var pattern = pop() as DartPatternImpl;
3089+
push(SwitchExpressionCaseImpl(
3090+
guardedPattern: GuardedPatternImpl(
3091+
pattern: pattern,
3092+
whenClause: whenClause,
3093+
),
3094+
arrow: arrow,
3095+
expression: expression));
3096+
}
3097+
30433098
@override
30443099
void endSwitchStatement(Token switchKeyword, Token endToken) {
30453100
assert(optional('switch', switchKeyword));

pkg/analyzer/lib/src/test_utilities/find_node.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class FindNode {
3939
}
4040
},
4141
switchPatternCase: (node) => nodes.add(node.guardedPattern),
42+
switchExpressionCase: (node) => nodes.add(node.guardedPattern),
4243
),
4344
);
4445
return nodes.single;

pkg/analyzer/lib/src/test_utilities/function_ast_visitor.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
1616
final void Function(Label)? label;
1717
final void Function(MethodInvocation)? methodInvocation;
1818
final void Function(SimpleIdentifier)? simpleIdentifier;
19+
final void Function(SwitchExpressionCase)? switchExpressionCase;
1920
final void Function(SwitchPatternCase)? switchPatternCase;
2021
final void Function(VariableDeclaration)? variableDeclaration;
2122

@@ -28,6 +29,7 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
2829
this.label,
2930
this.methodInvocation,
3031
this.simpleIdentifier,
32+
this.switchExpressionCase,
3133
this.switchPatternCase,
3234
this.variableDeclaration,
3335
});
@@ -94,6 +96,12 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
9496
super.visitSimpleIdentifier(node);
9597
}
9698

99+
@override
100+
void visitSwitchExpressionCase(SwitchExpressionCase node) {
101+
switchExpressionCase?.call(node);
102+
super.visitSwitchExpressionCase(node);
103+
}
104+
97105
@override
98106
void visitSwitchPatternCase(SwitchPatternCase node) {
99107
switchPatternCase?.call(node);

0 commit comments

Comments
 (0)