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 {...@@ -532,13 +532,15 @@ const DocData = struct {
532 // directly refer to their return value. The problem at the moment532 // directly refer to their return value. The problem at the moment
533 // is that we can't analyze function calls at all.533 // is that we can't analyze function calls at all.
534 call: usize, // index in `calls`534 call: usize, // index in `calls`
535 typeOf: *WalkResult,
535536
536 pub fn jsonStringify(537 pub fn jsonStringify(
537 self: TypeRef,538 self: TypeRef,
538 _: std.json.StringifyOptions,539 options: std.json.StringifyOptions,
539 w: anytype,540 w: anytype,
540 ) !void {541 ) !void {
541 switch (self) {542 switch (self) {
543 .typeOf => |v| try std.json.stringify(v, options, w),
542 .unspecified, .@"anytype" => {544 .unspecified, .@"anytype" => {
543 try w.print(545 try w.print(
544 \\{{ "{s}":{{}} }}546 \\{{ "{s}":{{}} }}
...@@ -591,13 +593,19 @@ const DocData = struct {...@@ -591,13 +593,19 @@ const DocData = struct {
591 array: Array,593 array: Array,
592 call: usize, // index in `calls`594 call: usize, // index in `calls`
593 enumLiteral: []const u8,595 enumLiteral: []const u8,
596 typeOf: *WalkResult,
597 sizeOf: *WalkResult,
598 compileError: []const u8,
599 string: []const u8,
594600
595 const Struct = struct {601 const Struct = struct {
596 typeRef: TypeRef,602 typeRef: TypeRef,
597 fieldVals: []struct {603 fieldVals: []FieldVal,
604
605 const FieldVal = struct {
598 name: []const u8,606 name: []const u8,
599 val: WalkResult,607 val: WalkResult,
600 },608 };
601 };609 };
602 const Array = struct {610 const Array = struct {
603 typeRef: TypeRef,611 typeRef: TypeRef,
...@@ -647,6 +655,9 @@ const DocData = struct {...@@ -647,6 +655,9 @@ const DocData = struct {
647 },655 },
648 .@"undefined" => |v| try std.json.stringify(v, options, w),656 .@"undefined" => |v| try std.json.stringify(v, options, w),
649 .@"null" => |v| try std.json.stringify(v, options, w),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 .@"struct" => |v| try std.json.stringify(661 .@"struct" => |v| try std.json.stringify(
651 struct { @"struct": Struct }{ .@"struct" = v },662 struct { @"struct": Struct }{ .@"struct" = v },
652 options,663 options,
...@@ -709,11 +720,21 @@ fn walkInstruction(...@@ -709,11 +720,21 @@ fn walkInstruction(
709720
710 switch (tags[inst_index]) {721 switch (tags[inst_index]) {
711 else => {722 else => {
712 std.debug.panic(723 panicWithContext(
724 file,
725 inst_index,
713 "TODO: implement `{s}` for walkInstruction\n\n",726 "TODO: implement `{s}` for walkInstruction\n\n",
714 .{@tagName(tags[inst_index])},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 .import => {738 .import => {
718 const str_tok = data[inst_index].str_tok;739 const str_tok = data[inst_index].str_tok;
719 const path = str_tok.get(file.zir);740 const path = str_tok.get(file.zir);
...@@ -749,11 +770,46 @@ fn walkInstruction(...@@ -749,11 +770,46 @@ fn walkInstruction(
749770
750 return new_file_walk_result;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 .enum_literal => {800 .enum_literal => {
753 const str_tok = data[inst_index].str_tok;801 const str_tok = data[inst_index].str_tok;
754 const literal = file.zir.nullTerminatedString(str_tok.start);802 const literal = file.zir.nullTerminatedString(str_tok.start);
755 return DocData.WalkResult{ .enumLiteral = literal };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 .int => {813 .int => {
758 const int = data[inst_index].int;814 const int = data[inst_index].int;
759 return DocData.WalkResult{815 return DocData.WalkResult{
...@@ -785,6 +841,25 @@ fn walkInstruction(...@@ -785,6 +841,25 @@ fn walkInstruction(
785841
786 return DocData.WalkResult{ .type = type_slot_index };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 .array_type => {863 .array_type => {
789 const bin = data[inst_index].bin;864 const bin = data[inst_index].bin;
790 const len = try self.walkRef(file, parent_scope, bin.lhs);865 const len = try self.walkRef(file, parent_scope, bin.lhs);
...@@ -848,6 +923,27 @@ fn walkInstruction(...@@ -848,6 +923,27 @@ fn walkInstruction(
848 operand.int.negated = true; // only support ints for now923 operand.int.negated = true; // only support ints for now
849 return operand;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 .as_node => {947 .as_node => {
852 const pl_node = data[inst_index].pl_node;948 const pl_node = data[inst_index].pl_node;
853 const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);949 const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
...@@ -857,11 +953,13 @@ fn walkInstruction(...@@ -857,11 +953,13 @@ fn walkInstruction(
857 var operand = try self.walkRef(file, parent_scope, extra.data.operand);953 var operand = try self.walkRef(file, parent_scope, extra.data.operand);
858954
859 switch (operand) {955 switch (operand) {
860 else => std.debug.panic(956 else => panicWithContext(
957 file,
958 inst_index,
861 "TODO: handle {s} in `walkInstruction.as_node`\n",959 "TODO: handle {s} in `walkInstruction.as_node`\n",
862 .{@tagName(operand)},960 .{@tagName(operand)},
863 ),961 ),
864 .declPath, .type => {},962 .declPath, .type, .string => {},
865 // we don't do anything because up until now,963 // we don't do anything because up until now,
866 // I've only seen this used as such:964 // I've only seen this used as such:
867 // @as(@as(type, Baz), .{})965 // @as(@as(type, Baz), .{})
...@@ -894,14 +992,16 @@ fn walkInstruction(...@@ -894,14 +992,16 @@ fn walkInstruction(
894 });992 });
895 return res;993 return res;
896 },994 },
897 .decl_val => {995 .decl_val, .decl_ref => {
898 const str_tok = data[inst_index].str_tok;996 const str_tok = data[inst_index].str_tok;
899 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);997 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
900 var path = try self.arena.alloc(usize, 1);998 var path = try self.arena.alloc(usize, 1);
901 path[0] = decls_slot_index;999 path[0] = decls_slot_index;
902 return DocData.WalkResult{ .declPath = .{ .path = path } };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 const pl_node = data[inst_index].pl_node;1005 const pl_node = data[inst_index].pl_node;
906 const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);1006 const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
9071007
...@@ -909,8 +1009,8 @@ fn walkInstruction(...@@ -909,8 +1009,8 @@ fn walkInstruction(
909 var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs1009 var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
9101010
911 try path.append(self.arena, extra.data.field_name_start);1011 try path.append(self.arena, extra.data.field_name_start);
912 // Put inside path the starting index of each decl name1012 // Put inside path the starting index of each decl name that
913 // that we encounter as we navigate through all the field_vals1013 // we encounter as we navigate through all the field_vals
914 while (tags[lhs] == .field_val or1014 while (tags[lhs] == .field_val or
915 tags[lhs] == .field_call_bind or1015 tags[lhs] == .field_call_bind or
916 tags[lhs] == .field_ptr)1016 tags[lhs] == .field_ptr)
...@@ -925,11 +1025,35 @@ fn walkInstruction(...@@ -925,11 +1025,35 @@ fn walkInstruction(
925 }1025 }
9261026
927 switch (tags[lhs]) {1027 switch (tags[lhs]) {
928 else => {1028 else => panicWithContext(
929 std.debug.panic(1029 file,
930 "TODO: handle `{s}` endings in walkInstruction.field_val",1030 inst_index,
931 .{@tagName(tags[lhs])},1031 "TODO: handle `{s}` in walkInstruction.field_val",
932 );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 .import => {1058 .import => {
935 const walk_result = try self.walkInstruction(file, parent_scope, lhs);1059 const walk_result = try self.walkInstruction(file, parent_scope, lhs);
...@@ -942,7 +1066,7 @@ fn walkInstruction(...@@ -942,7 +1066,7 @@ fn walkInstruction(
942 .line = 0,1066 .line = 0,
943 .col = 0,1067 .col = 0,
944 .docs = "",1068 .docs = "",
945 .fields = null, // walkInstruction will fill `fields` if necessary1069 .fields = null,
946 });1070 });
947 break :idx idx;1071 break :idx idx;
948 };1072 };
...@@ -999,6 +1123,76 @@ fn walkInstruction(...@@ -999,6 +1123,76 @@ fn walkInstruction(
999 .block_inline => {1123 .block_inline => {
1000 return self.walkRef(file, parent_scope, getBlockInlineBreak(file.zir, inst_index));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 .call => {1196 .call => {
1003 const pl_node = data[inst_index].pl_node;1197 const pl_node = data[inst_index].pl_node;
1004 const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);1198 const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);
...@@ -1048,12 +1242,12 @@ fn walkInstruction(...@@ -1048,12 +1242,12 @@ fn walkInstruction(
1048 // TODO: handle scope rules for fn parameters1242 // TODO: handle scope rules for fn parameters
1049 for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {1243 for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
1050 switch (tags[param_index]) {1244 switch (tags[param_index]) {
1051 else => {1245 else => panicWithContext(
1052 std.debug.panic(1246 file,
1053 "TODO: handle `{s}` in walkInstruction.func\n",1247 param_index,
1054 .{@tagName(tags[param_index])},1248 "TODO: handle `{s}` in walkInstruction.func\n",
1055 );1249 .{@tagName(tags[param_index])},
1056 },1250 ),
1057 .param_anytype => {1251 .param_anytype => {
1058 // TODO: where are the doc comments?1252 // TODO: where are the doc comments?
1059 const str_tok = data[param_index].str_tok;1253 const str_tok = data[param_index].str_tok;
...@@ -2158,6 +2352,8 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {...@@ -2158,6 +2352,8 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
2158 .{@tagName(wr)},2352 .{@tagName(wr)},
2159 ),2353 ),
21602354
2355 .typeOf => |v| .{ .typeOf = v },
2356 .comptimeExpr => |v| .{ .comptimeExpr = v },
2161 .declPath => |v| .{ .declPath = v },2357 .declPath => |v| .{ .declPath = v },
2162 .type => |v| .{ .type = v },2358 .type => |v| .{ .type = v },
2163 .call => |v| .{ .call = v },2359 .call => |v| .{ .call = v },
...@@ -2187,3 +2383,8 @@ fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref {...@@ -2187,3 +2383,8 @@ fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref {
2187 const break_index = zir.extra[extra.end..][extra.data.body_len - 1];2383 const break_index = zir.extra[extra.end..][extra.data.body_len - 1];
2188 return data[break_index].@"break".operand;2384 return data[break_index].@"break".operand;
2189}2385}
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}