authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2022-03-13 06:12:17+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-19 19:10:11-07:00
log67f1d2b967a13c794c57cb8b6783f2e52b029888
treeeb64c99d532ac224b4ffa84709aef66bef272fe7
parentec7f4d1faab8aa9b797f96d233d2ca12c99d2179

autodoc: add basic support for more builtin


1 files changed, 223 insertions(+), 22 deletions(-)

src/Autodoc.zig+223-22
......@@ -532,13 +532,15 @@ const DocData = struct {
532532 // directly refer to their return value. The problem at the moment
533533 // is that we can't analyze function calls at all.
534534 call: usize, // index in `calls`
535 typeOf: *WalkResult,
535536
536537 pub fn jsonStringify(
537538 self: TypeRef,
538 _: std.json.StringifyOptions,
539 options: std.json.StringifyOptions,
539540 w: anytype,
540541 ) !void {
541542 switch (self) {
543 .typeOf => |v| try std.json.stringify(v, options, w),
542544 .unspecified, .@"anytype" => {
543545 try w.print(
544546 \\{{ "{s}":{{}} }}
......@@ -591,13 +593,19 @@ const DocData = struct {
591593 array: Array,
592594 call: usize, // index in `calls`
593595 enumLiteral: []const u8,
596 typeOf: *WalkResult,
597 sizeOf: *WalkResult,
598 compileError: []const u8,
599 string: []const u8,
594600
595601 const Struct = struct {
596602 typeRef: TypeRef,
597 fieldVals: []struct {
603 fieldVals: []FieldVal,
604
605 const FieldVal = struct {
598606 name: []const u8,
599607 val: WalkResult,
600 },
608 };
601609 };
602610 const Array = struct {
603611 typeRef: TypeRef,
......@@ -647,6 +655,9 @@ const DocData = struct {
647655 },
648656 .@"undefined" => |v| try std.json.stringify(v, options, w),
649657 .@"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),
650661 .@"struct" => |v| try std.json.stringify(
651662 struct { @"struct": Struct }{ .@"struct" = v },
652663 options,
......@@ -709,11 +720,21 @@ fn walkInstruction(
709720
710721 switch (tags[inst_index]) {
711722 else => {
712 std.debug.panic(
723 panicWithContext(
724 file,
725 inst_index,
713726 "TODO: implement `{s}` for walkInstruction\n\n",
714727 .{@tagName(tags[inst_index])},
715728 );
716729 },
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 },
717738 .import => {
718739 const str_tok = data[inst_index].str_tok;
719740 const path = str_tok.get(file.zir);
......@@ -749,11 +770,46 @@ fn walkInstruction(
749770
750771 return new_file_walk_result;
751772 },
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 },
752800 .enum_literal => {
753801 const str_tok = data[inst_index].str_tok;
754802 const literal = file.zir.nullTerminatedString(str_tok.start);
755803 return DocData.WalkResult{ .enumLiteral = literal };
756804 },
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 },
757813 .int => {
758814 const int = data[inst_index].int;
759815 return DocData.WalkResult{
......@@ -785,6 +841,25 @@ fn walkInstruction(
785841
786842 return DocData.WalkResult{ .type = type_slot_index };
787843 },
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 },
788863 .array_type => {
789864 const bin = data[inst_index].bin;
790865 const len = try self.walkRef(file, parent_scope, bin.lhs);
......@@ -848,6 +923,27 @@ fn walkInstruction(
848923 operand.int.negated = true; // only support ints for now
849924 return operand;
850925 },
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 },
851947 .as_node => {
852948 const pl_node = data[inst_index].pl_node;
853949 const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
......@@ -857,11 +953,13 @@ fn walkInstruction(
857953 var operand = try self.walkRef(file, parent_scope, extra.data.operand);
858954
859955 switch (operand) {
860 else => std.debug.panic(
956 else => panicWithContext(
957 file,
958 inst_index,
861959 "TODO: handle {s} in `walkInstruction.as_node`\n",
862960 .{@tagName(operand)},
863961 ),
864 .declPath, .type => {},
962 .declPath, .type, .string => {},
865963 // we don't do anything because up until now,
866964 // I've only seen this used as such:
867965 // @as(@as(type, Baz), .{})
......@@ -894,14 +992,16 @@ fn walkInstruction(
894992 });
895993 return res;
896994 },
897 .decl_val => {
995 .decl_val, .decl_ref => {
898996 const str_tok = data[inst_index].str_tok;
899997 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
900998 var path = try self.arena.alloc(usize, 1);
901999 path[0] = decls_slot_index;
9021000 return DocData.WalkResult{ .declPath = .{ .path = path } };
9031001 },
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 :^)
9051005 const pl_node = data[inst_index].pl_node;
9061006 const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
9071007
......@@ -909,8 +1009,8 @@ fn walkInstruction(
9091009 var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
9101010
9111011 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
9141014 while (tags[lhs] == .field_val or
9151015 tags[lhs] == .field_call_bind or
9161016 tags[lhs] == .field_ptr)
......@@ -925,11 +1025,35 @@ fn walkInstruction(
9251025 }
9261026
9271027 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);
9331057 },
9341058 .import => {
9351059 const walk_result = try self.walkInstruction(file, parent_scope, lhs);
......@@ -942,7 +1066,7 @@ fn walkInstruction(
9421066 .line = 0,
9431067 .col = 0,
9441068 .docs = "",
945 .fields = null, // walkInstruction will fill `fields` if necessary
1069 .fields = null,
9461070 });
9471071 break :idx idx;
9481072 };
......@@ -999,6 +1123,76 @@ fn walkInstruction(
9991123 .block_inline => {
10001124 return self.walkRef(file, parent_scope, getBlockInlineBreak(file.zir, inst_index));
10011125 },
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 },
10021196 .call => {
10031197 const pl_node = data[inst_index].pl_node;
10041198 const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);
......@@ -1048,12 +1242,12 @@ fn walkInstruction(
10481242 // TODO: handle scope rules for fn parameters
10491243 for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
10501244 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 ),
10571251 .param_anytype => {
10581252 // TODO: where are the doc comments?
10591253 const str_tok = data[param_index].str_tok;
......@@ -2158,6 +2352,8 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
21582352 .{@tagName(wr)},
21592353 ),
21602354
2355 .typeOf => |v| .{ .typeOf = v },
2356 .comptimeExpr => |v| .{ .comptimeExpr = v },
21612357 .declPath => |v| .{ .declPath = v },
21622358 .type => |v| .{ .type = v },
21632359 .call => |v| .{ .call = v },
......@@ -2187,3 +2383,8 @@ fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref {
21872383 const break_index = zir.extra[extra.end..][extra.data.body_len - 1];
21882384 return data[break_index].@"break".operand;
21892385}
2386
2387fn 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}