authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2022-03-09 19:43:20+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-19 19:10:11-07:00
logd745dde54f00ee882010e681d364283f0c6f045a
tree050587e3737e03203cf6aaca6c52ab25e59beec2
parent4f949163a509e32f0fc658c2c72d918c92c14f0f

autodoc: improve comments


1 files changed, 87 insertions(+), 17 deletions(-)

src/Autodoc.zig+87-17
...@@ -9,12 +9,19 @@ const Ref = Zir.Inst.Ref;...@@ -9,12 +9,19 @@ const Ref = Zir.Inst.Ref;
9module: *Module,9module: *Module,
10doc_location: Compilation.EmitLoc,10doc_location: Compilation.EmitLoc,
11arena: std.mem.Allocator,11arena: std.mem.Allocator,
12
13// The goal of autodoc is to fill up these arrays
14// that will then be serialized as JSON and consumed
15// by the JS frontend.
12files: std.AutoHashMapUnmanaged(*File, usize) = .{},16files: std.AutoHashMapUnmanaged(*File, usize) = .{},
13calls: std.ArrayListUnmanaged(DocData.Call) = .{},17calls: std.ArrayListUnmanaged(DocData.Call) = .{},
14types: std.ArrayListUnmanaged(DocData.Type) = .{},18types: std.ArrayListUnmanaged(DocData.Type) = .{},
15decls: std.ArrayListUnmanaged(DocData.Decl) = .{},19decls: std.ArrayListUnmanaged(DocData.Decl) = .{},
16ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{},20ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{},
17comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{},21comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{},
22
23// These fields hold temporary state of the analysis process
24// and are mainly used by the decl path resolving algorithm.
18pending_decl_paths: std.AutoHashMapUnmanaged(25pending_decl_paths: std.AutoHashMapUnmanaged(
19 *usize, // pointer to declpath head (ie `&decl_path[0]`)26 *usize, // pointer to declpath head (ie `&decl_path[0]`)
20 std.ArrayListUnmanaged(DeclPathResumeInfo),27 std.ArrayListUnmanaged(DeclPathResumeInfo),
...@@ -47,6 +54,7 @@ pub fn deinit(_: *Autodoc) void {...@@ -47,6 +54,7 @@ pub fn deinit(_: *Autodoc) void {
47 arena_allocator.deinit();54 arena_allocator.deinit();
48}55}
4956
57/// The entry point of the Autodoc generation process.
50pub fn generateZirData(self: *Autodoc) !void {58pub fn generateZirData(self: *Autodoc) !void {
51 if (self.doc_location.directory) |dir| {59 if (self.doc_location.directory) |dir| {
52 if (dir.path) |path| {60 if (dir.path) |path| {
...@@ -66,7 +74,7 @@ pub fn generateZirData(self: *Autodoc) !void {...@@ -66,7 +74,7 @@ pub fn generateZirData(self: *Autodoc) !void {
66 defer self.arena.free(abs_root_path);74 defer self.arena.free(abs_root_path);
67 const file = self.module.import_table.get(abs_root_path).?;75 const file = self.module.import_table.get(abs_root_path).?;
6876
69 // append all the types in Zir.Inst.Ref77 // Append all the types in Zir.Inst.Ref.
70 {78 {
71 try self.types.append(self.arena, .{79 try self.types.append(self.arena, .{
72 .ComptimeExpr = .{ .name = "ComptimeExpr" },80 .ComptimeExpr = .{ .name = "ComptimeExpr" },
...@@ -80,9 +88,8 @@ pub fn generateZirData(self: *Autodoc) !void {...@@ -80,9 +88,8 @@ pub fn generateZirData(self: *Autodoc) !void {
80 self.arena,88 self.arena,
81 switch (@intToEnum(Ref, i)) {89 switch (@intToEnum(Ref, i)) {
82 else => blk: {90 else => blk: {
83 //std.debug.print("TODO: categorize `{s}` in typeKinds\n", .{91 // TODO: map the remaining refs to a correct type
84 // @tagName(t),92 // instead of just assinging "array" to them.
85 //});
86 break :blk .{93 break :blk .{
87 .Array = .{94 .Array = .{
88 .len = 1,95 .len = 1,
...@@ -213,6 +220,8 @@ pub fn generateZirData(self: *Autodoc) !void {...@@ -213,6 +220,8 @@ pub fn generateZirData(self: *Autodoc) !void {
213 special_dir.copyFile("index.html", output_dir, "index.html", .{}) catch unreachable;220 special_dir.copyFile("index.html", output_dir, "index.html", .{}) catch unreachable;
214}221}
215222
223/// Represents a chain of scopes, used to resolve decl references to the
224/// corresponding entry in `self.decls`.
216const Scope = struct {225const Scope = struct {
217 parent: ?*Scope,226 parent: ?*Scope,
218 map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls`227 map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls`
...@@ -237,6 +246,7 @@ const Scope = struct {...@@ -237,6 +246,7 @@ const Scope = struct {
237 }246 }
238};247};
239248
249/// The output of our analysis process.
240const DocData = struct {250const DocData = struct {
241 typeKinds: []const []const u8 = std.meta.fieldNames(DocTypeKinds),251 typeKinds: []const []const u8 = std.meta.fieldNames(DocTypeKinds),
242 rootPkg: u32 = 0,252 rootPkg: u32 = 0,
...@@ -290,6 +300,17 @@ const DocData = struct {...@@ -290,6 +300,17 @@ const DocData = struct {
290 args: []WalkResult,300 args: []WalkResult,
291 ret: WalkResult,301 ret: WalkResult,
292 };302 };
303
304 /// All the type "families" as described by `std.builtin.TypeId`
305 /// plus a couple extra that are unique to our use case.
306 ///
307 /// `Unanalyzed` is used so that we can refer to types that have started
308 /// analysis but that haven't been fully analyzed yet (in case we find
309 /// self-referential stuff, like `@This()`).
310 ///
311 /// `ComptimeExpr` represents the result of a piece of comptime logic
312 /// that we weren't able to analyze fully. Examples of that are comptime
313 /// function calls and comptime if / switch / ... expressions.
293 const DocTypeKinds = blk: {314 const DocTypeKinds = blk: {
294 var info = @typeInfo(std.builtin.TypeId);315 var info = @typeInfo(std.builtin.TypeId);
295 const original_len = info.Enum.fields.len;316 const original_len = info.Enum.fields.len;
...@@ -474,12 +495,25 @@ const DocData = struct {...@@ -474,12 +495,25 @@ const DocData = struct {
474 }495 }
475 };496 };
476497
498 /// A DeclPath represents an expression such as `foo.bar.baz` where each
499 /// component has been resolved to a corresponding index in `self.decls`.
500 /// If a DeclPath has a component that can't be fully solved (eg the
501 /// function call in `foo.bar().baz`), then it will be solved up until the
502 /// unresolved component, leaving the remaining part unresolved.
503 ///
504 /// Note that DeclPaths are currently stored in inverse order: the innermost
505 /// component is at index 0.
477 const DeclPath = struct {506 const DeclPath = struct {
478 path: []usize, // indexes in `decls`507 path: []usize, // indexes in `decls`
479 hasCte: bool = false, // a prefix of this path could not be resolved508 hasCte: bool = false, // a prefix of this path could not be resolved
480 // TODO: make hasCte return the actual index where the cte is!509 // TODO: make hasCte return the actual index where the cte is!
481 };510 };
482511
512 /// A TypeRef is a subset of WalkResult that refers a type in a direct or
513 /// indirect manner.
514 ///
515 /// An example of directness is `const foo = struct {...};`.
516 /// An example of indidirectness is `const bar = foo;`.
483 const TypeRef = union(enum) {517 const TypeRef = union(enum) {
484 unspecified,518 unspecified,
485 declPath: DeclPath,519 declPath: DeclPath,
...@@ -488,7 +522,7 @@ const DocData = struct {...@@ -488,7 +522,7 @@ const DocData = struct {
488 // TODO: maybe we should not consider calls to be typerefs and instread522 // TODO: maybe we should not consider calls to be typerefs and instread
489 // directly refer to their return value. The problem at the moment523 // directly refer to their return value. The problem at the moment
490 // is that we can't analyze function calls at all.524 // is that we can't analyze function calls at all.
491 call: usize, // index in `call`525 call: usize, // index in `calls`
492526
493 pub fn jsonStringify(527 pub fn jsonStringify(
494 self: TypeRef,528 self: TypeRef,
...@@ -518,6 +552,12 @@ const DocData = struct {...@@ -518,6 +552,12 @@ const DocData = struct {
518 }552 }
519 };553 };
520554
555 /// A WalkResult represents the result of the analysis process done to a
556 /// declaration. This includes: decls, fields, etc.
557 ///
558 /// The data in WalkResult is mostly normalized, which means that a
559 /// WalkResult that results in a type definition will hold an index into
560 /// `self.types`.
521 const WalkResult = union(enum) {561 const WalkResult = union(enum) {
522 comptimeExpr: usize, // index in `comptimeExprs`562 comptimeExpr: usize, // index in `comptimeExprs`
523 void,563 void,
...@@ -637,6 +677,14 @@ const DocData = struct {...@@ -637,6 +677,14 @@ const DocData = struct {
637 };677 };
638};678};
639679
680/// Called when we need to analyze a Zir instruction.
681/// For example it gets called by `generateZirData` on instruction 0,
682/// which represents the top-level struct corresponding to the root file.
683/// Note that in some situations where we're analyzing code that only allows
684/// for a limited subset of Zig syntax, we don't always resort to calling
685/// `walkInstruction` and instead sometimes we handle Zir directly.
686/// The best example of that are instructions corresponding to function
687/// params, as those can only occur while analyzing a function definition.
640fn walkInstruction(688fn walkInstruction(
641 self: *Autodoc,689 self: *Autodoc,
642 file: *File,690 file: *File,
...@@ -1399,13 +1447,13 @@ fn walkInstruction(...@@ -1399,13 +1447,13 @@ fn walkInstruction(
1399 }1447 }
1400}1448}
14011449
1402/// Called by `walkInstruction` when encountering a container type,1450/// Called by `walkInstruction` when encountering a container type.
1403/// iterates over all decl definitions in its body.1451/// Iterates over all decl definitions in its body and it also analyzes each
1404/// It also analyzes each decl's body recursively.1452/// decl's body recursively by calling into `walkInstruction`.
1405///1453///
1406/// Does not append to `self.decls` directly because `walkInstruction`1454/// Does not append to `self.decls` directly because `walkInstruction`
1407/// is expected to (look-ahead) scan all decls and reserve `body_len`1455/// is expected to look-ahead scan all decls and reserve `body_len`
1408/// slots in `self.decls`, which are then filled out by `walkDecls`.1456/// slots in `self.decls`, which are then filled out by this function.
1409fn walkDecls(1457fn walkDecls(
1410 self: *Autodoc,1458 self: *Autodoc,
1411 file: *File,1459 file: *File,
...@@ -1634,10 +1682,27 @@ fn walkDecls(...@@ -1634,10 +1682,27 @@ fn walkDecls(
1634}1682}
16351683
1636/// An unresolved path has a decl index at its end, while every other element1684/// An unresolved path has a decl index at its end, while every other element
1637/// is an index into the string table. Resolving means resolving iteratively1685/// is an index into the string table. Resolving means iteratively map each
1638/// each string into a decl_index. If we encounter an unanalyzed decl during1686/// string to a decl_index.
1639/// the process, we append the unsolved sub-path to `self.decl_paths_pending_on_decls`1687///
1640/// and bail out.1688/// If we encounter an unanalyzed decl during the process, we append the
1689/// unsolved sub-path to `self.decl_paths_pending_on_decls` and bail out.
1690/// Same happens when a decl holds a type definition that hasn't been fully
1691/// analyzed yet (except that we append to `self.decl_paths_pending_on_types`.
1692///
1693/// When a decl or a type is fully analyzed if will then check if there's any
1694/// pending decl path blocked on it and, if any, will progress their resolution
1695/// by calling tryResolveDeclPath again.
1696///
1697/// Decl paths can also depend on other decl paths. See
1698/// `self.pending_decl_paths` for more info.
1699///
1700/// A decl path that has a component that resolves into a comptimeExpr will
1701/// give up its resolution process entirely.
1702///
1703/// TODO: when giving up, translate remaining string indexes into data that
1704/// can be used by the frontend. Requires implementing a frontend string
1705/// table.
1641fn tryResolveDeclPath(1706fn tryResolveDeclPath(
1642 self: *Autodoc,1707 self: *Autodoc,
1643 /// File from which the decl path originates.1708 /// File from which the decl path originates.
...@@ -1920,6 +1985,8 @@ fn collectStructFieldInfo(...@@ -1920,6 +1985,8 @@ fn collectStructFieldInfo(
1920 }1985 }
1921}1986}
19221987
1988/// A Zir Ref can either refer to common types and values, or to a Zir index.
1989/// WalkRef resolves common cases and delegates to `walkInstruction` otherwise.
1923fn walkRef(1990fn walkRef(
1924 self: *Autodoc,1991 self: *Autodoc,
1925 file: *File,1992 file: *File,
...@@ -2014,6 +2081,9 @@ fn walkRef(...@@ -2014,6 +2081,9 @@ fn walkRef(
2014 }2081 }
2015}2082}
20162083
2084/// Maps some `DocData.WalkResult` cases to `DocData.TypeRef`.
2085/// Correct code should never cause this function to fail but
2086/// incorrect code might (eg: `const foo: 5 = undefined;`)
2017fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {2087fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
2018 return switch (wr) {2088 return switch (wr) {
2019 else => std.debug.panic(2089 else => std.debug.panic(
...@@ -2027,6 +2097,9 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {...@@ -2027,6 +2097,9 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
2027 };2097 };
2028}2098}
20292099
2100/// Given a WalkResult, tries to find its type.
2101/// Used to analyze instructions like `array_init`, which require us to
2102/// inspect its first element to find out the array type.
2030fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {2103fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {
2031 return switch (wr) {2104 return switch (wr) {
2032 else => std.debug.panic(2105 else => std.debug.panic(
...@@ -2040,9 +2113,6 @@ fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {...@@ -2040,9 +2113,6 @@ fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {
2040 };2113 };
2041}2114}
20422115
2043//fn collectParamInfo(self: *Autodoc, file: *File, scope: *Scope, inst_idx: Zir.Index) void {
2044
2045//}
2046fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref {2116fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref {
2047 const data = zir.instructions.items(.data);2117 const data = zir.instructions.items(.data);
2048 const pl_node = data[inst_index].pl_node;2118 const pl_node = data[inst_index].pl_node;