From 056ba8e57c9c2f4cfc6e72303af19a6476205a6f Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Wed, 9 Mar 2022 18:35:12 +0100 Subject: [PATCH] autodoc: add support for `@This` and improve call support in decl paths --- lib/docs/main.js | 39 +++++++++++--------- src/Autodoc.zig | 92 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 88 insertions(+), 43 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 07055cf570..1f986cb87d 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -661,9 +661,9 @@ if (typeValue.hasCte) { // TODO: find the cte, print it nicely if (wantLink) { - return '# CTE TODO #'; + return '[ComptimeExpr]'; } 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]; + } 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 += ''; - payloadHtml += '' + escapeHtml(decl.name) + ''; - payloadHtml += ''; + var valTypeName = typeShorthandName(valType); + payloadHtml += ''; + payloadHtml += '' + escapeHtml(decl.name) + ''; + payloadHtml += ''; + } } else if ("type" in value) { var name = typeValueName(value, false); payloadHtml += '' + escapeHtml(name) + ''; } else if ("comptimeExpr" in value) { - payloadHtml += ' # CTE TODO #'; + payloadHtml += '[ComptimeExpr]'; } else if (wantHtml) { payloadHtml += 'var'; } else { @@ -1350,7 +1355,7 @@ // TODO: handle nested decl paths properly! if (field.hasCte) { - html += "# CTE TODO #"; + html += "[ComptimeExpr]"; break; } diff --git a/src/Autodoc.zig b/src/Autodoc.zig index eb9c490d95..7eb9e38ee5 100644 --- a/src/Autodoc.zig +++ b/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 }, }; }