mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 05:20:34 +00:00
autodoc: add basic support for more builtin
This commit is contained in:
parent
ec7f4d1faa
commit
67f1d2b967
245
src/Autodoc.zig
245
src/Autodoc.zig
@ -532,13 +532,15 @@ const DocData = struct {
|
||||
// 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 `calls`
|
||||
typeOf: *WalkResult,
|
||||
|
||||
pub fn jsonStringify(
|
||||
self: TypeRef,
|
||||
_: std.json.StringifyOptions,
|
||||
options: std.json.StringifyOptions,
|
||||
w: anytype,
|
||||
) !void {
|
||||
switch (self) {
|
||||
.typeOf => |v| try std.json.stringify(v, options, w),
|
||||
.unspecified, .@"anytype" => {
|
||||
try w.print(
|
||||
\\{{ "{s}":{{}} }}
|
||||
@ -591,13 +593,19 @@ const DocData = struct {
|
||||
array: Array,
|
||||
call: usize, // index in `calls`
|
||||
enumLiteral: []const u8,
|
||||
typeOf: *WalkResult,
|
||||
sizeOf: *WalkResult,
|
||||
compileError: []const u8,
|
||||
string: []const u8,
|
||||
|
||||
const Struct = struct {
|
||||
typeRef: TypeRef,
|
||||
fieldVals: []struct {
|
||||
fieldVals: []FieldVal,
|
||||
|
||||
const FieldVal = struct {
|
||||
name: []const u8,
|
||||
val: WalkResult,
|
||||
},
|
||||
};
|
||||
};
|
||||
const Array = struct {
|
||||
typeRef: TypeRef,
|
||||
@ -647,6 +655,9 @@ const DocData = struct {
|
||||
},
|
||||
.@"undefined" => |v| try std.json.stringify(v, options, w),
|
||||
.@"null" => |v| try std.json.stringify(v, options, w),
|
||||
.typeOf, .sizeOf => |v| try std.json.stringify(v, options, w),
|
||||
.compileError => |v| try std.json.stringify(v, options, w),
|
||||
.string => |v| try std.json.stringify(v, options, w),
|
||||
.@"struct" => |v| try std.json.stringify(
|
||||
struct { @"struct": Struct }{ .@"struct" = v },
|
||||
options,
|
||||
@ -709,11 +720,21 @@ fn walkInstruction(
|
||||
|
||||
switch (tags[inst_index]) {
|
||||
else => {
|
||||
std.debug.panic(
|
||||
panicWithContext(
|
||||
file,
|
||||
inst_index,
|
||||
"TODO: implement `{s}` for walkInstruction\n\n",
|
||||
.{@tagName(tags[inst_index])},
|
||||
);
|
||||
},
|
||||
.closure_get => {
|
||||
const inst_node = data[inst_index].inst_node;
|
||||
return try self.walkInstruction(file, parent_scope, inst_node.inst);
|
||||
},
|
||||
.closure_capture => {
|
||||
const un_tok = data[inst_index].un_tok;
|
||||
return try self.walkRef(file, parent_scope, un_tok.operand);
|
||||
},
|
||||
.import => {
|
||||
const str_tok = data[inst_index].str_tok;
|
||||
const path = str_tok.get(file.zir);
|
||||
@ -749,11 +770,46 @@ fn walkInstruction(
|
||||
|
||||
return new_file_walk_result;
|
||||
},
|
||||
.str => {
|
||||
const str = data[inst_index].str;
|
||||
return DocData.WalkResult{
|
||||
.string = str.get(file.zir),
|
||||
};
|
||||
},
|
||||
.compile_error => {
|
||||
const un_node = data[inst_index].un_node;
|
||||
var operand: DocData.WalkResult = try self.walkRef(
|
||||
file,
|
||||
parent_scope,
|
||||
un_node.operand,
|
||||
);
|
||||
|
||||
return DocData.WalkResult{ .compileError = operand.string };
|
||||
},
|
||||
.switch_block => {
|
||||
const cte_slot_index = self.comptime_exprs.items.len;
|
||||
try self.comptime_exprs.append(self.arena, .{
|
||||
.code = "switch",
|
||||
.typeRef = .{
|
||||
.type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr),
|
||||
},
|
||||
});
|
||||
|
||||
return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
|
||||
},
|
||||
.enum_literal => {
|
||||
const str_tok = data[inst_index].str_tok;
|
||||
const literal = file.zir.nullTerminatedString(str_tok.start);
|
||||
return DocData.WalkResult{ .enumLiteral = literal };
|
||||
},
|
||||
.div_exact, .div => {
|
||||
const cte_slot_index = self.comptime_exprs.items.len;
|
||||
try self.comptime_exprs.append(self.arena, .{
|
||||
.code = "@div*(...)",
|
||||
.typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) },
|
||||
});
|
||||
return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
|
||||
},
|
||||
.int => {
|
||||
const int = data[inst_index].int;
|
||||
return DocData.WalkResult{
|
||||
@ -785,6 +841,25 @@ fn walkInstruction(
|
||||
|
||||
return DocData.WalkResult{ .type = type_slot_index };
|
||||
},
|
||||
.ptr_type => {
|
||||
const ptr = data[inst_index].ptr_type;
|
||||
const extra = file.zir.extraData(Zir.Inst.PtrType, ptr.payload_index);
|
||||
|
||||
const type_slot_index = self.types.items.len;
|
||||
const elem_type_ref = try self.walkRef(
|
||||
file,
|
||||
parent_scope,
|
||||
extra.data.elem_type,
|
||||
);
|
||||
try self.types.append(self.arena, .{
|
||||
.Pointer = .{
|
||||
.size = ptr.size,
|
||||
.child = walkResultToTypeRef(elem_type_ref),
|
||||
},
|
||||
});
|
||||
|
||||
return DocData.WalkResult{ .type = type_slot_index };
|
||||
},
|
||||
.array_type => {
|
||||
const bin = data[inst_index].bin;
|
||||
const len = try self.walkRef(file, parent_scope, bin.lhs);
|
||||
@ -848,6 +923,27 @@ fn walkInstruction(
|
||||
operand.int.negated = true; // only support ints for now
|
||||
return operand;
|
||||
},
|
||||
.size_of => {
|
||||
const un_node = data[inst_index].un_node;
|
||||
var operand = try self.arena.create(DocData.WalkResult);
|
||||
operand.* = try self.walkRef(
|
||||
file,
|
||||
parent_scope,
|
||||
un_node.operand,
|
||||
);
|
||||
return DocData.WalkResult{ .sizeOf = operand };
|
||||
},
|
||||
|
||||
.typeof => {
|
||||
const un_node = data[inst_index].un_node;
|
||||
var operand = try self.arena.create(DocData.WalkResult);
|
||||
operand.* = try self.walkRef(
|
||||
file,
|
||||
parent_scope,
|
||||
un_node.operand,
|
||||
);
|
||||
return DocData.WalkResult{ .typeOf = operand };
|
||||
},
|
||||
.as_node => {
|
||||
const pl_node = data[inst_index].pl_node;
|
||||
const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
|
||||
@ -857,11 +953,13 @@ fn walkInstruction(
|
||||
var operand = try self.walkRef(file, parent_scope, extra.data.operand);
|
||||
|
||||
switch (operand) {
|
||||
else => std.debug.panic(
|
||||
else => panicWithContext(
|
||||
file,
|
||||
inst_index,
|
||||
"TODO: handle {s} in `walkInstruction.as_node`\n",
|
||||
.{@tagName(operand)},
|
||||
),
|
||||
.declPath, .type => {},
|
||||
.declPath, .type, .string => {},
|
||||
// we don't do anything because up until now,
|
||||
// I've only seen this used as such:
|
||||
// @as(@as(type, Baz), .{})
|
||||
@ -894,14 +992,16 @@ fn walkInstruction(
|
||||
});
|
||||
return res;
|
||||
},
|
||||
.decl_val => {
|
||||
.decl_val, .decl_ref => {
|
||||
const str_tok = data[inst_index].str_tok;
|
||||
const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
|
||||
var path = try self.arena.alloc(usize, 1);
|
||||
path[0] = decls_slot_index;
|
||||
return DocData.WalkResult{ .declPath = .{ .path = path } };
|
||||
},
|
||||
.field_val, .field_call_bind, .field_ptr => {
|
||||
.field_val, .field_call_bind, .field_ptr, .field_type => {
|
||||
// TODO: field type uses Zir.Inst.FieldType, it just happens to have the
|
||||
// same layout as Zir.Inst.Field :^)
|
||||
const pl_node = data[inst_index].pl_node;
|
||||
const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
|
||||
|
||||
@ -909,8 +1009,8 @@ fn walkInstruction(
|
||||
var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
|
||||
|
||||
try path.append(self.arena, extra.data.field_name_start);
|
||||
// Put inside path the starting index of each decl name
|
||||
// that we encounter as we navigate through all the field_vals
|
||||
// Put inside path the starting index of each decl name that
|
||||
// we encounter as we navigate through all the field_vals
|
||||
while (tags[lhs] == .field_val or
|
||||
tags[lhs] == .field_call_bind or
|
||||
tags[lhs] == .field_ptr)
|
||||
@ -925,11 +1025,35 @@ fn walkInstruction(
|
||||
}
|
||||
|
||||
switch (tags[lhs]) {
|
||||
else => {
|
||||
std.debug.panic(
|
||||
"TODO: handle `{s}` endings in walkInstruction.field_val",
|
||||
.{@tagName(tags[lhs])},
|
||||
);
|
||||
else => panicWithContext(
|
||||
file,
|
||||
inst_index,
|
||||
"TODO: handle `{s}` in walkInstruction.field_val",
|
||||
.{@tagName(tags[lhs])},
|
||||
),
|
||||
.call => {
|
||||
const walk_result = try self.walkInstruction(file, parent_scope, lhs);
|
||||
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,
|
||||
});
|
||||
break :idx idx;
|
||||
};
|
||||
|
||||
const decls_slot_index = self.decls.items.len;
|
||||
try self.decls.append(self.arena, .{
|
||||
._analyzed = true,
|
||||
.name = "call()",
|
||||
.src = ast_node_index,
|
||||
.value = walk_result,
|
||||
.kind = "const",
|
||||
});
|
||||
try path.append(self.arena, decls_slot_index);
|
||||
},
|
||||
.import => {
|
||||
const walk_result = try self.walkInstruction(file, parent_scope, lhs);
|
||||
@ -942,7 +1066,7 @@ fn walkInstruction(
|
||||
.line = 0,
|
||||
.col = 0,
|
||||
.docs = "",
|
||||
.fields = null, // walkInstruction will fill `fields` if necessary
|
||||
.fields = null,
|
||||
});
|
||||
break :idx idx;
|
||||
};
|
||||
@ -999,6 +1123,76 @@ fn walkInstruction(
|
||||
.block_inline => {
|
||||
return self.walkRef(file, parent_scope, getBlockInlineBreak(file.zir, inst_index));
|
||||
},
|
||||
.struct_init => {
|
||||
const pl_node = data[inst_index].pl_node;
|
||||
const extra = file.zir.extraData(Zir.Inst.StructInit, pl_node.payload_index);
|
||||
const field_vals = try self.arena.alloc(
|
||||
DocData.WalkResult.Struct.FieldVal,
|
||||
extra.data.fields_len,
|
||||
);
|
||||
|
||||
var type_ref: DocData.TypeRef = undefined;
|
||||
var idx = extra.end;
|
||||
for (field_vals) |*fv| {
|
||||
const init_extra = file.zir.extraData(Zir.Inst.StructInit.Item, idx);
|
||||
idx = init_extra.end;
|
||||
|
||||
const field_name = blk: {
|
||||
const field_inst_index = init_extra.data.field_type;
|
||||
if (tags[field_inst_index] != .field_type) unreachable;
|
||||
const field_pl_node = data[field_inst_index].pl_node;
|
||||
const field_extra = file.zir.extraData(
|
||||
Zir.Inst.FieldType,
|
||||
field_pl_node.payload_index,
|
||||
);
|
||||
|
||||
// On first iteration use field info to find out the struct type
|
||||
if (idx == extra.end) {
|
||||
const wr = try self.walkRef(
|
||||
file,
|
||||
parent_scope,
|
||||
field_extra.data.container_type,
|
||||
);
|
||||
type_ref = walkResultToTypeRef(wr);
|
||||
}
|
||||
break :blk file.zir.nullTerminatedString(field_extra.data.name_start);
|
||||
};
|
||||
|
||||
const value = try self.walkRef(file, parent_scope, init_extra.data.init);
|
||||
fv.* = .{ .name = field_name, .val = value };
|
||||
}
|
||||
|
||||
return DocData.WalkResult{ .@"struct" = .{
|
||||
.typeRef = type_ref,
|
||||
.fieldVals = field_vals,
|
||||
} };
|
||||
},
|
||||
.param_anytype => {
|
||||
// Analysis of anytype function params happens in `.func`.
|
||||
// This switch case handles the case where an expression depends
|
||||
// on an anytype field. E.g.: `fn foo(bar: anytype) @TypeOf(bar)`.
|
||||
// This means that we're looking at a generic expression.
|
||||
const str_tok = data[inst_index].str_tok;
|
||||
const name = str_tok.get(file.zir);
|
||||
const cte_slot_index = self.comptime_exprs.items.len;
|
||||
try self.comptime_exprs.append(self.arena, .{
|
||||
.code = name,
|
||||
.typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) },
|
||||
});
|
||||
return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
|
||||
},
|
||||
.param, .param_comptime => {
|
||||
// See .param_anytype for more information.
|
||||
const pl_tok = data[inst_index].pl_tok;
|
||||
const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index);
|
||||
const name = file.zir.nullTerminatedString(extra.data.name);
|
||||
const cte_slot_index = self.comptime_exprs.items.len;
|
||||
try self.comptime_exprs.append(self.arena, .{
|
||||
.code = name,
|
||||
.typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) },
|
||||
});
|
||||
return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
|
||||
},
|
||||
.call => {
|
||||
const pl_node = data[inst_index].pl_node;
|
||||
const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);
|
||||
@ -1048,12 +1242,12 @@ fn walkInstruction(
|
||||
// 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 => {
|
||||
std.debug.panic(
|
||||
"TODO: handle `{s}` in walkInstruction.func\n",
|
||||
.{@tagName(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;
|
||||
@ -2158,6 +2352,8 @@ fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
|
||||
.{@tagName(wr)},
|
||||
),
|
||||
|
||||
.typeOf => |v| .{ .typeOf = v },
|
||||
.comptimeExpr => |v| .{ .comptimeExpr = v },
|
||||
.declPath => |v| .{ .declPath = v },
|
||||
.type => |v| .{ .type = v },
|
||||
.call => |v| .{ .call = v },
|
||||
@ -2187,3 +2383,8 @@ fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref {
|
||||
const break_index = zir.extra[extra.end..][extra.data.body_len - 1];
|
||||
return data[break_index].@"break".operand;
|
||||
}
|
||||
|
||||
fn panicWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: anytype) noreturn {
|
||||
std.debug.print("Context [{s}] % {}\n", .{ file.sub_file_path, inst });
|
||||
std.debug.panic(fmt, args);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user