| ... | ... | @@ -532,13 +532,15 @@ const DocData = struct { |
| 532 | 532 | // directly refer to their return value. The problem at the moment |
| 533 | 533 | // is that we can't analyze function calls at all. |
| 534 | 534 | call: usize, // index in `calls` |
| 535 | typeOf: *WalkResult, |
| 535 | 536 | |
| 536 | 537 | pub fn jsonStringify( |
| 537 | 538 | self: TypeRef, |
| 538 | | _: std.json.StringifyOptions, |
| 539 | options: std.json.StringifyOptions, |
| 539 | 540 | w: anytype, |
| 540 | 541 | ) !void { |
| 541 | 542 | switch (self) { |
| 543 | .typeOf => |v| try std.json.stringify(v, options, w), |
| 542 | 544 | .unspecified, .@"anytype" => { |
| 543 | 545 | try w.print( |
| 544 | 546 | \\{{ "{s}":{{}} }} |
| ... | ... | @@ -591,13 +593,19 @@ const DocData = struct { |
| 591 | 593 | array: Array, |
| 592 | 594 | call: usize, // index in `calls` |
| 593 | 595 | enumLiteral: []const u8, |
| 596 | typeOf: *WalkResult, |
| 597 | sizeOf: *WalkResult, |
| 598 | compileError: []const u8, |
| 599 | string: []const u8, |
| 594 | 600 | |
| 595 | 601 | const Struct = struct { |
| 596 | 602 | typeRef: TypeRef, |
| 597 | | fieldVals: []struct { |
| 603 | fieldVals: []FieldVal, |
| 604 | |
| 605 | const FieldVal = struct { |
| 598 | 606 | name: []const u8, |
| 599 | 607 | val: WalkResult, |
| 600 | | }, |
| 608 | }; |
| 601 | 609 | }; |
| 602 | 610 | const Array = struct { |
| 603 | 611 | typeRef: TypeRef, |
| ... | ... | @@ -647,6 +655,9 @@ const DocData = struct { |
| 647 | 655 | }, |
| 648 | 656 | .@"undefined" => |v| try std.json.stringify(v, options, w), |
| 649 | 657 | .@"null" => |v| try std.json.stringify(v, options, w), |
| 658 | .typeOf, .sizeOf => |v| try std.json.stringify(v, options, w), |
| 659 | .compileError => |v| try std.json.stringify(v, options, w), |
| 660 | .string => |v| try std.json.stringify(v, options, w), |
| 650 | 661 | .@"struct" => |v| try std.json.stringify( |
| 651 | 662 | struct { @"struct": Struct }{ .@"struct" = v }, |
| 652 | 663 | options, |
| ... | ... | @@ -709,11 +720,21 @@ fn walkInstruction( |
| 709 | 720 | |
| 710 | 721 | switch (tags[inst_index]) { |
| 711 | 722 | else => { |
| 712 | | std.debug.panic( |
| 723 | panicWithContext( |
| 724 | file, |
| 725 | inst_index, |
| 713 | 726 | "TODO: implement `{s}` for walkInstruction\n\n", |
| 714 | 727 | .{@tagName(tags[inst_index])}, |
| 715 | 728 | ); |
| 716 | 729 | }, |
| 730 | .closure_get => { |
| 731 | const inst_node = data[inst_index].inst_node; |
| 732 | return try self.walkInstruction(file, parent_scope, inst_node.inst); |
| 733 | }, |
| 734 | .closure_capture => { |
| 735 | const un_tok = data[inst_index].un_tok; |
| 736 | return try self.walkRef(file, parent_scope, un_tok.operand); |
| 737 | }, |
| 717 | 738 | .import => { |
| 718 | 739 | const str_tok = data[inst_index].str_tok; |
| 719 | 740 | const path = str_tok.get(file.zir); |
| ... | ... | @@ -749,11 +770,46 @@ fn walkInstruction( |
| 749 | 770 | |
| 750 | 771 | return new_file_walk_result; |
| 751 | 772 | }, |
| 773 | .str => { |
| 774 | const str = data[inst_index].str; |
| 775 | return DocData.WalkResult{ |
| 776 | .string = str.get(file.zir), |
| 777 | }; |
| 778 | }, |
| 779 | .compile_error => { |
| 780 | const un_node = data[inst_index].un_node; |
| 781 | var operand: DocData.WalkResult = try self.walkRef( |
| 782 | file, |
| 783 | parent_scope, |
| 784 | un_node.operand, |
| 785 | ); |
| 786 | |
| 787 | return DocData.WalkResult{ .compileError = operand.string }; |
| 788 | }, |
| 789 | .switch_block => { |
| 790 | const cte_slot_index = self.comptime_exprs.items.len; |
| 791 | try self.comptime_exprs.append(self.arena, .{ |
| 792 | .code = "switch", |
| 793 | .typeRef = .{ |
| 794 | .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr), |
| 795 | }, |
| 796 | }); |
| 797 | |
| 798 | return DocData.WalkResult{ .comptimeExpr = cte_slot_index }; |
| 799 | }, |
| 752 | 800 | .enum_literal => { |
| 753 | 801 | const str_tok = data[inst_index].str_tok; |
| 754 | 802 | const literal = file.zir.nullTerminatedString(str_tok.start); |
| 755 | 803 | return DocData.WalkResult{ .enumLiteral = literal }; |
| 756 | 804 | }, |
| 805 | .div_exact, .div => { |
| 806 | const cte_slot_index = self.comptime_exprs.items.len; |
| 807 | try self.comptime_exprs.append(self.arena, .{ |
| 808 | .code = "@div*(...)", |
| 809 | .typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) }, |
| 810 | }); |
| 811 | return DocData.WalkResult{ .comptimeExpr = cte_slot_index }; |
| 812 | }, |
| 757 | 813 | .int => { |
| 758 | 814 | const int = data[inst_index].int; |
| 759 | 815 | return DocData.WalkResult{ |
| ... | ... | @@ -785,6 +841,25 @@ fn walkInstruction( |
| 785 | 841 | |
| 786 | 842 | return DocData.WalkResult{ .type = type_slot_index }; |
| 787 | 843 | }, |
| 844 | .ptr_type => { |
| 845 | const ptr = data[inst_index].ptr_type; |
| 846 | const extra = file.zir.extraData(Zir.Inst.PtrType, ptr.payload_index); |
| 847 | |
| 848 | const type_slot_index = self.types.items.len; |
| 849 | const elem_type_ref = try self.walkRef( |
| 850 | file, |
| 851 | parent_scope, |
| 852 | extra.data.elem_type, |
| 853 | ); |
| 854 | try self.types.append(self.arena, .{ |
| 855 | .Pointer = .{ |
| 856 | .size = ptr.size, |
| 857 | .child = walkResultToTypeRef(elem_type_ref), |
| 858 | }, |
| 859 | }); |
| 860 | |
| 861 | return DocData.WalkResult{ .type = type_slot_index }; |
| 862 | }, |
| 788 | 863 | .array_type => { |
| 789 | 864 | const bin = data[inst_index].bin; |
| 790 | 865 | const len = try self.walkRef(file, parent_scope, bin.lhs); |
| ... | ... | @@ -848,6 +923,27 @@ fn walkInstruction( |
| 848 | 923 | operand.int.negated = true; // only support ints for now |
| 849 | 924 | return operand; |
| 850 | 925 | }, |
| 926 | .size_of => { |
| 927 | const un_node = data[inst_index].un_node; |
| 928 | var operand = try self.arena.create(DocData.WalkResult); |
| 929 | operand.* = try self.walkRef( |
| 930 | file, |
| 931 | parent_scope, |
| 932 | un_node.operand, |
| 933 | ); |
| 934 | return DocData.WalkResult{ .sizeOf = operand }; |
| 935 | }, |
| 936 | |
| 937 | .typeof => { |
| 938 | const un_node = data[inst_index].un_node; |
| 939 | var operand = try self.arena.create(DocData.WalkResult); |
| 940 | operand.* = try self.walkRef( |
| 941 | file, |
| 942 | parent_scope, |
| 943 | un_node.operand, |
| 944 | ); |
| 945 | return DocData.WalkResult{ .typeOf = operand }; |
| 946 | }, |
| 851 | 947 | .as_node => { |
| 852 | 948 | const pl_node = data[inst_index].pl_node; |
| 853 | 949 | const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index); |
| ... | ... | @@ -857,11 +953,13 @@ fn walkInstruction( |
| 857 | 953 | var operand = try self.walkRef(file, parent_scope, extra.data.operand); |
| 858 | 954 | |
| 859 | 955 | switch (operand) { |
| 860 | | else => std.debug.panic( |
| 956 | else => panicWithContext( |
| 957 | file, |
| 958 | inst_index, |
| 861 | 959 | "TODO: handle {s} in `walkInstruction.as_node`\n", |
| 862 | 960 | .{@tagName(operand)}, |
| 863 | 961 | ), |
| 864 | | .declPath, .type => {}, |
| 962 | .declPath, .type, .string => {}, |
| 865 | 963 | // we don't do anything because up until now, |
| 866 | 964 | // I've only seen this used as such: |
| 867 | 965 | // @as(@as(type, Baz), .{}) |
| ... | ... | @@ -894,14 +992,16 @@ fn walkInstruction( |
| 894 | 992 | }); |
| 895 | 993 | return res; |
| 896 | 994 | }, |
| 897 | | .decl_val => { |
| 995 | .decl_val, .decl_ref => { |
| 898 | 996 | const str_tok = data[inst_index].str_tok; |
| 899 | 997 | const decls_slot_index = parent_scope.resolveDeclName(str_tok.start); |
| 900 | 998 | var path = try self.arena.alloc(usize, 1); |
| 901 | 999 | path[0] = decls_slot_index; |
| 902 | 1000 | return DocData.WalkResult{ .declPath = .{ .path = path } }; |
| 903 | 1001 | }, |
| 904 | | .field_val, .field_call_bind, .field_ptr => { |
| 1002 | .field_val, .field_call_bind, .field_ptr, .field_type => { |
| 1003 | // TODO: field type uses Zir.Inst.FieldType, it just happens to have the |
| 1004 | // same layout as Zir.Inst.Field :^) |
| 905 | 1005 | const pl_node = data[inst_index].pl_node; |
| 906 | 1006 | const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); |
| 907 | 1007 | |
| ... | ... | @@ -909,8 +1009,8 @@ fn walkInstruction( |
| 909 | 1009 | var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs |
| 910 | 1010 | |
| 911 | 1011 | try path.append(self.arena, extra.data.field_name_start); |
| 912 | | // Put inside path the starting index of each decl name |
| 913 | | // that we encounter as we navigate through all the field_vals |
| 1012 | // Put inside path the starting index of each decl name that |
| 1013 | // we encounter as we navigate through all the field_vals |
| 914 | 1014 | while (tags[lhs] == .field_val or |
| 915 | 1015 | tags[lhs] == .field_call_bind or |
| 916 | 1016 | tags[lhs] == .field_ptr) |
| ... | ... | @@ -925,11 +1025,35 @@ fn walkInstruction( |
| 925 | 1025 | } |
| 926 | 1026 | |
| 927 | 1027 | switch (tags[lhs]) { |
| 928 | | else => { |
| 929 | | std.debug.panic( |
| 930 | | "TODO: handle `{s}` endings in walkInstruction.field_val", |
| 931 | | .{@tagName(tags[lhs])}, |
| 932 | | ); |
| 1028 | else => panicWithContext( |
| 1029 | file, |
| 1030 | inst_index, |
| 1031 | "TODO: handle `{s}` in walkInstruction.field_val", |
| 1032 | .{@tagName(tags[lhs])}, |
| 1033 | ), |
| 1034 | .call => { |
| 1035 | const walk_result = try self.walkInstruction(file, parent_scope, lhs); |
| 1036 | const ast_node_index = idx: { |
| 1037 | const idx = self.ast_nodes.items.len; |
| 1038 | try self.ast_nodes.append(self.arena, .{ |
| 1039 | .file = 0, |
| 1040 | .line = 0, |
| 1041 | .col = 0, |
| 1042 | .docs = "", |
| 1043 | .fields = null, |
| 1044 | }); |
| 1045 | break :idx idx; |
| 1046 | }; |
| 1047 | |
| 1048 | const decls_slot_index = self.decls.items.len; |
| 1049 | try self.decls.append(self.arena, .{ |
| 1050 | ._analyzed = true, |
| 1051 | .name = "call()", |
| 1052 | .src = ast_node_index, |
| 1053 | .value = walk_result, |
| 1054 | .kind = "const", |
| 1055 | }); |
| 1056 | try path.append(self.arena, decls_slot_index); |
| 933 | 1057 | }, |
| 934 | 1058 | .import => { |
| 935 | 1059 | const walk_result = try self.walkInstruction(file, parent_scope, lhs); |
| ... | ... | @@ -942,7 +1066,7 @@ fn walkInstruction( |
| 942 | 1066 | .line = 0, |
| 943 | 1067 | .col = 0, |
| 944 | 1068 | .docs = "", |
| 945 | | .fields = null, // walkInstruction will fill `fields` if necessary |
| 1069 | .fields = null, |
| 946 | 1070 | }); |
| 947 | 1071 | break :idx idx; |
| 948 | 1072 | }; |
| ... | ... | @@ -999,6 +1123,76 @@ fn walkInstruction( |
| 999 | 1123 | .block_inline => { |
| 1000 | 1124 | return self.walkRef(file, parent_scope, getBlockInlineBreak(file.zir, inst_index)); |
| 1001 | 1125 | }, |
| 1126 | .struct_init => { |
| 1127 | const pl_node = data[inst_index].pl_node; |
| 1128 | const extra = file.zir.extraData(Zir.Inst.StructInit, pl_node.payload_index); |
| 1129 | const field_vals = try self.arena.alloc( |
| 1130 | DocData.WalkResult.Struct.FieldVal, |
| 1131 | extra.data.fields_len, |
| 1132 | ); |
| 1133 | |
| 1134 | var type_ref: DocData.TypeRef = undefined; |
| 1135 | var idx = extra.end; |
| 1136 | for (field_vals) |*fv| { |
| 1137 | const init_extra = file.zir.extraData(Zir.Inst.StructInit.Item, idx); |
| 1138 | idx = init_extra.end; |
| 1139 | |
| 1140 | const field_name = blk: { |
| 1141 | const field_inst_index = init_extra.data.field_type; |
| 1142 | if (tags[field_inst_index] != .field_type) unreachable; |
| 1143 | const field_pl_node = data[field_inst_index].pl_node; |
| 1144 | const field_extra = file.zir.extraData( |
| 1145 | Zir.Inst.FieldType, |
| 1146 | field_pl_node.payload_index, |
| 1147 | ); |
| 1148 | |
| 1149 | // On first iteration use field info to find out the struct type |
| 1150 | if (idx == extra.end) { |
| 1151 | const wr = try self.walkRef( |
| 1152 | file, |
| 1153 | parent_scope, |
| 1154 | field_extra.data.container_type, |
| 1155 | ); |
| 1156 | type_ref = walkResultToTypeRef(wr); |
| 1157 | } |
| 1158 | break :blk file.zir.nullTerminatedString(field_extra.data.name_start); |
| 1159 | }; |
| 1160 | |
| 1161 | const value = try self.walkRef(file, parent_scope, init_extra.data.init); |
| 1162 | fv.* = .{ .name = field_name, .val = value }; |
| 1163 | } |
| 1164 | |
| 1165 | return DocData.WalkResult{ .@"struct" = .{ |
| 1166 | .typeRef = type_ref, |
| 1167 | .fieldVals = field_vals, |
| 1168 | } }; |
| 1169 | }, |
| 1170 | .param_anytype => { |
| 1171 | // Analysis of anytype function params happens in `.func`. |
| 1172 | // This switch case handles the case where an expression depends |
| 1173 | // on an anytype field. E.g.: `fn foo(bar: anytype) @TypeOf(bar)`. |
| 1174 | // This means that we're looking at a generic expression. |
| 1175 | const str_tok = data[inst_index].str_tok; |
| 1176 | const name = str_tok.get(file.zir); |
| 1177 | const cte_slot_index = self.comptime_exprs.items.len; |
| 1178 | try self.comptime_exprs.append(self.arena, .{ |
| 1179 | .code = name, |
| 1180 | .typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) }, |
| 1181 | }); |
| 1182 | return DocData.WalkResult{ .comptimeExpr = cte_slot_index }; |
| 1183 | }, |
| 1184 | .param, .param_comptime => { |
| 1185 | // See .param_anytype for more information. |
| 1186 | const pl_tok = data[inst_index].pl_tok; |
| 1187 | const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index); |
| 1188 | const name = file.zir.nullTerminatedString(extra.data.name); |
| 1189 | const cte_slot_index = self.comptime_exprs.items.len; |
| 1190 | try self.comptime_exprs.append(self.arena, .{ |
| 1191 | .code = name, |
| 1192 | .typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) }, |
| 1193 | }); |
| 1194 | return DocData.WalkResult{ .comptimeExpr = cte_slot_index }; |
| 1195 | }, |
| 1002 | 1196 | .call => { |
| 1003 | 1197 | const pl_node = data[inst_index].pl_node; |
| 1004 | 1198 | const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index); |
| ... | ... | @@ -1048,12 +1242,12 @@ fn walkInstruction( |
| 1048 | 1242 | // TODO: handle scope rules for fn parameters |
| 1049 | 1243 | for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| { |
| 1050 | 1244 | switch (tags[param_index]) { |
| 1051 | | else => { |
| 1052 | | std.debug.panic( |
| 1053 | | "TODO: handle `{s}` in walkInstruction.func\n", |
| 1054 | | .{@tagName(tags[param_index])}, |
| 1055 | | ); |
| 1056 | | }, |
| 1245 | else => panicWithContext( |
| 1246 | file, |
| 1247 | param_index, |
| 1248 | "TODO: handle `{s}` in walkInstruction.func\n", |
| 1249 | .{@tagName(tags[param_index])}, |
| 1250 | ), |
| 1057 | 1251 | .param_anytype => { |
| 1058 | 1252 | // TODO: where are the doc comments? |
| 1059 | 1253 | const str_tok = data[param_index].str_tok; |
| ... | ... | @@ -2158,6 +2352,8 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef { |
| 2158 | 2352 | .{@tagName(wr)}, |
| 2159 | 2353 | ), |
| 2160 | 2354 | |
| 2355 | .typeOf => |v| .{ .typeOf = v }, |
| 2356 | .comptimeExpr => |v| .{ .comptimeExpr = v }, |
| 2161 | 2357 | .declPath => |v| .{ .declPath = v }, |
| 2162 | 2358 | .type => |v| .{ .type = v }, |
| 2163 | 2359 | .call => |v| .{ .call = v }, |
| ... | ... | @@ -2187,3 +2383,8 @@ fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref { |
| 2187 | 2383 | const break_index = zir.extra[extra.end..][extra.data.body_len - 1]; |
| 2188 | 2384 | return data[break_index].@"break".operand; |
| 2189 | 2385 | } |
| 2386 | |
| 2387 | fn panicWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: anytype) noreturn { |
| 2388 | std.debug.print("Context [{s}] % {}\n", .{ file.sub_file_path, inst }); |
| 2389 | std.debug.panic(fmt, args); |
| 2390 | } |