Commit eced8c065d

Loris Cro <kappaloris@gmail.com>
2022-03-04 20:43:43
autodoc: add support for solving decl paths depending on other decl paths
1 parent 8bb529b
Changed files (1)
src/Autodoc.zig
@@ -15,6 +15,10 @@ types: std.ArrayListUnmanaged(DocData.Type) = .{},
 decls: std.ArrayListUnmanaged(DocData.Decl) = .{},
 ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{},
 comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{},
+pending_decl_paths: std.AutoHashMapUnmanaged(
+    *usize, // pointer to declpath head (ie `&decl_path[0]`)
+    std.ArrayListUnmanaged(DeclPathResumeInfo),
+) = .{},
 decl_paths_pending_on_decls: std.AutoHashMapUnmanaged(
     usize,
     std.ArrayListUnmanaged(DeclPathResumeInfo),
@@ -154,6 +158,10 @@ pub fn generateZirData(self: *Autodoc) !void {
         @panic("some decl paths were never fully analized (pending on types)");
     }
 
+    if (self.pending_decl_paths.count() > 0) {
+        @panic("some decl paths were never fully analized");
+    }
+
     var data = DocData{
         .files = .{ .data = self.files },
         .calls = self.calls.items,
@@ -733,15 +741,49 @@ fn walkInstruction(
                 lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
             }
 
-            if (tags[lhs] != .decl_val and tags[lhs] != .decl_ref) {
-                std.debug.panic(
-                    "TODO: handle `{s}` endings in walkInstruction.field_val",
-                    .{@tagName(tags[lhs])},
-                );
+            switch (tags[lhs]) {
+                else => {
+                    std.debug.panic(
+                        "TODO: handle `{s}` endings in walkInstruction.field_val",
+                        .{@tagName(tags[lhs])},
+                    );
+                },
+                .import => {
+                    const walk_result = try self.walkInstruction(file, parent_scope, lhs);
+
+                    // astnode
+                    const ast_node_index = idx: {
+                        const idx = self.ast_nodes.items.len;
+                        try self.ast_nodes.append(self.arena, .{
+                            .file = 0,
+                            .line = 0,
+                            .col = 0,
+                            .docs = "",
+                            .fields = null, // walkInstruction will fill `fields` if necessary
+                        });
+                        break :idx idx;
+                    };
+                    const str_tok = data[inst_index].str_tok;
+                    const file_path = str_tok.get(file.zir);
+
+                    const name = try std.fmt.allocPrint(self.arena, "@import({s})", .{file_path});
+                    const decls_slot_index = self.decls.items.len;
+                    try self.decls.append(self.arena, .{
+                        ._analyzed = true,
+                        .name = name,
+                        .src = ast_node_index,
+                        // .typeRef = decl_type_ref,
+                        .value = walk_result,
+                        .kind = "const", // find where this information can be found
+                    });
+                    try path.append(self.arena, decls_slot_index);
+                },
+                .decl_val, .decl_ref => {
+                    const str_tok = data[lhs].str_tok;
+                    const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
+                    try path.append(self.arena, decls_slot_index);
+                },
             }
-            const str_tok = data[lhs].str_tok;
-            const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
-            try path.append(self.arena, decls_slot_index);
 
             // Righ now, every element of `path` is the first index of a
             // decl name except for the final element, which instead points to
@@ -1458,7 +1500,7 @@ fn tryResolveDeclPath(
     /// File from which the decl path originates.
     file: *File,
     path: []usize,
-) !void {
+) error{OutOfMemory}!void {
     var i: usize = path.len;
     while (i > 1) {
         i -= 1;
@@ -1467,12 +1509,19 @@ fn tryResolveDeclPath(
 
         const parent = self.decls.items[decl_index];
         if (!parent._analyzed) {
+            // This decl path is pending completion
+            {
+                const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]);
+                if (!res.found_existing) res.value_ptr.* = .{};
+            }
+
             const res = try self.decl_paths_pending_on_decls.getOrPut(self.arena, decl_index);
             if (!res.found_existing) res.value_ptr.* = .{};
             try res.value_ptr.*.append(self.arena, .{
                 .file = file,
                 .path = path[0 .. i + 1],
             });
+
             return;
         }
 
@@ -1480,10 +1529,37 @@ fn tryResolveDeclPath(
         switch (parent.value) {
             else => {
                 std.debug.panic(
-                    "TODO: handle `{s}`in walkInstruction.field_val\n",
-                    .{@tagName(parent.value)},
+                    "TODO: handle `{s}`in walkInstruction.field_val\n \"{s}\":{}",
+                    .{ @tagName(parent.value), parent.name, parent.value },
                 );
             },
+            .declPath => |dp| {
+                if (self.pending_decl_paths.getPtr(&dp[0])) |waiter_list| {
+                    try waiter_list.append(self.arena, .{
+                        .file = file,
+                        .path = path[0 .. i + 1],
+                    });
+
+                    // This decl path is pending completion
+                    {
+                        const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]);
+                        if (!res.found_existing) res.value_ptr.* = .{};
+                    }
+
+                    return;
+                }
+
+                const final_decl_index = dp[0];
+                // For the purpose of being able to call tryResolveDeclPath again,
+                // we momentarily replace the decl index present in `path[i]`
+                // with the final decl in `dp`.
+                // We then write the original value back as soon as we're done with the
+                // recoursive call. This will work out correctly even if the path
+                // will not get fully resolved.
+                path[i] = final_decl_index;
+                try self.tryResolveDeclPath(file, path);
+                path[i] = decl_index;
+            },
             .type => |t_index| switch (self.types.items[t_index]) {
                 else => {
                     std.debug.panic(
@@ -1492,6 +1568,12 @@ fn tryResolveDeclPath(
                     );
                 },
                 .Unanalyzed => {
+                    // This decl path is pending completion
+                    {
+                        const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]);
+                        if (!res.found_existing) res.value_ptr.* = .{};
+                    }
+
                     const res = try self.decl_paths_pending_on_types.getOrPut(
                         self.arena,
                         t_index,
@@ -1501,6 +1583,7 @@ fn tryResolveDeclPath(
                         .file = file,
                         .path = path[0 .. i + 1],
                     });
+
                     return;
                 },
                 .Struct => |t_struct| {
@@ -1526,6 +1609,18 @@ fn tryResolveDeclPath(
             },
         }
     }
+
+    if (self.pending_decl_paths.get(&path[0])) |waiter_list| {
+        // It's important to de-register oureslves as pending before
+        // attempting to resolve any other decl.
+        _ = self.pending_decl_paths.remove(&path[0]);
+
+        for (waiter_list.items) |resume_info| {
+            try self.tryResolveDeclPath(resume_info.file, resume_info.path);
+        }
+        // TODO: this is where we should free waiter_list, but its in the arena
+        //       that said, we might want to store it elsewhere and reclaim memory asap
+    }
 }
 
 fn collectUnionFieldInfo(