Commit 056ba8e57c

Loris Cro <kappaloris@gmail.com>
2022-03-09 18:35:12
autodoc: add support for `@This` and improve call support in decl paths
1 parent 3eb90a1
Changed files (2)
lib
docs
src
lib/docs/main.js
@@ -661,9 +661,9 @@
             if (typeValue.hasCte) {
                 // TODO: find the cte, print it nicely
                 if (wantLink) {
-                    return '<a href=""># CTE TODO #</a>';
+                    return '<a href="">[ComptimeExpr]</a>';
                 } else {
-                    return "# CTE TODO #";
+                    return "[ComptimeExpr]";
                 }
             }
             var declIndex = typeValue.declPath[0];
@@ -735,7 +735,7 @@
     function getValueText(typeRef, value, wantHtml, wantLink) {
         var resolvedTypeRef = resolveValue(typeRef);
         if ("comptimeExpr" in resolvedTypeRef) {
-            return "# CTE TODO #";
+            return "[ComptimeExpr]";
         }
         console.assert("type" in resolvedTypeRef);
         var typeObj = zigAnalysis.types[typeRef.type];
@@ -773,9 +773,10 @@
                 return "?" + typeValueName(typeObj.child, wantHtml, wantSubLink, fnDecl, linkFnNameDecl);
             case typeKinds.Pointer:
                     var name = "";
-                switch (typeObj.len) {
-                    case pointerSizeEnum.One:
+                switch (typeObj.size) {
                     default:
+                        console.log("TODO: implement unhandled pointer size case");
+                    case pointerSizeEnum.One:
                         name += "*";
                         break;
                     case pointerSizeEnum.Many:
@@ -976,23 +977,27 @@
                                 }
                             }
 
-                            if (isVarArgs && i === typeObj.args.length - 1) {
+                            if (isVarArgs && i === typeObj.params.length - 1) {
                                 payloadHtml += '...';
-                            } else if ("declRef" in value) {
-                                var decl = zigAnalysis.decls[value.declRef];
-                                var val = resolveValue(decl.value);
-                                var valType = zigAnalysis.types[argTypeIndex];
-
-                                var valTypeName = typeShorthandName(valType);
-
-                                payloadHtml += '<a href="'+navLinkDecl(decl.name)+'">';
-                                payloadHtml += '<span class="tok-kw" style="color:lightblue;">' + escapeHtml(decl.name) + '</span>';
-                                payloadHtml += '</a>';
+                            } else if ("declPath" in value) {
+                                if (value.hasCte) {
+                                    var cte = findCteInDeclPath(value.declPath);
+                                    payloadHtml += "[ComptimeExpr]";
+                                } else {
+                                    var decl = zigAnalysis.decls[value.declPath[0]];
+                                    var val = resolveValue(decl.value);
+                                    var valType = zigAnalysis.types[argTypeIndex];
+
+                                    var valTypeName = typeShorthandName(valType);
+                                    payloadHtml += '<a href="'+navLinkDecl(decl.name)+'">';
+                                    payloadHtml += '<span class="tok-kw" style="color:lightblue;">' + escapeHtml(decl.name) + '</span>';
+                                    payloadHtml += '</a>';
+                                }
                             } else if ("type" in value) {
                                 var name = typeValueName(value, false);
                                 payloadHtml += '<span class="tok-kw">' + escapeHtml(name) + '</span>';
                             } else if ("comptimeExpr" in value) {
-                                payloadHtml += '<span class="tok-kw"> # CTE TODO #</span>';
+                                payloadHtml += '<span class="tok-kw">[ComptimeExpr]</span>';
                             } else if (wantHtml) {
                                 payloadHtml += '<span class="tok-kw">var</span>';
                             } else {
@@ -1350,7 +1355,7 @@
 
                             // TODO: handle nested decl paths properly!
                             if (field.hasCte) {
-                                html += "<a href=\"\"># CTE TODO #</a>";
+                                html += "<a href=\"\">[ComptimeExpr]</a>";
                                 break;
                             }
 
src/Autodoc.zig
@@ -144,10 +144,11 @@ pub fn generateZirData(self: *Autodoc) !void {
         }
     }
 
-    var root_scope = Scope{ .parent = null };
+    const main_type_index = self.types.items.len;
+    var root_scope = Scope{ .parent = null, .enclosing_type = main_type_index };
     try self.ast_nodes.append(self.arena, .{ .name = "(root)" });
-    try self.files.put(self.arena, file, self.types.items.len);
-    const main_type_index = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst);
+    try self.files.put(self.arena, file, main_type_index);
+    _ = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst);
 
     if (self.decl_paths_pending_on_decls.count() > 0) {
         @panic("some decl paths were never fully analized (pending on decls)");
@@ -170,7 +171,7 @@ pub fn generateZirData(self: *Autodoc) !void {
         .comptimeExprs = self.comptime_exprs.items,
     };
 
-    data.packages[0].main = main_type_index.type;
+    data.packages[0].main = main_type_index;
 
     if (self.doc_location.directory) |d| {
         d.handle.makeDir(
@@ -215,6 +216,7 @@ pub fn generateZirData(self: *Autodoc) !void {
 const Scope = struct {
     parent: ?*Scope,
     map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls`
+    enclosing_type: usize, // index into `types`
 
     /// Assumes all decls in present scope and upper scopes have already
     /// been either fully resolved or at least reserved.
@@ -475,6 +477,7 @@ const DocData = struct {
     const DeclPath = struct {
         path: []usize, // indexes in `decls`
         hasCte: bool = false, // a prefix of this path could not be resolved
+        // TODO: make hasCte return the actual index where the cte is!
     };
 
     const TypeRef = union(enum) {
@@ -482,14 +485,10 @@ const DocData = struct {
         declPath: DeclPath,
         type: usize, // index in `types`
         comptimeExpr: usize, // index in `comptimeExprs`
-
-        pub fn fromWalkResult(wr: WalkResult) TypeRef {
-            return switch (wr) {
-                .declPath => |v| .{ .declPath = v },
-                .type => |v| .{ .type = v },
-                else => @panic("Found non-type WalkResult"),
-            };
-        }
+        // TODO: maybe we should not consider calls to be typerefs and instread
+        // directly refer to their return value. The problem at the moment
+        // is that we can't analyze function calls at all.
+        call: usize, // index in `call`
 
         pub fn jsonStringify(
             self: TypeRef,
@@ -503,7 +502,7 @@ const DocData = struct {
                     , .{});
                 },
 
-                .type, .comptimeExpr => |v| {
+                .type, .comptimeExpr, .call => |v| {
                     try w.print(
                         \\{{ "{s}":{} }}
                     , .{ @tagName(self), v });
@@ -647,7 +646,7 @@ fn walkInstruction(
     switch (tags[inst_index]) {
         else => {
             std.debug.panic(
-                "TODO: implement `walkInstruction` for {s}\n\n",
+                "TODO: implement `{s}` for walkInstruction\n\n",
                 .{@tagName(tags[inst_index])},
             );
         },
@@ -674,7 +673,10 @@ fn walkInstruction(
 
             result.value_ptr.* = self.types.items.len;
 
-            var new_scope = Scope{ .parent = null };
+            var new_scope = Scope{
+                .parent = null,
+                .enclosing_type = self.types.items.len,
+            };
             const new_file_walk_result = self.walkInstruction(
                 new_file.file,
                 &new_scope,
@@ -849,7 +851,7 @@ fn walkInstruction(
                         });
                         break :idx idx;
                     };
-                    const str_tok = data[inst_index].str_tok;
+                    const str_tok = data[lhs].str_tok;
                     const file_path = str_tok.get(file.zir);
 
                     const name = try std.fmt.allocPrint(self.arena, "@import({s})", .{file_path});
@@ -906,7 +908,7 @@ fn walkInstruction(
             const pl_node = data[inst_index].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);
 
-            const callee = DocData.TypeRef.fromWalkResult(
+            const callee = walkResultToTypeRef(
                 try self.walkRef(file, parent_scope, extra.data.callee),
             );
 
@@ -917,11 +919,21 @@ fn walkInstruction(
                 args[idx] = try self.walkRef(file, parent_scope, ref);
             }
 
+            // TODO: see if we can ever do something better than just always
+            //       resolve function calls to a comptimeExpr.
+            const cte_slot_index = self.comptime_exprs.items.len;
+            try self.comptime_exprs.append(self.arena, .{
+                .code = "func call",
+                .typeRef = .{
+                    .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr),
+                }, // TODO: extract return type from callee when available
+            });
+
             const call_slot_index = self.calls.items.len;
             try self.calls.append(self.arena, .{
                 .func = callee,
                 .args = args,
-                .ret = .{ .void = {} }, // TODO: handle returns!
+                .ret = .{ .comptimeExpr = cte_slot_index },
             });
 
             return DocData.WalkResult{ .call = call_slot_index };
@@ -967,7 +979,7 @@ fn walkInstruction(
                         const param_type_ref = try self.walkRef(file, parent_scope, break_operand);
 
                         param_type_refs.appendAssumeCapacity(
-                            DocData.TypeRef.fromWalkResult(param_type_ref),
+                            walkResultToTypeRef(param_type_ref),
                         );
                     },
                 }
@@ -978,7 +990,7 @@ fn walkInstruction(
                 const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1];
                 const break_operand = data[last_instr_index].@"break".operand;
                 const wr = try self.walkRef(file, parent_scope, break_operand);
-                break :blk DocData.TypeRef.fromWalkResult(wr);
+                break :blk walkResultToTypeRef(wr);
             };
 
             self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;
@@ -1025,7 +1037,10 @@ fn walkInstruction(
                     );
                 },
                 .union_decl => {
-                    var scope: Scope = .{ .parent = parent_scope };
+                    var scope: Scope = .{
+                        .parent = parent_scope,
+                        .enclosing_type = type_slot_index,
+                    };
 
                     const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
                     var extra_index: usize = extended.operand;
@@ -1128,7 +1143,10 @@ fn walkInstruction(
                     return DocData.WalkResult{ .type = type_slot_index };
                 },
                 .enum_decl => {
-                    var scope: Scope = .{ .parent = parent_scope };
+                    var scope: Scope = .{
+                        .parent = parent_scope,
+                        .enclosing_type = type_slot_index,
+                    };
 
                     const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small);
                     var extra_index: usize = extended.operand;
@@ -1256,7 +1274,10 @@ fn walkInstruction(
                     return DocData.WalkResult{ .type = type_slot_index };
                 },
                 .struct_decl => {
-                    var scope: Scope = .{ .parent = parent_scope };
+                    var scope: Scope = .{
+                        .parent = parent_scope,
+                        .enclosing_type = type_slot_index,
+                    };
 
                     const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
                     var extra_index: usize = extended.operand;
@@ -1345,6 +1366,24 @@ fn walkInstruction(
 
                     return DocData.WalkResult{ .type = type_slot_index };
                 },
+                .this => {
+                    // TODO: consider if we should reuse an existing decl
+                    //       that points to this type (if present).
+                    const decl_slot_index = self.decls.items.len;
+                    try self.decls.append(self.arena, .{
+                        .name = "@This()",
+                        .value = .{ .type = parent_scope.enclosing_type },
+                        .src = 0,
+                        .kind = "const",
+                        ._analyzed = false,
+                    });
+                    const dpath = try self.arena.alloc(usize, 1);
+                    dpath[0] = decl_slot_index;
+                    return DocData.WalkResult{ .declPath = .{
+                        .hasCte = false,
+                        .path = dpath,
+                    } };
+                },
             }
         },
     }
@@ -1625,11 +1664,11 @@ fn tryResolveDeclPath(
         switch (parent.value) {
             else => {
                 std.debug.panic(
-                    "TODO: handle `{s}`in tryResolveDecl.field_val\n \"{s}\":{}",
+                    "TODO: handle `{s}`in tryResolveDecl\n \"{s}\":{}",
                     .{ @tagName(parent.value), parent.name, parent.value },
                 );
             },
-            .comptimeExpr => {
+            .comptimeExpr, .call => {
                 // Since we hit a cte, we leave the remaining strings unresolved
                 // and completely give up on resolving this decl path.
                 decl_path.hasCte = true;
@@ -1968,12 +2007,13 @@ fn walkRef(
 fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
     return switch (wr) {
         else => std.debug.panic(
-            "TODO: handle `{s}` in `walkResultToTypeRef.as_node.dest_type`\n",
+            "TODO: handle `{s}` in `walkResultToTypeRef`\n",
             .{@tagName(wr)},
         ),
 
         .declPath => |v| .{ .declPath = v },
         .type => |v| .{ .type = v },
+        .call => |v| .{ .call = v },
     };
 }