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;
99module: *Module,
1010doc_location: Compilation.EmitLoc,
1111arena: 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.
1216files: std.AutoHashMapUnmanaged(*File, usize) = .{},
1317calls: std.ArrayListUnmanaged(DocData.Call) = .{},
1418types: std.ArrayListUnmanaged(DocData.Type) = .{},
1519decls: std.ArrayListUnmanaged(DocData.Decl) = .{},
1620ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{},
1721comptime_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.
1825pending_decl_paths: std.AutoHashMapUnmanaged(
1926 *usize, // pointer to declpath head (ie `&decl_path[0]`)
2027 std.ArrayListUnmanaged(DeclPathResumeInfo),
......@@ -47,6 +54,7 @@ pub fn deinit(_: *Autodoc) void {
4754 arena_allocator.deinit();
4855}
4956
57/// The entry point of the Autodoc generation process.
5058pub fn generateZirData(self: *Autodoc) !void {
5159 if (self.doc_location.directory) |dir| {
5260 if (dir.path) |path| {
......@@ -66,7 +74,7 @@ pub fn generateZirData(self: *Autodoc) !void {
6674 defer self.arena.free(abs_root_path);
6775 const file = self.module.import_table.get(abs_root_path).?;
6876
69 // append all the types in Zir.Inst.Ref
77 // Append all the types in Zir.Inst.Ref.
7078 {
7179 try self.types.append(self.arena, .{
7280 .ComptimeExpr = .{ .name = "ComptimeExpr" },
......@@ -80,9 +88,8 @@ pub fn generateZirData(self: *Autodoc) !void {
8088 self.arena,
8189 switch (@intToEnum(Ref, i)) {
8290 else => blk: {
83 //std.debug.print("TODO: categorize `{s}` in typeKinds\n", .{
84 // @tagName(t),
85 //});
91 // TODO: map the remaining refs to a correct type
92 // instead of just assinging "array" to them.
8693 break :blk .{
8794 .Array = .{
8895 .len = 1,
......@@ -213,6 +220,8 @@ pub fn generateZirData(self: *Autodoc) !void {
213220 special_dir.copyFile("index.html", output_dir, "index.html", .{}) catch unreachable;
214221}
215222
223/// Represents a chain of scopes, used to resolve decl references to the
224/// corresponding entry in `self.decls`.
216225const Scope = struct {
217226 parent: ?*Scope,
218227 map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls`
......@@ -237,6 +246,7 @@ const Scope = struct {
237246 }
238247};
239248
249/// The output of our analysis process.
240250const DocData = struct {
241251 typeKinds: []const []const u8 = std.meta.fieldNames(DocTypeKinds),
242252 rootPkg: u32 = 0,
......@@ -290,6 +300,17 @@ const DocData = struct {
290300 args: []WalkResult,
291301 ret: WalkResult,
292302 };
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.
293314 const DocTypeKinds = blk: {
294315 var info = @typeInfo(std.builtin.TypeId);
295316 const original_len = info.Enum.fields.len;
......@@ -474,12 +495,25 @@ const DocData = struct {
474495 }
475496 };
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.
477506 const DeclPath = struct {
478507 path: []usize, // indexes in `decls`
479508 hasCte: bool = false, // a prefix of this path could not be resolved
480509 // TODO: make hasCte return the actual index where the cte is!
481510 };
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;`.
483517 const TypeRef = union(enum) {
484518 unspecified,
485519 declPath: DeclPath,
......@@ -488,7 +522,7 @@ const DocData = struct {
488522 // TODO: maybe we should not consider calls to be typerefs and instread
489523 // directly refer to their return value. The problem at the moment
490524 // is that we can't analyze function calls at all.
491 call: usize, // index in `call`
525 call: usize, // index in `calls`
492526
493527 pub fn jsonStringify(
494528 self: TypeRef,
......@@ -518,6 +552,12 @@ const DocData = struct {
518552 }
519553 };
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`.
521561 const WalkResult = union(enum) {
522562 comptimeExpr: usize, // index in `comptimeExprs`
523563 void,
......@@ -637,6 +677,14 @@ const DocData = struct {
637677 };
638678};
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.
640688fn walkInstruction(
641689 self: *Autodoc,
642690 file: *File,
......@@ -1399,13 +1447,13 @@ fn walkInstruction(
13991447 }
14001448}
14011449
1402/// Called by `walkInstruction` when encountering a container type,
1403/// iterates over all decl definitions in its body.
1404/// It also analyzes each decl's body recursively.
1450/// Called by `walkInstruction` when encountering a container type.
1451/// Iterates over all decl definitions in its body and it also analyzes each
1452/// decl's body recursively by calling into `walkInstruction`.
14051453///
14061454/// Does not append to `self.decls` directly because `walkInstruction`
1407/// is expected to (look-ahead) scan all decls and reserve `body_len`
1408/// slots in `self.decls`, which are then filled out by `walkDecls`.
1455/// is expected to look-ahead scan all decls and reserve `body_len`
1456/// slots in `self.decls`, which are then filled out by this function.
14091457fn walkDecls(
14101458 self: *Autodoc,
14111459 file: *File,
......@@ -1634,10 +1682,27 @@ fn walkDecls(
16341682}
16351683
16361684/// 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 iteratively
1638/// each string into a decl_index. If we encounter an unanalyzed decl during
1639/// the process, we append the unsolved sub-path to `self.decl_paths_pending_on_decls`
1640/// and bail out.
1685/// is an index into the string table. Resolving means iteratively map each
1686/// string to a decl_index.
1687///
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.
16411706fn tryResolveDeclPath(
16421707 self: *Autodoc,
16431708 /// File from which the decl path originates.
......@@ -1920,6 +1985,8 @@ fn collectStructFieldInfo(
19201985 }
19211986}
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.
19231990fn walkRef(
19241991 self: *Autodoc,
19251992 file: *File,
......@@ -2014,6 +2081,9 @@ fn walkRef(
20142081 }
20152082}
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;`)
20172087fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
20182088 return switch (wr) {
20192089 else => std.debug.panic(
......@@ -2027,6 +2097,9 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
20272097 };
20282098}
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.
20302103fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {
20312104 return switch (wr) {
20322105 else => std.debug.panic(
......@@ -2040,9 +2113,6 @@ fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {
20402113 };
20412114}
20422115
2043//fn collectParamInfo(self: *Autodoc, file: *File, scope: *Scope, inst_idx: Zir.Index) void {
2044
2045//}
20462116fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref {
20472117 const data = zir.instructions.items(.data);
20482118 const pl_node = data[inst_index].pl_node;