Skip to content

Commit 9aab3c7

Browse files
stereotype441Commit Queue
authored and
Commit Queue
committed
Patterns parsing: add support for guards.
Bug: #50035 Change-Id: I21db09c9d8c1d359e1b125eea2bae2749cdb72fd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/264101 Reviewed-by: Jens Johansen <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 4cf63fb commit 9aab3c7

File tree

48 files changed

+1649
-9
lines changed

Some content is hidden

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

48 files changed

+1649
-9
lines changed

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6240,8 +6240,14 @@ class Parser {
62406240
if (allowPatterns && optional('case', next)) {
62416241
Token case_ = token = next;
62426242
token = parsePattern(token);
6243+
next = token.next!;
6244+
Token? when;
6245+
if (optional('when', next)) {
6246+
when = token = next;
6247+
token = parseExpression(token);
6248+
}
62436249
token = ensureCloseParen(token, begin);
6244-
listener.handleParenthesizedCondition(begin, case_, null);
6250+
listener.handleParenthesizedCondition(begin, case_, when);
62456251
} else {
62466252
token = ensureCloseParen(token, begin);
62476253
listener.handleParenthesizedCondition(
@@ -8258,9 +8264,15 @@ class Parser {
82588264
} else {
82598265
token = parseExpression(caseKeyword);
82608266
}
8267+
Token? next = token.next!;
8268+
Token? when;
8269+
if (optional('when', next)) {
8270+
when = token = next;
8271+
token = parseExpression(token);
8272+
}
82618273
token = ensureColon(token);
8262-
listener.endCaseExpression(null, token);
8263-
listener.handleCaseMatch(caseKeyword, null, token);
8274+
listener.endCaseExpression(when, token);
8275+
listener.handleCaseMatch(caseKeyword, when, token);
82648276
expressionCount++;
82658277
peek = peekPastLabels(token.next!);
82668278
} else if (expressionCount > 0) {

pkg/_fe_analyzer_shared/lib/src/scanner/token.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,17 @@ class Keyword extends TokenType {
327327
static const Keyword VOID =
328328
const Keyword(/* index = */ 145, "void", "VOID", KeywordStyle.reserved);
329329

330+
static const Keyword WHEN =
331+
const Keyword(/* index = */ 146, "when", 'WHEN', KeywordStyle.pseudo);
332+
330333
static const Keyword WHILE =
331-
const Keyword(/* index = */ 146, "while", "WHILE", KeywordStyle.reserved);
334+
const Keyword(/* index = */ 147, "while", "WHILE", KeywordStyle.reserved);
332335

333336
static const Keyword WITH =
334-
const Keyword(/* index = */ 147, "with", "WITH", KeywordStyle.reserved);
337+
const Keyword(/* index = */ 148, "with", "WITH", KeywordStyle.reserved);
335338

336339
static const Keyword YIELD =
337-
const Keyword(/* index = */ 148, "yield", "YIELD", KeywordStyle.pseudo);
340+
const Keyword(/* index = */ 149, "yield", "YIELD", KeywordStyle.pseudo);
338341

339342
static const List<Keyword> values = const <Keyword>[
340343
ABSTRACT,
@@ -404,6 +407,7 @@ class Keyword extends TokenType {
404407
TYPEDEF,
405408
VAR,
406409
VOID,
410+
WHEN,
407411
WHILE,
408412
WITH,
409413
YIELD,
@@ -1978,6 +1982,7 @@ const List<TokenType> _tokenTypesByIndex = [
19781982
Keyword.TYPEDEF,
19791983
Keyword.VAR,
19801984
Keyword.VOID,
1985+
Keyword.WHEN,
19811986
Keyword.WHILE,
19821987
Keyword.WITH,
19831988
Keyword.YIELD,

pkg/analyzer/lib/src/dart/ast/ast.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,7 @@ class CaseClauseImpl extends AstNodeImpl implements CaseClause {
14831483
required this.whenClause,
14841484
}) {
14851485
_becomeParentOf(pattern);
1486+
_becomeParentOf(whenClause);
14861487
}
14871488

14881489
@override

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3282,12 +3282,17 @@ class AstBuilder extends StackListener {
32823282
debugEvent("CaseMatch");
32833283

32843284
if (_featureSet.isEnabled(Feature.patterns)) {
3285+
WhenClauseImpl? whenClause;
3286+
if (when != null) {
3287+
var expression = pop() as ExpressionImpl;
3288+
whenClause = WhenClauseImpl(whenKeyword: when, expression: expression);
3289+
}
32853290
var pattern = pop() as DartPatternImpl;
32863291
push(SwitchPatternCaseImpl(
32873292
labels: <Label>[],
32883293
keyword: caseKeyword,
32893294
pattern: pattern,
3290-
whenClause: null,
3295+
whenClause: whenClause,
32913296
colon: colon,
32923297
statements: <Statement>[]));
32933298
} else {
@@ -4561,10 +4566,14 @@ class AstBuilder extends StackListener {
45614566
ExpressionImpl condition;
45624567
CaseClauseImpl? caseClause;
45634568
if (case_ != null) {
4564-
// TODO(paulberry): what about a guard?
4569+
WhenClauseImpl? whenClause;
4570+
if (when != null) {
4571+
var expression = pop() as ExpressionImpl;
4572+
whenClause = WhenClauseImpl(whenKeyword: when, expression: expression);
4573+
}
45654574
var pattern = pop() as DartPatternImpl;
45664575
caseClause = CaseClauseImpl(
4567-
caseKeyword: case_, pattern: pattern, whenClause: null);
4576+
caseKeyword: case_, pattern: pattern, whenClause: whenClause);
45684577
}
45694578
condition = pop() as ExpressionImpl;
45704579
push(_ParenthesizedCondition(leftParenthesis, condition, caseClause));

pkg/analyzer/test/generated/patterns_parser_test.dart

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,238 @@ main() {
2121
class PatternsTest extends ParserDiagnosticsTest {
2222
late FindNode findNode;
2323

24+
test_caseHead_withClassicPattern_guarded_insideIfStatement() {
25+
_parse('''
26+
void f(x) {
27+
if (x case 0 when true) {}
28+
}
29+
''');
30+
var node = findNode.ifStatement('case');
31+
assertParsedNodeText(node, r'''
32+
IfStatement
33+
ifKeyword: if
34+
leftParenthesis: (
35+
condition: SimpleIdentifier
36+
token: x
37+
caseClause: CaseClause
38+
caseKeyword: case
39+
pattern: ConstantPattern
40+
expression: IntegerLiteral
41+
literal: 0
42+
whenClause: WhenClause
43+
whenKeyword: when
44+
expression: BooleanLiteral
45+
literal: true
46+
rightParenthesis: )
47+
thenStatement: Block
48+
leftBracket: {
49+
rightBracket: }
50+
''');
51+
}
52+
53+
test_caseHead_withClassicPattern_guarded_insideSwitchStatement() {
54+
_parse('''
55+
void f(x) {
56+
switch (x) {
57+
case 0 when true:
58+
break;
59+
}
60+
}
61+
''');
62+
var node = findNode.switchPatternCase('case');
63+
assertParsedNodeText(node, r'''
64+
SwitchPatternCase
65+
keyword: case
66+
pattern: ConstantPattern
67+
expression: IntegerLiteral
68+
literal: 0
69+
whenClause: WhenClause
70+
whenKeyword: when
71+
expression: BooleanLiteral
72+
literal: true
73+
colon: :
74+
statements
75+
BreakStatement
76+
breakKeyword: break
77+
semicolon: ;
78+
''');
79+
}
80+
81+
test_caseHead_withClassicPattern_unguarded_insideIfStatement() {
82+
_parse('''
83+
void f(x) {
84+
if (x case 0) {}
85+
}
86+
''');
87+
var node = findNode.ifStatement('case');
88+
assertParsedNodeText(node, r'''
89+
IfStatement
90+
ifKeyword: if
91+
leftParenthesis: (
92+
condition: SimpleIdentifier
93+
token: x
94+
caseClause: CaseClause
95+
caseKeyword: case
96+
pattern: ConstantPattern
97+
expression: IntegerLiteral
98+
literal: 0
99+
rightParenthesis: )
100+
thenStatement: Block
101+
leftBracket: {
102+
rightBracket: }
103+
''');
104+
}
105+
106+
test_caseHead_withClassicPattern_unguarded_insideSwitchStatement() {
107+
_parse('''
108+
void f(x) {
109+
switch (x) {
110+
case 0:
111+
break;
112+
}
113+
}
114+
''');
115+
var node = findNode.switchPatternCase('case');
116+
assertParsedNodeText(node, r'''
117+
SwitchPatternCase
118+
keyword: case
119+
pattern: ConstantPattern
120+
expression: IntegerLiteral
121+
literal: 0
122+
colon: :
123+
statements
124+
BreakStatement
125+
breakKeyword: break
126+
semicolon: ;
127+
''');
128+
}
129+
130+
test_caseHead_withNewPattern_guarded_insideIfStatement() {
131+
_parse('''
132+
void f(x) {
133+
if (x case 0 as int when true) {}
134+
}
135+
''');
136+
var node = findNode.ifStatement('case');
137+
assertParsedNodeText(node, r'''
138+
IfStatement
139+
ifKeyword: if
140+
leftParenthesis: (
141+
condition: SimpleIdentifier
142+
token: x
143+
caseClause: CaseClause
144+
caseKeyword: case
145+
pattern: CastPattern
146+
pattern: ConstantPattern
147+
expression: IntegerLiteral
148+
literal: 0
149+
asToken: as
150+
type: NamedType
151+
name: SimpleIdentifier
152+
token: int
153+
whenClause: WhenClause
154+
whenKeyword: when
155+
expression: BooleanLiteral
156+
literal: true
157+
rightParenthesis: )
158+
thenStatement: Block
159+
leftBracket: {
160+
rightBracket: }
161+
''');
162+
}
163+
164+
test_caseHead_withNewPattern_guarded_insideSwitchStatement() {
165+
_parse('''
166+
void f(x) {
167+
switch (x) {
168+
case 0 as int when true:
169+
break;
170+
}
171+
}
172+
''');
173+
var node = findNode.switchPatternCase('case');
174+
assertParsedNodeText(node, r'''
175+
SwitchPatternCase
176+
keyword: case
177+
pattern: CastPattern
178+
pattern: ConstantPattern
179+
expression: IntegerLiteral
180+
literal: 0
181+
asToken: as
182+
type: NamedType
183+
name: SimpleIdentifier
184+
token: int
185+
whenClause: WhenClause
186+
whenKeyword: when
187+
expression: BooleanLiteral
188+
literal: true
189+
colon: :
190+
statements
191+
BreakStatement
192+
breakKeyword: break
193+
semicolon: ;
194+
''');
195+
}
196+
197+
test_caseHead_withNewPattern_unguarded_insideIfStatement() {
198+
_parse('''
199+
void f(x) {
200+
if (x case 0 as int) {}
201+
}
202+
''');
203+
var node = findNode.ifStatement('case');
204+
assertParsedNodeText(node, r'''
205+
IfStatement
206+
ifKeyword: if
207+
leftParenthesis: (
208+
condition: SimpleIdentifier
209+
token: x
210+
caseClause: CaseClause
211+
caseKeyword: case
212+
pattern: CastPattern
213+
pattern: ConstantPattern
214+
expression: IntegerLiteral
215+
literal: 0
216+
asToken: as
217+
type: NamedType
218+
name: SimpleIdentifier
219+
token: int
220+
rightParenthesis: )
221+
thenStatement: Block
222+
leftBracket: {
223+
rightBracket: }
224+
''');
225+
}
226+
227+
test_caseHead_withNewPattern_unguarded_insideSwitchStatement() {
228+
_parse('''
229+
void f(x) {
230+
switch (x) {
231+
case 0 as int:
232+
break;
233+
}
234+
}
235+
''');
236+
var node = findNode.switchPatternCase('case');
237+
assertParsedNodeText(node, r'''
238+
SwitchPatternCase
239+
keyword: case
240+
pattern: CastPattern
241+
pattern: ConstantPattern
242+
expression: IntegerLiteral
243+
literal: 0
244+
asToken: as
245+
type: NamedType
246+
name: SimpleIdentifier
247+
token: int
248+
colon: :
249+
statements
250+
BreakStatement
251+
breakKeyword: break
252+
semicolon: ;
253+
''');
254+
}
255+
24256
test_cast_insideCase() {
25257
_parse('''
26258
void f(x) {

pkg/analyzer/test/src/summary/resolved_ast_printer.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,14 @@ class ResolvedAstPrinter extends ThrowingAstVisitor<void> {
14381438
});
14391439
}
14401440

1441+
@override
1442+
void visitWhenClause(WhenClause node) {
1443+
_writeln('WhenClause');
1444+
_withIndent(() {
1445+
_writeNamedChildEntities(node);
1446+
});
1447+
}
1448+
14411449
@override
14421450
void visitWhileStatement(WhileStatement node) {
14431451
_writeln('WhileStatement');

0 commit comments

Comments
 (0)