@@ -29,12 +29,17 @@ class NamedType<Type extends Object> {
29
29
NamedType (this .name, this .type);
30
30
}
31
31
32
- class RecordPatternField <Pattern extends Object > {
32
+ class RecordPatternField <Node extends Object , Pattern extends Object > {
33
+ /// The client specific node from which this object was created. It can be
34
+ /// used for error reporting.
35
+ final Node node;
36
+
33
37
/// If not `null` then the field is named, otherwise it is positional.
34
38
final String ? name;
35
39
final Pattern pattern;
36
40
37
41
RecordPatternField ({
42
+ required this .node,
38
43
required this .name,
39
44
required this .pattern,
40
45
});
@@ -341,6 +346,33 @@ mixin TypeAnalyzer<
341
346
_analyzeIfCommon (node, ifTrue, ifFalse);
342
347
}
343
348
349
+ /// Analyzes a collection element of the form `if (condition) ifTrue` or
350
+ /// `if (condition) ifTrue else ifFalse` .
351
+ ///
352
+ /// [node] should be the AST node for the entire element, [condition] for
353
+ /// the condition expression, [ifTrue] for the "then" branch, and [ifFalse]
354
+ /// for the "else" branch (if present).
355
+ ///
356
+ /// Stack effect: pushes (Expression condition, CollectionElement ifTrue,
357
+ /// CollectionElement ifFalse). Note that if there is no `else` clause, the
358
+ /// representation for `ifFalse` will be pushed by
359
+ /// [handleNoCollectionElement] .
360
+ void analyzeIfElement ({
361
+ required Node node,
362
+ required Expression condition,
363
+ required Node ifTrue,
364
+ required Node ? ifFalse,
365
+ required Object ? context,
366
+ }) {
367
+ // Stack: ()
368
+ flow? .ifStatement_conditionBegin ();
369
+ analyzeExpression (condition, boolType);
370
+ handle_ifElement_conditionEnd (node);
371
+ // Stack: (Expression condition)
372
+ flow? .ifStatement_thenBegin (condition, node);
373
+ _analyzeIfElementCommon (node, ifTrue, ifFalse, context);
374
+ }
375
+
344
376
/// Analyzes a statement of the form `if (condition) ifTrue` or
345
377
/// `if (condition) ifTrue else ifFalse` .
346
378
///
@@ -587,7 +619,7 @@ mixin TypeAnalyzer<
587
619
MatchContext <Node , Expression > context,
588
620
Pattern node, {
589
621
required Type ? requiredType,
590
- required List <RecordPatternField <Pattern >> fields,
622
+ required List <RecordPatternField <Node , Pattern >> fields,
591
623
}) {
592
624
requiredType ?? = downwardInferObjectPatternRequiredType (
593
625
matchedType: matchedType,
@@ -606,7 +638,7 @@ mixin TypeAnalyzer<
606
638
}
607
639
608
640
// Stack: ()
609
- for (RecordPatternField <Pattern > field in fields) {
641
+ for (RecordPatternField <Node , Pattern > field in fields) {
610
642
Type propertyType = resolveObjectPatternPropertyGet (
611
643
receiverType: requiredType,
612
644
field: field,
@@ -638,9 +670,12 @@ mixin TypeAnalyzer<
638
670
Map <Variable , VariableTypeInfo <Pattern , Type >> typeInfos,
639
671
MatchContext <Node , Expression > context,
640
672
Pattern node, {
641
- required List <RecordPatternField <Pattern >> fields,
673
+ required List <RecordPatternField <Node , Pattern >> fields,
642
674
}) {
643
- void dispatchField (RecordPatternField <Pattern > field, Type matchedType) {
675
+ void dispatchField (
676
+ RecordPatternField <Node , Pattern > field,
677
+ Type matchedType,
678
+ ) {
644
679
dispatchPattern (matchedType, typeInfos, context, field.pattern);
645
680
}
646
681
@@ -653,7 +688,7 @@ mixin TypeAnalyzer<
653
688
// Build the required type.
654
689
int requiredTypePositionalCount = 0 ;
655
690
List <NamedType <Type >> requiredTypeNamedTypes = [];
656
- for (RecordPatternField <Pattern > field in fields) {
691
+ for (RecordPatternField <Node , Pattern > field in fields) {
657
692
String ? name = field.name;
658
693
if (name == null ) {
659
694
requiredTypePositionalCount++ ;
@@ -709,11 +744,11 @@ mixin TypeAnalyzer<
709
744
///
710
745
/// Stack effect: none.
711
746
Type analyzeRecordPatternSchema ({
712
- required List <RecordPatternField <Pattern >> fields,
747
+ required List <RecordPatternField <Node , Pattern >> fields,
713
748
}) {
714
749
List <Type > positional = [];
715
750
List <NamedType <Type >> named = [];
716
- for (RecordPatternField <Pattern > field in fields) {
751
+ for (RecordPatternField <Node , Pattern > field in fields) {
717
752
Type fieldType = dispatchPatternSchema (field.pattern);
718
753
String ? name = field.name;
719
754
if (name != null ) {
@@ -1036,6 +1071,15 @@ mixin TypeAnalyzer<
1036
1071
/// If [type] is a record type, returns it.
1037
1072
RecordType <Type >? asRecordType (Type type);
1038
1073
1074
+ /// Calls the appropriate `analyze` method according to the form of
1075
+ /// collection [element] , and then adjusts the stack as needed to combine
1076
+ /// any sub-structures into a single collection element.
1077
+ ///
1078
+ /// For example, if [element] is an `if` element, calls [analyzeIfElement] .
1079
+ ///
1080
+ /// Stack effect: pushes (CollectionElement).
1081
+ void dispatchCollectionElement (Node element, Object ? context);
1082
+
1039
1083
/// Calls the appropriate `analyze` method according to the form of
1040
1084
/// [expression] , and then adjusts the stack as needed to combine any
1041
1085
/// sub-structures into a single expression.
@@ -1121,6 +1165,15 @@ mixin TypeAnalyzer<
1121
1165
SwitchStatementMemberInfo <Node , Statement , Expression >
1122
1166
getSwitchStatementMemberInfo (Statement node, int caseIndex);
1123
1167
1168
+ /// Called after visiting the expression of an `if` element.
1169
+ void handle_ifElement_conditionEnd (Node node) {}
1170
+
1171
+ /// Called after visiting the `else` element of an `if` element.
1172
+ void handle_ifElement_elseEnd (Node node, Node ifFalse) {}
1173
+
1174
+ /// Called after visiting the `then` element of an `if` element.
1175
+ void handle_ifElement_thenEnd (Node node, Node ifTrue) {}
1176
+
1124
1177
/// Called after visiting the expression of an `if` statement.
1125
1178
void handle_ifStatement_conditionEnd (Statement node) {}
1126
1179
@@ -1170,6 +1223,13 @@ mixin TypeAnalyzer<
1170
1223
required int executionPathIndex,
1171
1224
required int numStatements});
1172
1225
1226
+ /// Called when visiting a syntactic construct where there is an implicit
1227
+ /// no-op collection element. For example, this is called in place of the
1228
+ /// missing `else` part of an `if` element that lacks an `else` clause.
1229
+ ///
1230
+ /// Stack effect: pushes (CollectionElement).
1231
+ void handleNoCollectionElement (Node node);
1232
+
1173
1233
/// Called when visiting a `case` that lacks a guard clause. Since the lack
1174
1234
/// of a guard clause is semantically equivalent to `when true` , this method
1175
1235
/// should behave similarly to visiting the boolean literal `true` .
@@ -1220,7 +1280,7 @@ mixin TypeAnalyzer<
1220
1280
/// should report an error, and return `dynamic` for recovery.
1221
1281
Type resolveObjectPatternPropertyGet ({
1222
1282
required Type receiverType,
1223
- required RecordPatternField <Pattern > field,
1283
+ required RecordPatternField <Node , Pattern > field,
1224
1284
});
1225
1285
1226
1286
/// Records that type inference has assigned a [type] to a [variable] . This
@@ -1253,6 +1313,29 @@ mixin TypeAnalyzer<
1253
1313
// Stack: (Statement ifTrue, Statement ifFalse)
1254
1314
}
1255
1315
1316
+ /// Common functionality shared by [analyzeIfElement] and
1317
+ /// [analyzeIfCaseElement] .
1318
+ ///
1319
+ /// Stack effect: pushes (CollectionElement ifTrue,
1320
+ /// CollectionElement ifFalse).
1321
+ void _analyzeIfElementCommon (
1322
+ Node node, Node ifTrue, Node ? ifFalse, Object ? context) {
1323
+ // Stack: ()
1324
+ dispatchCollectionElement (ifTrue, context);
1325
+ handle_ifElement_thenEnd (node, ifTrue);
1326
+ // Stack: (CollectionElement ifTrue)
1327
+ if (ifFalse == null ) {
1328
+ handleNoCollectionElement (node);
1329
+ flow? .ifStatement_end (false );
1330
+ } else {
1331
+ flow? .ifStatement_elseBegin ();
1332
+ dispatchCollectionElement (ifFalse, context);
1333
+ flow? .ifStatement_end (true );
1334
+ handle_ifElement_elseEnd (node, ifFalse);
1335
+ }
1336
+ // Stack: (CollectionElement ifTrue, CollectionElement ifFalse)
1337
+ }
1338
+
1256
1339
void _checkGuardType (Expression expression, Type type) {
1257
1340
// TODO(paulberry): harmonize this with analyzer's checkForNonBoolExpression
1258
1341
// TODO(paulberry): spec says the type must be `bool` or `dynamic`. This
@@ -1267,7 +1350,7 @@ mixin TypeAnalyzer<
1267
1350
/// [matchedType] , returns matched types for each field in [fields] .
1268
1351
/// Otherwise returns `null` .
1269
1352
List <Type >? _matchRecordTypeShape (
1270
- List <RecordPatternField <Pattern >> fields,
1353
+ List <RecordPatternField <Node , Pattern >> fields,
1271
1354
RecordType <Type > matchedType,
1272
1355
) {
1273
1356
Map <String , Type > matchedTypeNamed = {};
@@ -1278,7 +1361,7 @@ mixin TypeAnalyzer<
1278
1361
List <Type > result = [];
1279
1362
int positionalIndex = 0 ;
1280
1363
int namedCount = 0 ;
1281
- for (RecordPatternField <Pattern > field in fields) {
1364
+ for (RecordPatternField <Node , Pattern > field in fields) {
1282
1365
Type ? fieldType;
1283
1366
String ? name = field.name;
1284
1367
if (name != null ) {
0 commit comments