@@ -722,6 +722,56 @@ pub fn isTypeIdent(text: []const u8) bool {
722
722
return true ;
723
723
}
724
724
725
+ const FindBreaks = struct {
726
+ const Error = error {OutOfMemory };
727
+
728
+ label : ? []const u8 ,
729
+ allow_unlabeled : bool ,
730
+ allocator : std.mem.Allocator ,
731
+ break_operands : std .ArrayListUnmanaged (Ast .Node .Index ) = .{},
732
+
733
+ fn deinit (context : * FindBreaks ) void {
734
+ context .break_operands .deinit (context .allocator );
735
+ }
736
+
737
+ fn findBreakOperands (context : * FindBreaks , tree : Ast , node : Ast.Node.Index ) Error ! void {
738
+ if (node == 0 )
739
+ return ;
740
+
741
+ const allow_unlabeled = context .allow_unlabeled ;
742
+ const node_tags = tree .nodes .items (.tag );
743
+ const datas = tree .nodes .items (.data );
744
+
745
+ switch (node_tags [node ]) {
746
+ .@"break" = > {
747
+ const label_token = datas [node ].lhs ;
748
+ const operand = datas [node ].rhs ;
749
+ if (allow_unlabeled and label_token == 0 ) {
750
+ try context .break_operands .append (context .allocator , operand );
751
+ } else if (context .label ) | label | {
752
+ if (label_token != 0 and std .mem .eql (u8 , label , tree .tokenSlice (label_token )))
753
+ try context .break_operands .append (context .allocator , operand );
754
+ }
755
+ },
756
+
757
+ .@"while" ,
758
+ .while_simple ,
759
+ .while_cont ,
760
+ .@"for" ,
761
+ .for_simple ,
762
+ = > {
763
+ context .allow_unlabeled = false ;
764
+ try ast .iterateChildren (tree , node , context , Error , findBreakOperands );
765
+ context .allow_unlabeled = allow_unlabeled ;
766
+ },
767
+
768
+ else = > {
769
+ try ast .iterateChildren (tree , node , context , Error , findBreakOperands );
770
+ },
771
+ }
772
+ }
773
+ };
774
+
725
775
/// Resolves the type of a node
726
776
fn resolveTypeOfNodeInternal (analyser : * Analyser , node_handle : NodeWithHandle ) error {OutOfMemory }! ? TypeWithHandle {
727
777
const node_with_uri = NodeWithUri {
@@ -1187,6 +1237,50 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
1187
1237
1188
1238
return TypeWithHandle .fromEither (analyser .gpa , try either .toOwnedSlice (analyser .arena .allocator ()), handle );
1189
1239
},
1240
+ .@"while" ,
1241
+ .while_simple ,
1242
+ .while_cont ,
1243
+ .@"for" ,
1244
+ .for_simple ,
1245
+ = > {
1246
+ const loop : struct {
1247
+ label_token : ? Ast.TokenIndex ,
1248
+ then_expr : Ast.Node.Index ,
1249
+ else_expr : Ast.Node.Index ,
1250
+ } = if (tree .fullWhile (node )) | while_node |
1251
+ .{
1252
+ .label_token = while_node .label_token ,
1253
+ .then_expr = while_node .ast .then_expr ,
1254
+ .else_expr = while_node .ast .else_expr ,
1255
+ }
1256
+ else if (tree .fullFor (node )) | for_node |
1257
+ .{
1258
+ .label_token = for_node .label_token ,
1259
+ .then_expr = for_node .ast .then_expr ,
1260
+ .else_expr = for_node .ast .else_expr ,
1261
+ }
1262
+ else
1263
+ unreachable ;
1264
+
1265
+ if (loop .else_expr == 0 )
1266
+ return null ;
1267
+
1268
+ // TODO: peer type resolution based on `else` and all `break` statements
1269
+ if (try analyser .resolveTypeOfNodeInternal (.{ .node = loop .else_expr , .handle = handle })) | else_type |
1270
+ return else_type ;
1271
+
1272
+ var context = FindBreaks {
1273
+ .label = if (loop .label_token ) | token | tree .tokenSlice (token ) else null ,
1274
+ .allow_unlabeled = true ,
1275
+ .allocator = analyser .gpa ,
1276
+ };
1277
+ defer context .deinit ();
1278
+ try context .findBreakOperands (tree , loop .then_expr );
1279
+ for (context .break_operands .items ) | operand | {
1280
+ if (try analyser .resolveTypeOfNodeInternal (.{ .node = operand , .handle = handle })) | operand_type |
1281
+ return operand_type ;
1282
+ }
1283
+ },
1190
1284
.block ,
1191
1285
.block_semicolon ,
1192
1286
.block_two ,
@@ -1197,21 +1291,17 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
1197
1291
1198
1292
const block_label = tree .tokenSlice (first_token );
1199
1293
1200
- var buffer : [2 ]Ast.Node.Index = undefined ;
1201
- const statements = ast .blockStatements (tree , node , & buffer ).? ;
1202
-
1203
- for (statements ) | child_idx | {
1204
- // TODO: Recursively find matching `break :label` (e.g. inside `if`)
1205
- if (node_tags [child_idx ] == .@"break" ) {
1206
- if (datas [child_idx ].lhs == 0 ) continue ;
1207
- if (datas [child_idx ].rhs == 0 ) continue ;
1208
-
1209
- const break_label = tree .tokenSlice (datas [child_idx ].lhs );
1210
- if (! std .mem .eql (u8 , block_label , break_label )) continue ;
1211
-
1212
- const operand = .{ .node = datas [child_idx ].rhs , .handle = handle };
1213
- return try analyser .resolveTypeOfNodeInternal (operand );
1214
- }
1294
+ // TODO: peer type resolution based on all `break` statements
1295
+ var context = FindBreaks {
1296
+ .label = block_label ,
1297
+ .allow_unlabeled = false ,
1298
+ .allocator = analyser .gpa ,
1299
+ };
1300
+ defer context .deinit ();
1301
+ try context .findBreakOperands (tree , node );
1302
+ for (context .break_operands .items ) | operand | {
1303
+ if (try analyser .resolveTypeOfNodeInternal (.{ .node = operand , .handle = handle })) | operand_type |
1304
+ return operand_type ;
1215
1305
}
1216
1306
},
1217
1307
else = > {},
0 commit comments