@@ -14,29 +14,36 @@ class ExpressionCaseInfo<Expression, Node> {
14
14
/// For a `case` clause, the case pattern. For a `default` clause, `null` .
15
15
final Node ? pattern;
16
16
17
+ /// For a `case` clause that has a `when` part, the expression following
18
+ /// `when` . Otherwise `null` .
19
+ final Expression ? when ;
20
+
17
21
/// The body of the `case` or `default` clause.
18
22
final Expression body;
19
23
20
- ExpressionCaseInfo (this .pattern, this .body);
24
+ ExpressionCaseInfo ({ required this .pattern, this .when , required this . body} );
21
25
}
22
26
23
27
/// Information supplied by the client to [TypeAnalyzer.analyzeSwitchStatement]
24
28
/// about an individual `case` or `default` clause.
25
29
///
26
30
/// The client is free to `implement` or `extend` this class.
27
- class StatementCaseInfo <Statement , Node > {
31
+ class StatementCaseInfo <Statement , Expression , Node > {
28
32
/// The AST node for this `case` or `default` clause. This is used for error
29
33
/// reporting, in case errors arise from mismatch among the variables bound by
30
34
/// various cases that share a body.
31
35
final Node node;
32
36
33
- /// Indicates whether this `case` or `default` clause is preceded by one or
34
- /// more `goto` labels.
35
- final bool hasLabel;
37
+ /// The labels preceding this `case` or `default` clause, if any.
38
+ final List <Node > labels;
36
39
37
40
/// For a `case` clause, the case pattern. For a `default` clause, `null` .
38
41
final Node ? pattern;
39
42
43
+ /// For a `case` clause that has a `when` part, the expression following
44
+ /// `when` . Otherwise `null` .
45
+ final Expression ? when ;
46
+
40
47
/// The statements following this `case` or `default` clause. If this list is
41
48
/// empty, and this is not the last `case` or `default` clause, this clause
42
49
/// will be considered to share a body with the `case` or `default` clause
@@ -45,8 +52,9 @@ class StatementCaseInfo<Statement, Node> {
45
52
46
53
StatementCaseInfo (
47
54
{required this .node,
48
- required this .hasLabel ,
55
+ this .labels = const [] ,
49
56
required this .pattern,
57
+ this .when ,
50
58
required this .body});
51
59
}
52
60
@@ -70,6 +78,9 @@ class StatementCaseInfo<Statement, Node> {
70
78
mixin TypeAnalyzer <Node extends Object , Statement extends Node ,
71
79
Expression extends Node , Variable extends Object , Type extends Object >
72
80
implements VariableBindingCallbacks <Node , Variable , Type > {
81
+ /// Returns the type `bool` .
82
+ Type get boolType;
83
+
73
84
/// Returns the type `double` .
74
85
Type get doubleType;
75
86
@@ -175,6 +186,8 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
175
186
/// - [analyzeExpression]
176
187
/// - For each `case` or `default` clause:
177
188
/// - [dispatchPattern] if this is a `case` clause
189
+ /// - [analyzeExpression] if this is a `case` clause with a `when` part
190
+ /// - [handleCaseHead] if this is a `case` clause
178
191
/// - [handleDefault] if this is a `default` clause
179
192
/// - [handleCase_afterCaseHeads]
180
193
/// - [analyzeExpression]
@@ -196,10 +209,17 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
196
209
if (pattern != null ) {
197
210
dispatchPattern (pattern)
198
211
.match (expressionType, bindings, isFinal: true , isLate: false );
212
+ Expression ? when = caseInfo.when ;
213
+ bool hasWhen = when != null ;
214
+ if (hasWhen) {
215
+ analyzeExpression (when , boolType);
216
+ flow? .switchStatement_afterWhen (when );
217
+ }
218
+ handleCaseHead (hasWhen: hasWhen);
199
219
} else {
200
220
handleDefault ();
201
221
}
202
- handleCase_afterCaseHeads (1 );
222
+ handleCase_afterCaseHeads (const [], 1 );
203
223
Type type = analyzeExpression (caseInfo.body, context);
204
224
if (lubType == null ) {
205
225
lubType = type;
@@ -217,8 +237,11 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
217
237
/// Invokes the following [TypeAnalyzer] methods (in order):
218
238
/// - [dispatchExpression]
219
239
/// - For each `case` or `default` body:
220
- /// - [dispatchPattern] for each `case` pattern associated with the body
221
- /// - [handleDefault] if a `default` clause is associated with the body
240
+ /// - For each `case` or `default` clause associated with the body:
241
+ /// - [dispatchPattern] if this is a `case` clause
242
+ /// - [analyzeExpression] if this is a `case` clause with a `when` part
243
+ /// - [handleCaseHead] if this is a `case` clause
244
+ /// - [handleDefault] if this is a `default` clause
222
245
/// - [handleCase_afterCaseHeads]
223
246
/// - [dispatchStatement] for each statement in the body
224
247
/// - [finishStatementCase]
@@ -228,42 +251,65 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
228
251
/// length of [cases] because a case with no statements get merged into the
229
252
/// case that follows).
230
253
int analyzeSwitchStatement (Statement node, Expression scrutinee,
231
- List <StatementCaseInfo <Statement , Node >> cases) {
254
+ List <StatementCaseInfo <Statement , Expression , Node >> cases) {
232
255
Type expressionType = analyzeExpression (scrutinee, unknownType);
233
256
flow? .switchStatement_expressionEnd (node);
234
- bool hasLabel = false ;
235
- List <StatementCaseInfo <Statement , Node >>? casesInThisExecutionPath;
257
+ List <Node > labels = [];
258
+ List <StatementCaseInfo <Statement , Expression , Node >>?
259
+ casesInThisExecutionPath;
236
260
int numExecutionPaths = 0 ;
237
261
for (int i = 0 ; i < cases.length; i++ ) {
238
- StatementCaseInfo <Statement , Node > caseInfo = cases[i];
239
- hasLabel = hasLabel || caseInfo.hasLabel ;
262
+ StatementCaseInfo <Statement , Expression , Node > caseInfo = cases[i];
263
+ labels. addAll ( caseInfo.labels) ;
240
264
(casesInThisExecutionPath ?? = []).add (caseInfo);
241
265
if (i == cases.length - 1 || caseInfo.body.isNotEmpty) {
242
266
numExecutionPaths++ ;
243
- flow? .switchStatement_beginCase (hasLabel , node);
267
+ flow? .switchStatement_beginCase (labels.isNotEmpty , node);
244
268
VariableBindings <Node , Variable , Type > bindings =
245
269
new VariableBindings (this );
246
270
bindings.startAlternatives ();
247
- for (int i = 0 ; i < casesInThisExecutionPath.length; i++ ) {
248
- StatementCaseInfo <Statement , Node > caseInfo =
271
+ // Labels count as empty patterns for the purposes of bindings.
272
+ for (Node label in labels) {
273
+ bindings.startAlternative (label);
274
+ bindings.finishAlternative ();
275
+ }
276
+ int numCasesInThisExecutionPath = casesInThisExecutionPath.length;
277
+ if (numCasesInThisExecutionPath > 1 ) {
278
+ flow? .switchStatement_beginAlternatives ();
279
+ }
280
+ for (int i = 0 ; i < numCasesInThisExecutionPath; i++ ) {
281
+ StatementCaseInfo <Statement , Expression , Node > caseInfo =
249
282
casesInThisExecutionPath[i];
250
283
bindings.startAlternative (caseInfo.node);
251
284
Node ? pattern = caseInfo.pattern;
252
285
if (pattern != null ) {
253
286
dispatchPattern (pattern)
254
287
.match (expressionType, bindings, isFinal: true , isLate: false );
288
+ Expression ? when = caseInfo.when ;
289
+ bool hasWhen = when != null ;
290
+ if (hasWhen) {
291
+ analyzeExpression (when , boolType);
292
+ flow? .switchStatement_afterWhen (when );
293
+ }
294
+ handleCaseHead (hasWhen: hasWhen);
255
295
} else {
256
296
handleDefault ();
257
297
}
258
298
bindings.finishAlternative ();
299
+ if (numCasesInThisExecutionPath > 1 ) {
300
+ flow? .switchStatement_endAlternative ();
301
+ }
259
302
}
260
303
bindings.finishAlternatives ();
261
- handleCase_afterCaseHeads (casesInThisExecutionPath.length);
304
+ if (numCasesInThisExecutionPath > 1 ) {
305
+ flow? .switchStatement_endAlternatives ();
306
+ }
307
+ handleCase_afterCaseHeads (labels, numCasesInThisExecutionPath);
262
308
for (Statement statement in caseInfo.body) {
263
309
dispatchStatement (statement);
264
310
}
265
311
finishStatementCase (node, i, caseInfo.body.length);
266
- hasLabel = false ;
312
+ labels. clear () ;
267
313
casesInThisExecutionPath = null ;
268
314
}
269
315
}
@@ -334,7 +380,10 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
334
380
void finishStatementCase (Statement node, int caseIndex, int numStatements);
335
381
336
382
/// See [analyzeSwitchStatement] and [analyzeSwitchExpression] .
337
- void handleCase_afterCaseHeads (int numHeads);
383
+ void handleCase_afterCaseHeads (List <Node > labels, int numHeads);
384
+
385
+ /// See [analyzeSwitchStatement] and [analyzeSwitchExpression] .
386
+ void handleCaseHead ({required bool hasWhen});
338
387
339
388
/// See [analyzeConstOrLiteralPattern] .
340
389
void handleConstOrLiteralPattern ();
0 commit comments