autodoc: add support for @This and improve call support in decl paths

This commit is contained in:
Loris Cro 2022-03-09 18:35:12 +01:00 committed by Andrew Kelley
parent 3eb90a110f
commit 056ba8e57c
2 changed files with 88 additions and 43 deletions

View File

@ -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];
} 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>';
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;
}

View File

@ -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 },
};
}