autodoc: added support for error sets and extended functions

This commit is contained in:
Loris Cro 2022-03-13 19:25:34 +01:00 committed by Andrew Kelley
parent 67f1d2b967
commit 63be9e65ed
2 changed files with 154 additions and 84 deletions

View file

@ -236,6 +236,10 @@
return { type: typeTypeId };
}
if ("bool" in decl.value) {
return { type: typeKinds.Bool };
}
console.log("TODO: handle in `typeOfDecl` more cases: ", decl);
console.assert(false);
throw {};

View file

@ -402,7 +402,10 @@ const DocData = struct {
child: TypeRef,
},
ErrorUnion: struct { name: []const u8 },
ErrorSet: struct { name: []const u8 },
ErrorSet: struct {
name: []const u8,
fields: []const Field,
},
Enum: struct {
name: []const u8,
src: ?usize = null, // index into astNodes
@ -430,6 +433,11 @@ const DocData = struct {
Vector: struct { name: []const u8 },
EnumLiteral: struct { name: []const u8 },
const Field = struct {
name: []const u8,
docs: []const u8,
};
pub fn jsonStringify(
self: Type,
opt: std.json.StringifyOptions,
@ -1167,6 +1175,37 @@ fn walkInstruction(
.fieldVals = field_vals,
} };
},
.error_set_decl => {
const pl_node = data[inst_index].pl_node;
const extra = file.zir.extraData(Zir.Inst.ErrorSetDecl, pl_node.payload_index);
const fields = try self.arena.alloc(
DocData.Type.Field,
extra.data.fields_len,
);
var idx = extra.end;
for (fields) |*f| {
const name = file.zir.nullTerminatedString(file.zir.extra[idx]);
idx += 1;
const docs = file.zir.nullTerminatedString(file.zir.extra[idx]);
idx += 1;
f.* = .{
.name = name,
.docs = docs,
};
}
const type_slot_index = self.types.items.len;
try self.types.append(self.arena, .{
.ErrorSet = .{
.name = "todo errset",
.fields = fields,
},
});
return DocData.WalkResult{ .type = type_slot_index };
},
.param_anytype => {
// Analysis of anytype function params happens in `.func`.
// This switch case handles the case where an expression depends
@ -1228,88 +1267,12 @@ fn walkInstruction(
return DocData.WalkResult{ .call = call_slot_index };
},
.func, .func_inferred => {
const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index));
try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len);
var param_type_refs = try std.ArrayListUnmanaged(DocData.TypeRef).initCapacity(
self.arena,
fn_info.total_params_len,
return self.analyzeFunction(
file,
parent_scope,
inst_index,
self_ast_node_index,
);
var param_ast_indexes = try std.ArrayListUnmanaged(usize).initCapacity(
self.arena,
fn_info.total_params_len,
);
// TODO: handle scope rules for fn parameters
for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
switch (tags[param_index]) {
else => panicWithContext(
file,
param_index,
"TODO: handle `{s}` in walkInstruction.func\n",
.{@tagName(tags[param_index])},
),
.param_anytype => {
// TODO: where are the doc comments?
const str_tok = data[param_index].str_tok;
const name = str_tok.get(file.zir);
param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len);
self.ast_nodes.appendAssumeCapacity(.{
.name = name,
.docs = "",
.@"comptime" = true,
});
param_type_refs.appendAssumeCapacity(
DocData.TypeRef{ .@"anytype" = {} },
);
},
.param, .param_comptime => {
const pl_tok = data[param_index].pl_tok;
const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index);
const doc_comment = if (extra.data.doc_comment != 0)
file.zir.nullTerminatedString(extra.data.doc_comment)
else
"";
const name = file.zir.nullTerminatedString(extra.data.name);
param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len);
self.ast_nodes.appendAssumeCapacity(.{
.name = name,
.docs = doc_comment,
.@"comptime" = tags[param_index] == .param_comptime,
});
const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1];
const break_operand = data[break_index].@"break".operand;
const param_type_ref = try self.walkRef(file, parent_scope, break_operand);
param_type_refs.appendAssumeCapacity(
walkResultToTypeRef(param_type_ref),
);
},
}
}
// ret
const ret_type_ref = blk: {
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 walkResultToTypeRef(wr);
};
self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;
try self.types.append(self.arena, .{
.Fn = .{
.name = "todo_name func",
.src = self_ast_node_index,
.params = param_type_refs.items,
.ret = ret_type_ref,
},
});
return DocData.WalkResult{ .type = self.types.items.len - 1 };
},
.extended => {
// NOTE: this code + the subsequent defer block are working towards
@ -1338,11 +1301,21 @@ fn walkInstruction(
const extended = data[inst_index].extended;
switch (extended.opcode) {
else => {
std.debug.panic(
"TODO: implement `walkinstruction.extended` for {s}\n\n",
panicWithContext(
file,
inst_index,
"TODO: implement `walkInstruction.extended` for {s}\n\n",
.{@tagName(extended.opcode)},
);
},
.func => {
return try self.analyzeFunction(
file,
parent_scope,
inst_index,
self_ast_node_index,
);
},
.variable => {
const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);
var extra_index: usize = extended.operand;
@ -2105,6 +2078,99 @@ fn tryResolveDeclPath(
}
}
fn analyzeFunction(
self: *Autodoc,
file: *File,
scope: *Scope,
inst_index: usize,
self_ast_node_index: usize,
) error{OutOfMemory}!DocData.WalkResult {
const tags = file.zir.instructions.items(.tag);
const data = file.zir.instructions.items(.data);
const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index));
try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len);
var param_type_refs = try std.ArrayListUnmanaged(DocData.TypeRef).initCapacity(
self.arena,
fn_info.total_params_len,
);
var param_ast_indexes = try std.ArrayListUnmanaged(usize).initCapacity(
self.arena,
fn_info.total_params_len,
);
// TODO: handle scope rules for fn parameters
for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
switch (tags[param_index]) {
else => panicWithContext(
file,
param_index,
"TODO: handle `{s}` in walkInstruction.func\n",
.{@tagName(tags[param_index])},
),
.param_anytype => {
// TODO: where are the doc comments?
const str_tok = data[param_index].str_tok;
const name = str_tok.get(file.zir);
param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len);
self.ast_nodes.appendAssumeCapacity(.{
.name = name,
.docs = "",
.@"comptime" = true,
});
param_type_refs.appendAssumeCapacity(
DocData.TypeRef{ .@"anytype" = {} },
);
},
.param, .param_comptime => {
const pl_tok = data[param_index].pl_tok;
const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index);
const doc_comment = if (extra.data.doc_comment != 0)
file.zir.nullTerminatedString(extra.data.doc_comment)
else
"";
const name = file.zir.nullTerminatedString(extra.data.name);
param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len);
self.ast_nodes.appendAssumeCapacity(.{
.name = name,
.docs = doc_comment,
.@"comptime" = tags[param_index] == .param_comptime,
});
const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1];
const break_operand = data[break_index].@"break".operand;
const param_type_ref = try self.walkRef(file, scope, break_operand);
param_type_refs.appendAssumeCapacity(
walkResultToTypeRef(param_type_ref),
);
},
}
}
// ret
const ret_type_ref = blk: {
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, scope, break_operand);
break :blk walkResultToTypeRef(wr);
};
self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;
try self.types.append(self.arena, .{
.Fn = .{
.name = "todo_name func",
.src = self_ast_node_index,
.params = param_type_refs.items,
.ret = ret_type_ref,
},
});
return DocData.WalkResult{ .type = self.types.items.len - 1 };
}
fn collectUnionFieldInfo(
self: *Autodoc,
file: *File,