| ... | ... | @@ -9,6 +9,7 @@ const Ref = Zir.Inst.Ref; |
| 9 | 9 | module: *Module, |
| 10 | 10 | doc_location: Compilation.EmitLoc, |
| 11 | 11 | arena: std.mem.Allocator, |
| 12 | files: std.AutoHashMapUnmanaged(*File, DocData.AutodocFile) = .{}, |
| 12 | 13 | types: std.ArrayListUnmanaged(DocData.Type) = .{}, |
| 13 | 14 | decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, |
| 14 | 15 | ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, |
| ... | ... | @@ -126,12 +127,20 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 126 | 127 | } |
| 127 | 128 | } |
| 128 | 129 | |
| 129 | | var root_scope: Scope = .{ .parent = null }; |
| 130 | var root_scope = Scope{ .parent = null }; |
| 130 | 131 | try self.ast_nodes.append(self.arena, .{ .name = "(root)" }); |
| 132 | try self.files.put(self.arena, file, .{ |
| 133 | .analyzed = false, |
| 134 | .root_struct = self.types.items.len, |
| 135 | }); |
| 131 | 136 | const main_type_index = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst); |
| 137 | self.files.getPtr(file).?.analyzed = true; |
| 138 | |
| 139 | // TODO: solve every single pending declpath whose analysis |
| 140 | // was delayed because of circular imports. |
| 132 | 141 | |
| 133 | 142 | var data = DocData{ |
| 134 | | .files = &[1][]const u8{root_file_path}, |
| 143 | .files = .{ .data = self.files }, |
| 135 | 144 | .types = self.types.items, |
| 136 | 145 | .decls = self.decls.items, |
| 137 | 146 | .astNodes = self.ast_nodes.items, |
| ... | ... | @@ -221,11 +230,42 @@ const DocData = struct { |
| 221 | 230 | |
| 222 | 231 | // non-hardcoded stuff |
| 223 | 232 | astNodes: []AstNode, |
| 224 | | files: []const []const u8, |
| 233 | files: struct { |
| 234 | // this struct is a temporary hack to support json serialization |
| 235 | data: std.AutoHashMapUnmanaged(*File, AutodocFile), |
| 236 | pub fn jsonStringify( |
| 237 | self: @This(), |
| 238 | opt: std.json.StringifyOptions, |
| 239 | w: anytype, |
| 240 | ) !void { |
| 241 | var idx: usize = 0; |
| 242 | var it = self.data.iterator(); |
| 243 | try w.writeAll("{\n"); |
| 244 | |
| 245 | var options = opt; |
| 246 | if (options.whitespace) |*ws| ws.indent_level += 1; |
| 247 | while (it.next()) |kv| : (idx += 1) { |
| 248 | if (options.whitespace) |ws| try ws.outputIndent(w); |
| 249 | try w.print("\"{s}\": {d}", .{ |
| 250 | kv.key_ptr.*.sub_file_path, |
| 251 | kv.value_ptr.root_struct, |
| 252 | }); |
| 253 | if (idx != self.data.count() - 1) try w.writeByte(','); |
| 254 | try w.writeByte('\n'); |
| 255 | } |
| 256 | if (opt.whitespace) |ws| try ws.outputIndent(w); |
| 257 | try w.writeAll("}"); |
| 258 | } |
| 259 | }, |
| 225 | 260 | types: []Type, |
| 226 | 261 | decls: []Decl, |
| 227 | 262 | comptimeExprs: []ComptimeExpr, |
| 228 | 263 | |
| 264 | const AutodocFile = struct { |
| 265 | analyzed: bool, // omitted in json data |
| 266 | root_struct: usize, // index into `types` |
| 267 | }; |
| 268 | |
| 229 | 269 | const DocTypeKinds = blk: { |
| 230 | 270 | var info = @typeInfo(std.builtin.TypeId); |
| 231 | 271 | info.Enum.fields = info.Enum.fields ++ [1]std.builtin.TypeInfo.EnumField{ |
| ... | ... | @@ -289,7 +329,7 @@ const DocData = struct { |
| 289 | 329 | name: []const u8, |
| 290 | 330 | src: ?usize = null, // index into astNodes |
| 291 | 331 | privDecls: ?[]usize = null, // index into decls |
| 292 | | pubDecls: ?[]usize = null, // index into decls |
| 332 | pubDecls: []usize, // index into decls |
| 293 | 333 | fields: ?[]TypeRef = null, // (use src->fields to find names) |
| 294 | 334 | }, |
| 295 | 335 | ComptimeExpr: struct { name: []const u8 }, |
| ... | ... | @@ -544,9 +584,27 @@ fn walkInstruction( |
| 544 | 584 | // importFile cannot error out since all files |
| 545 | 585 | // are already loaded at this point |
| 546 | 586 | const new_file = self.module.importFile(file, path) catch unreachable; |
| 547 | | // TODO: cycles not handled, add file info to outuput |
| 587 | |
| 588 | const result = try self.files.getOrPut(self.arena, new_file.file); |
| 589 | if (result.found_existing) { |
| 590 | return DocData.WalkResult{ .type = result.value_ptr.root_struct }; |
| 591 | } |
| 592 | |
| 593 | result.value_ptr.* = .{ |
| 594 | .analyzed = false, |
| 595 | .root_struct = self.types.items.len, |
| 596 | }; |
| 597 | |
| 548 | 598 | var new_scope = Scope{ .parent = null }; |
| 549 | | return self.walkInstruction(new_file.file, &new_scope, Zir.main_struct_inst); |
| 599 | const new_file_walk_result = self.walkInstruction( |
| 600 | new_file.file, |
| 601 | &new_scope, |
| 602 | Zir.main_struct_inst, |
| 603 | ); |
| 604 | // We re-access the hashmap in case it was modified |
| 605 | // by walkInstruction() |
| 606 | self.files.getPtr(new_file.file).?.analyzed = true; |
| 607 | return new_file_walk_result; |
| 550 | 608 | }, |
| 551 | 609 | .block => { |
| 552 | 610 | const res = DocData.WalkResult{ .comptimeExpr = self.comptimeExprs.items.len }; |
| ... | ... | @@ -641,18 +699,69 @@ fn walkInstruction( |
| 641 | 699 | path[0] = decls_slot_index; |
| 642 | 700 | return DocData.WalkResult{ .declPath = path }; |
| 643 | 701 | }, |
| 644 | | //.field_val => { |
| 645 | | // const pl_node = data[inst_index].pl_node; |
| 646 | | // const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); |
| 647 | | // // In case that we have a path (eg Foo.Bar.Baz.X.Y.Z), |
| 648 | | // // then we want to deal with them in a while loop instead |
| 649 | | // // of using `walkRef()`. |
| 650 | | // |
| 651 | | // var path = try self.arena.alloc(usize, 1); |
| 652 | | // path[0] = decls_slot_index; |
| 653 | | // return DocData.WalkResult{ .declPath = path }; |
| 702 | .field_val => { |
| 703 | const pl_node = data[inst_index].pl_node; |
| 704 | const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); |
| 705 | |
| 706 | var path: std.ArrayListUnmanaged(usize) = .{}; |
| 707 | var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs |
| 708 | |
| 709 | try path.append(self.arena, extra.data.field_name_start); |
| 710 | // Put inside path the starting index of each decl name |
| 711 | // that we encounter as we navigate through all the field_vals |
| 712 | while (tags[lhs] == .field_val) { |
| 713 | const lhs_extra = file.zir.extraData( |
| 714 | Zir.Inst.Field, |
| 715 | data[lhs].pl_node.payload_index, |
| 716 | ); |
| 654 | 717 | |
| 655 | | //}, |
| 718 | try path.append(self.arena, lhs_extra.data.field_name_start); |
| 719 | lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs |
| 720 | } |
| 721 | |
| 722 | if (tags[lhs] != .decl_val) { |
| 723 | @panic("TODO: handle non-decl_val endings in walkInstruction.field_val"); |
| 724 | } |
| 725 | const str_tok = data[lhs].str_tok; |
| 726 | const decls_slot_index = parent_scope.resolveDeclName(str_tok.start); |
| 727 | try path.append(self.arena, decls_slot_index); |
| 728 | |
| 729 | // Righ now, every element of `path` is the first index of a |
| 730 | // decl name except for the final element, which instead points to |
| 731 | // the analyzed data corresponding to the top-most decl of this path. |
| 732 | // We are now going to reverse loop over `path` to resolve each name |
| 733 | // to its corresponding index in `decls`. |
| 734 | |
| 735 | var i: usize = path.items.len; |
| 736 | while (i > 1) { |
| 737 | i -= 1; |
| 738 | const parent = self.decls.items[path.items[i]]; |
| 739 | const child_decl_name = file.zir.nullTerminatedString(path.items[i - 1]); |
| 740 | switch (parent.value) { |
| 741 | else => { |
| 742 | std.debug.print( |
| 743 | "TODO: handle `{s}`in walkInstruction.field_val\n", |
| 744 | .{@tagName(parent.value)}, |
| 745 | ); |
| 746 | unreachable; |
| 747 | }, |
| 748 | .type => |t_index| { |
| 749 | const t_struct = self.types.items[t_index].Struct; // todo: support more types |
| 750 | for (t_struct.pubDecls) |d| { |
| 751 | // TODO: this could be improved a lot |
| 752 | // by having our own string table! |
| 753 | const decl = self.decls.items[d]; |
| 754 | if (std.mem.eql(u8, decl.name, child_decl_name)) { |
| 755 | path.items[i - 1] = d; |
| 756 | continue; |
| 757 | } |
| 758 | } |
| 759 | }, |
| 760 | } |
| 761 | } |
| 762 | |
| 763 | return DocData.WalkResult{ .declPath = path.items }; |
| 764 | }, |
| 656 | 765 | .int_type => { |
| 657 | 766 | const int_type = data[inst_index].int_type; |
| 658 | 767 | const sign = if (int_type.signedness == .unsigned) "u" else "i"; |