mirror of
https://github.com/ziglang/zig.git
synced 2026-01-25 00:35:20 +00:00
1358 lines
52 KiB
Zig
1358 lines
52 KiB
Zig
const std = @import("std");
|
|
const Autodoc = @This();
|
|
const Compilation = @import("Compilation.zig");
|
|
const Module = @import("Module.zig");
|
|
const File = Module.File;
|
|
const Zir = @import("Zir.zig");
|
|
const Ref = Zir.Inst.Ref;
|
|
|
|
module: *Module,
|
|
doc_location: Compilation.EmitLoc,
|
|
arena: std.mem.Allocator,
|
|
types: std.ArrayListUnmanaged(DocData.Type) = .{},
|
|
decls: std.ArrayListUnmanaged(DocData.Decl) = .{},
|
|
ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{},
|
|
|
|
var arena_allocator: std.heap.ArenaAllocator = undefined;
|
|
pub fn init(m: *Module, doc_location: Compilation.EmitLoc) Autodoc {
|
|
arena_allocator = std.heap.ArenaAllocator.init(m.gpa);
|
|
return .{
|
|
.module = m,
|
|
.doc_location = doc_location,
|
|
.arena = arena_allocator.allocator(),
|
|
};
|
|
}
|
|
|
|
pub fn deinit(_: *Autodoc) void {
|
|
arena_allocator.deinit();
|
|
}
|
|
|
|
pub fn generateZirData(self: *Autodoc) !void {
|
|
if (self.doc_location.directory) |dir| {
|
|
if (dir.path) |path| {
|
|
std.debug.print("path: {s}\n", .{path});
|
|
}
|
|
}
|
|
std.debug.print("basename: {s}\n", .{self.doc_location.basename});
|
|
|
|
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
|
const dir =
|
|
if (self.module.main_pkg.root_src_directory.path) |rp|
|
|
std.os.realpath(rp, &buf) catch unreachable
|
|
else
|
|
std.os.getcwd(&buf) catch unreachable;
|
|
const root_file_path = self.module.main_pkg.root_src_path;
|
|
const abs_root_path = try std.fs.path.join(self.arena, &.{ dir, root_file_path });
|
|
defer self.arena.free(abs_root_path);
|
|
const file = self.module.import_table.get(abs_root_path).?;
|
|
|
|
// append all the types in Zir.Inst.Ref
|
|
{
|
|
|
|
// TODO: we don't want to add .none, but the index math has to check out
|
|
var i: u32 = 0;
|
|
while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) {
|
|
var tmpbuf = std.ArrayList(u8).init(self.arena);
|
|
try Ref.typed_value_map[i].val.format("", .{}, tmpbuf.writer());
|
|
try self.types.append(
|
|
self.arena,
|
|
switch (@intToEnum(Ref, i)) {
|
|
else => blk: {
|
|
//std.debug.print("TODO: categorize `{s}` in typeKinds\n", .{
|
|
// @tagName(t),
|
|
//});
|
|
break :blk .{
|
|
.Array = .{ .name = tmpbuf.toOwnedSlice() },
|
|
};
|
|
},
|
|
.u1_type,
|
|
.u8_type,
|
|
.i8_type,
|
|
.u16_type,
|
|
.i16_type,
|
|
.u32_type,
|
|
.i32_type,
|
|
.u64_type,
|
|
.i64_type,
|
|
.u128_type,
|
|
.i128_type,
|
|
.usize_type,
|
|
.isize_type,
|
|
.c_short_type,
|
|
.c_ushort_type,
|
|
.c_int_type,
|
|
.c_uint_type,
|
|
.c_long_type,
|
|
.c_ulong_type,
|
|
.c_longlong_type,
|
|
.c_ulonglong_type,
|
|
.c_longdouble_type,
|
|
=> .{
|
|
.Int = .{ .name = tmpbuf.toOwnedSlice() },
|
|
},
|
|
.f16_type,
|
|
.f32_type,
|
|
.f64_type,
|
|
.f128_type,
|
|
=> .{
|
|
.Float = .{ .name = tmpbuf.toOwnedSlice() },
|
|
},
|
|
.comptime_int_type => .{
|
|
.ComptimeInt = .{ .name = tmpbuf.toOwnedSlice() },
|
|
},
|
|
.comptime_float_type => .{
|
|
.ComptimeFloat = .{ .name = tmpbuf.toOwnedSlice() },
|
|
},
|
|
|
|
.bool_type => .{
|
|
.Bool = .{ .name = tmpbuf.toOwnedSlice() },
|
|
},
|
|
|
|
.void_type => .{
|
|
.Void = .{ .name = tmpbuf.toOwnedSlice() },
|
|
},
|
|
.type_type => .{
|
|
.Type = .{ .name = tmpbuf.toOwnedSlice() },
|
|
},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
var root_scope: Scope = .{ .parent = null };
|
|
try self.ast_nodes.append(self.arena, .{ .name = "(root)" });
|
|
const main_type_index = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst);
|
|
|
|
var data = DocData{
|
|
.files = &[1][]const u8{root_file_path},
|
|
.types = self.types.items,
|
|
.decls = self.decls.items,
|
|
.astNodes = self.ast_nodes.items,
|
|
};
|
|
|
|
data.packages[0].main = main_type_index.type;
|
|
|
|
if (self.doc_location.directory) |d| {
|
|
d.handle.makeDir(
|
|
self.doc_location.basename,
|
|
) catch |e| switch (e) {
|
|
error.PathAlreadyExists => {},
|
|
else => unreachable,
|
|
};
|
|
} else {
|
|
self.module.zig_cache_artifact_directory.handle.makeDir(
|
|
self.doc_location.basename,
|
|
) catch |e| switch (e) {
|
|
error.PathAlreadyExists => {},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
const output_dir = if (self.doc_location.directory) |d|
|
|
(d.handle.openDir(self.doc_location.basename, .{}) catch unreachable)
|
|
else
|
|
(self.module.zig_cache_artifact_directory.handle.openDir(self.doc_location.basename, .{}) catch unreachable);
|
|
const data_js_f = output_dir.createFile("data.js", .{}) catch unreachable;
|
|
defer data_js_f.close();
|
|
const out = data_js_f.writer();
|
|
out.print("zigAnalysis=", .{}) catch unreachable;
|
|
std.json.stringify(
|
|
data,
|
|
.{
|
|
.whitespace = .{},
|
|
.emit_null_optional_fields = false,
|
|
},
|
|
out,
|
|
) catch unreachable;
|
|
out.print(";", .{}) catch unreachable;
|
|
// copy main.js, index.html
|
|
const special = try self.module.comp.zig_lib_directory.join(self.arena, &.{ "std", "special", "docs", std.fs.path.sep_str });
|
|
var special_dir = std.fs.openDirAbsolute(special, .{}) catch unreachable;
|
|
defer special_dir.close();
|
|
special_dir.copyFile("main.js", output_dir, "main.js", .{}) catch unreachable;
|
|
special_dir.copyFile("index.html", output_dir, "index.html", .{}) catch unreachable;
|
|
}
|
|
|
|
const Scope = struct {
|
|
parent: ?*Scope,
|
|
map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls`
|
|
|
|
/// Assumes all decls in present scope and upper scopes have already
|
|
/// been either fully resolved or at least reserved.
|
|
pub fn resolveDeclName(self: Scope, string_table_idx: u32) usize {
|
|
var cur: ?*const Scope = &self;
|
|
return while (cur) |s| : (cur = s.parent) {
|
|
break s.map.get(string_table_idx) orelse continue;
|
|
} else unreachable;
|
|
}
|
|
|
|
pub fn insertDeclRef(
|
|
self: *Scope,
|
|
arena: std.mem.Allocator,
|
|
decl_name_index: u32, // decl name
|
|
decls_slot_index: usize,
|
|
) !void {
|
|
try self.map.put(arena, decl_name_index, decls_slot_index);
|
|
}
|
|
};
|
|
|
|
const DocData = struct {
|
|
typeKinds: []const []const u8 = std.meta.fieldNames(std.builtin.TypeId),
|
|
rootPkg: u32 = 0,
|
|
params: struct {
|
|
zigId: []const u8 = "arst",
|
|
zigVersion: []const u8 = "arst",
|
|
target: []const u8 = "arst",
|
|
rootName: []const u8 = "arst",
|
|
builds: []const struct { target: []const u8 } = &.{
|
|
.{ .target = "arst" },
|
|
},
|
|
} = .{},
|
|
packages: [1]Package = .{.{}},
|
|
fns: []struct {} = &.{},
|
|
errors: []struct {} = &.{},
|
|
calls: []struct {} = &.{},
|
|
|
|
// non-hardcoded stuff
|
|
astNodes: []AstNode,
|
|
files: []const []const u8,
|
|
types: []Type,
|
|
decls: []Decl,
|
|
|
|
const Package = struct {
|
|
name: []const u8 = "root",
|
|
file: usize = 0, // index into files
|
|
main: usize = 0, // index into decls
|
|
table: struct { root: usize } = .{
|
|
.root = 0,
|
|
},
|
|
};
|
|
|
|
const Decl = struct {
|
|
name: []const u8,
|
|
kind: []const u8, // TODO: where do we find this info?
|
|
src: usize, // index into astNodes
|
|
// typeRef: TypeRef,
|
|
value: WalkResult,
|
|
};
|
|
|
|
const AstNode = struct {
|
|
file: usize = 0, // index into files
|
|
line: usize = 0,
|
|
col: usize = 0,
|
|
name: ?[]const u8 = null,
|
|
docs: ?[]const u8 = null,
|
|
fields: ?[]usize = null, // index into astNodes
|
|
};
|
|
|
|
const Type = union(std.builtin.TypeId) {
|
|
Type: struct { name: []const u8 },
|
|
Void: struct { name: []const u8 },
|
|
Bool: struct { name: []const u8 },
|
|
NoReturn: struct { name: []const u8 },
|
|
Int: struct { name: []const u8 },
|
|
Float: struct { name: []const u8 },
|
|
Pointer: struct { name: []const u8 },
|
|
Array: struct { name: []const u8 },
|
|
Struct: struct {
|
|
name: []const u8,
|
|
src: ?usize = null, // index into astNodes
|
|
privDecls: ?[]usize = null, // index into decls
|
|
pubDecls: ?[]usize = null, // index into decls
|
|
fields: ?[]TypeRef = null, // (use src->fields to find names)
|
|
},
|
|
ComptimeFloat: struct { name: []const u8 },
|
|
ComptimeInt: struct { name: []const u8 },
|
|
Undefined: struct { name: []const u8 },
|
|
Null: struct { name: []const u8 },
|
|
Optional: struct { name: []const u8 },
|
|
ErrorUnion: struct { name: []const u8 },
|
|
ErrorSet: struct { name: []const u8 },
|
|
Enum: struct {
|
|
name: []const u8,
|
|
src: ?usize = null, // index into astNodes
|
|
privDecls: ?[]usize = null, // index into decls
|
|
pubDecls: ?[]usize = null, // index into decls
|
|
// (use src->fields to find field names)
|
|
},
|
|
Union: struct {
|
|
name: []const u8,
|
|
src: ?usize = null, // index into astNodes
|
|
privDecls: ?[]usize = null, // index into decls
|
|
pubDecls: ?[]usize = null, // index into decls
|
|
fields: ?[]TypeRef = null, // (use src->fields to find names)
|
|
},
|
|
Fn: struct {
|
|
name: []const u8,
|
|
src: ?usize = null, // index into astNodes
|
|
ret: TypeRef,
|
|
params: ?[]TypeRef = null, // (use src->fields to find names)
|
|
},
|
|
BoundFn: struct { name: []const u8 },
|
|
Opaque: struct { name: []const u8 },
|
|
Frame: struct { name: []const u8 },
|
|
AnyFrame: struct { name: []const u8 },
|
|
Vector: struct { name: []const u8 },
|
|
EnumLiteral: struct { name: []const u8 },
|
|
|
|
pub fn jsonStringify(
|
|
self: Type,
|
|
opt: std.json.StringifyOptions,
|
|
w: anytype,
|
|
) !void {
|
|
try w.print(
|
|
\\{{ "kind": {},
|
|
\\
|
|
, .{@enumToInt(std.meta.activeTag(self))});
|
|
var options = opt;
|
|
if (options.whitespace) |*ws| ws.indent_level += 1;
|
|
switch (self) {
|
|
.Array => |v| try printTypeBody(v, options, w),
|
|
.Bool => |v| try printTypeBody(v, options, w),
|
|
.Void => |v| try printTypeBody(v, options, w),
|
|
.ComptimeInt => |v| try printTypeBody(v, options, w),
|
|
.ComptimeFloat => |v| try printTypeBody(v, options, w),
|
|
.Null => |v| try printTypeBody(v, options, w),
|
|
|
|
.Struct => |v| try printTypeBody(v, options, w),
|
|
.Fn => |v| try printTypeBody(v, options, w),
|
|
.Union => |v| try printTypeBody(v, options, w),
|
|
.Enum => |v| try printTypeBody(v, options, w),
|
|
.Int => |v| try printTypeBody(v, options, w),
|
|
.Float => |v| try printTypeBody(v, options, w),
|
|
.Type => |v| try printTypeBody(v, options, w),
|
|
else => {
|
|
std.debug.print(
|
|
"TODO: add {s} to `DocData.Type.jsonStringify`\n",
|
|
.{@tagName(self)},
|
|
);
|
|
},
|
|
}
|
|
try w.print("}}", .{});
|
|
}
|
|
|
|
fn printTypeBody(
|
|
body: anytype,
|
|
options: std.json.StringifyOptions,
|
|
w: anytype,
|
|
) !void {
|
|
const fields = std.meta.fields(@TypeOf(body));
|
|
inline for (fields) |f, idx| {
|
|
if (options.whitespace) |ws| try ws.outputIndent(w);
|
|
try w.print("\"{s}\": ", .{f.name});
|
|
try std.json.stringify(@field(body, f.name), options, w);
|
|
if (idx != fields.len - 1) try w.writeByte(',');
|
|
try w.writeByte('\n');
|
|
}
|
|
if (options.whitespace) |ws| {
|
|
var up = ws;
|
|
up.indent_level -= 1;
|
|
try up.outputIndent(w);
|
|
}
|
|
}
|
|
};
|
|
|
|
const TypeRef = union(enum) {
|
|
unspecified,
|
|
declRef: usize, // index in `decls`
|
|
type: usize, // index in `types`
|
|
|
|
pub fn fromWalkResult(wr: WalkResult) TypeRef {
|
|
return switch (wr) {
|
|
.declRef => |v| .{ .declRef = v },
|
|
.type => |v| .{ .type = v },
|
|
else => @panic("Found non-type WalkResult"),
|
|
};
|
|
}
|
|
|
|
pub fn jsonStringify(
|
|
self: TypeRef,
|
|
_: std.json.StringifyOptions,
|
|
w: anytype,
|
|
) !void {
|
|
switch (self) {
|
|
.unspecified => {
|
|
try w.print(
|
|
\\{{ "unspecified":{{}} }}
|
|
, .{});
|
|
},
|
|
|
|
.declRef, .type => |v| {
|
|
try w.print(
|
|
\\{{ "{s}":{} }}
|
|
, .{ @tagName(self), v });
|
|
},
|
|
}
|
|
}
|
|
};
|
|
|
|
const WalkResult = union(enum) {
|
|
void,
|
|
@"unreachable",
|
|
@"null": TypeRef,
|
|
@"undefined": TypeRef,
|
|
@"struct": struct {
|
|
typeRef: TypeRef,
|
|
fieldVals: []struct {
|
|
name: []const u8,
|
|
val: WalkResult,
|
|
},
|
|
},
|
|
bool: bool,
|
|
type: usize, // index in `types`
|
|
declRef: usize, // index in `decls`
|
|
int: struct {
|
|
typeRef: TypeRef,
|
|
value: usize, // direct value
|
|
negated: bool = false,
|
|
},
|
|
float: struct {
|
|
typeRef: TypeRef,
|
|
value: f64, // direct value
|
|
negated: bool = false,
|
|
},
|
|
pub fn jsonStringify(
|
|
self: WalkResult,
|
|
options: std.json.StringifyOptions,
|
|
w: anytype,
|
|
) !void {
|
|
switch (self) {
|
|
.void, .@"unreachable" => {
|
|
try w.print(
|
|
\\{{ "{s}":{{}} }}
|
|
, .{@tagName(self)});
|
|
},
|
|
.type, .declRef => |v| {
|
|
try w.print(
|
|
\\{{ "{s}":{} }}
|
|
, .{ @tagName(self), v });
|
|
},
|
|
.int => |v| {
|
|
const neg = if (v.negated) "-" else "";
|
|
try w.print(
|
|
\\{{ "int": {{ "typeRef":
|
|
, .{});
|
|
try v.typeRef.jsonStringify(options, w);
|
|
try w.print(
|
|
\\, "value": {s}{} }} }}
|
|
, .{ neg, v.value });
|
|
},
|
|
.float => |v| {
|
|
const neg = if (v.negated) "-" else "";
|
|
try w.print(
|
|
\\{{ "float": {{ "typeRef":
|
|
, .{});
|
|
try v.typeRef.jsonStringify(options, w);
|
|
try w.print(
|
|
\\, "value": {s}{} }} }}
|
|
, .{ neg, v.value });
|
|
},
|
|
.bool => |v| {
|
|
try w.print(
|
|
\\{{ "bool":{} }}
|
|
, .{v});
|
|
},
|
|
.@"undefined" => |v| try std.json.stringify(v, options, w),
|
|
.@"null" => |v| try std.json.stringify(v, options, w),
|
|
.@"struct" => |v| try std.json.stringify(v, options, w),
|
|
// .decl_ref => |v| {
|
|
// try w.print(
|
|
// \\{{ "{s}":"{s}" }}
|
|
// , .{ @tagName(self), v });
|
|
// },
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
fn walkInstruction(
|
|
self: *Autodoc,
|
|
file: *File,
|
|
parent_scope: *Scope,
|
|
inst_index: usize,
|
|
) error{OutOfMemory}!DocData.WalkResult {
|
|
const tags = file.zir.instructions.items(.tag);
|
|
const data = file.zir.instructions.items(.data);
|
|
|
|
// We assume that the topmost ast_node entry corresponds to our decl
|
|
const self_ast_node_index = self.ast_nodes.items.len - 1;
|
|
|
|
switch (tags[inst_index]) {
|
|
else => {
|
|
std.debug.panic(
|
|
"TODO: implement `walkInstruction` for {s}\n\n",
|
|
.{@tagName(tags[inst_index])},
|
|
);
|
|
},
|
|
.import => {
|
|
const str_tok = data[inst_index].str_tok;
|
|
const path = str_tok.get(file.zir);
|
|
// importFile cannot error out since all files
|
|
// are already loaded at this point
|
|
const new_file = self.module.importFile(file, path) catch unreachable;
|
|
// TODO: cycles not handled, add file info to outuput
|
|
var new_scope = Scope{ .parent = null };
|
|
return self.walkInstruction(new_file.file, &new_scope, Zir.main_struct_inst);
|
|
},
|
|
.int => {
|
|
const int = data[inst_index].int;
|
|
return DocData.WalkResult{
|
|
.int = .{
|
|
.typeRef = .{
|
|
.type = @enumToInt(Ref.comptime_int_type),
|
|
},
|
|
.value = int,
|
|
},
|
|
};
|
|
},
|
|
.float => {
|
|
const float = data[inst_index].float;
|
|
return DocData.WalkResult{
|
|
.float = .{
|
|
.typeRef = .{
|
|
.type = @enumToInt(Ref.comptime_float_type),
|
|
},
|
|
.value = float,
|
|
},
|
|
};
|
|
},
|
|
.negate => {
|
|
const un_node = data[inst_index].un_node;
|
|
var operand: DocData.WalkResult = try self.walkRef(
|
|
file,
|
|
parent_scope,
|
|
un_node.operand,
|
|
);
|
|
operand.int.negated = true; // only support ints for now
|
|
return operand;
|
|
},
|
|
.as_node => {
|
|
const pl_node = data[inst_index].pl_node;
|
|
const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
|
|
const dest_type_walk = try self.walkRef(file, parent_scope, extra.data.dest_type);
|
|
const dest_type_ref = walkResultToTypeRef(dest_type_walk);
|
|
|
|
var operand = try self.walkRef(file, parent_scope, extra.data.operand);
|
|
|
|
switch (operand) {
|
|
else => std.debug.panic(
|
|
"TODO: handle {s} in `walkInstruction.as_node`\n",
|
|
.{@tagName(operand)},
|
|
),
|
|
.declRef => {},
|
|
// we don't do anything because up until now,
|
|
// I've only seen this used as such:
|
|
// @as(@as(type, Baz), .{})
|
|
// and we don't want to toss away the
|
|
// decl_val information (eg by replacing it with
|
|
// a WalkResult.type).
|
|
|
|
.int => operand.int.typeRef = dest_type_ref,
|
|
.@"struct" => operand.@"struct".typeRef = dest_type_ref,
|
|
.@"undefined" => operand.@"undefined" = dest_type_ref,
|
|
}
|
|
|
|
return operand;
|
|
},
|
|
.decl_val => {
|
|
const str_tok = data[inst_index].str_tok;
|
|
const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
|
|
return DocData.WalkResult{ .declRef = decls_slot_index };
|
|
},
|
|
.int_type => {
|
|
const int_type = data[inst_index].int_type;
|
|
const sign = if (int_type.signedness == .unsigned) "u" else "i";
|
|
const bits = int_type.bit_count;
|
|
const name = try std.fmt.allocPrint(self.arena, "{s}{}", .{ sign, bits });
|
|
|
|
try self.types.append(self.arena, .{
|
|
.Int = .{ .name = name },
|
|
});
|
|
return DocData.WalkResult{ .type = self.types.items.len - 1 };
|
|
},
|
|
//.block => {
|
|
//const pl_node = data[inst_index].pl_node;
|
|
//const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index);
|
|
//const last_instr_index = file.zir.extra[extra.end..][extra.data.body_len - 1];
|
|
//const break_operand = data[break_index].@"break".operand;
|
|
//return self.walkRef(file, parent_scope, break_operand);
|
|
//},
|
|
.block_inline => {
|
|
const pl_node = data[inst_index].pl_node;
|
|
const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index);
|
|
const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1];
|
|
const break_operand = data[break_index].@"break".operand;
|
|
return self.walkRef(file, parent_scope, break_operand);
|
|
},
|
|
.func => {
|
|
const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index));
|
|
|
|
// TODO: change this to a resize and change the appends accordingly
|
|
try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len);
|
|
try self.types.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,
|
|
);
|
|
for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
|
|
if (tags[param_index] != .param) unreachable; // TODO: handle more param types
|
|
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
|
|
"";
|
|
|
|
param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len);
|
|
try self.ast_nodes.append(self.arena, .{
|
|
.name = file.zir.nullTerminatedString(extra.data.name),
|
|
.docs = doc_comment,
|
|
});
|
|
|
|
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(
|
|
DocData.TypeRef.fromWalkResult(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 DocData.TypeRef.fromWalkResult(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 => {
|
|
const extended = data[inst_index].extended;
|
|
switch (extended.opcode) {
|
|
else => {
|
|
std.debug.panic(
|
|
"TODO: implement `walkinstruction.extended` for {s}\n\n",
|
|
.{@tagName(extended.opcode)},
|
|
);
|
|
},
|
|
.union_decl => {
|
|
var scope: Scope = .{ .parent = parent_scope };
|
|
|
|
const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
|
|
var extra_index: usize = extended.operand;
|
|
|
|
const src_node: ?i32 = if (small.has_src_node) blk: {
|
|
const src_node = @bitCast(i32, file.zir.extra[extra_index]);
|
|
extra_index += 1;
|
|
break :blk src_node;
|
|
} else null;
|
|
_ = src_node;
|
|
|
|
const tag_type: ?Ref = if (small.has_tag_type) blk: {
|
|
const tag_type = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk @intToEnum(Ref, tag_type);
|
|
} else null;
|
|
_ = tag_type;
|
|
|
|
const body_len = if (small.has_body_len) blk: {
|
|
const body_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk body_len;
|
|
} else 0;
|
|
|
|
const fields_len = if (small.has_fields_len) blk: {
|
|
const fields_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk fields_len;
|
|
} else 0;
|
|
_ = fields_len;
|
|
|
|
const decls_len = if (small.has_decls_len) blk: {
|
|
const decls_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk decls_len;
|
|
} else 0;
|
|
|
|
var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
|
|
const decls_first_index = self.decls.items.len;
|
|
// Decl name lookahead for reserving slots in `scope` (and `decls`).
|
|
// Done to make sure that all decl refs can be resolved correctly,
|
|
// even if we haven't fully analyzed the decl yet.
|
|
{
|
|
var it = file.zir.declIterator(@intCast(u32, inst_index));
|
|
try self.decls.resize(self.arena, decls_first_index + it.decls_len);
|
|
var decls_slot_index = decls_first_index;
|
|
while (it.next()) |d| : (decls_slot_index += 1) {
|
|
const decl_name_index = file.zir.extra[d.sub_index + 5];
|
|
try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index);
|
|
}
|
|
}
|
|
|
|
extra_index = try self.walkDecls(
|
|
file,
|
|
&scope,
|
|
decls_first_index,
|
|
decls_len,
|
|
&decl_indexes,
|
|
&priv_decl_indexes,
|
|
extra_index,
|
|
);
|
|
|
|
// const body = file.zir.extra[extra_index..][0..body_len];
|
|
extra_index += body_len;
|
|
|
|
var field_type_refs = try std.ArrayListUnmanaged(DocData.TypeRef).initCapacity(
|
|
self.arena,
|
|
fields_len,
|
|
);
|
|
var field_name_indexes = try std.ArrayListUnmanaged(usize).initCapacity(
|
|
self.arena,
|
|
fields_len,
|
|
);
|
|
try self.collectUnionFieldInfo(
|
|
file,
|
|
&scope,
|
|
fields_len,
|
|
&field_type_refs,
|
|
&field_name_indexes,
|
|
extra_index,
|
|
);
|
|
|
|
self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items;
|
|
|
|
try self.types.append(self.arena, .{
|
|
.Union = .{
|
|
.name = "todo_name",
|
|
.src = self_ast_node_index,
|
|
.privDecls = priv_decl_indexes.items,
|
|
.pubDecls = decl_indexes.items,
|
|
.fields = field_type_refs.items,
|
|
},
|
|
});
|
|
|
|
return DocData.WalkResult{ .type = self.types.items.len - 1 };
|
|
},
|
|
.enum_decl => {
|
|
var scope: Scope = .{ .parent = parent_scope };
|
|
|
|
const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small);
|
|
var extra_index: usize = extended.operand;
|
|
|
|
const src_node: ?i32 = if (small.has_src_node) blk: {
|
|
const src_node = @bitCast(i32, file.zir.extra[extra_index]);
|
|
extra_index += 1;
|
|
break :blk src_node;
|
|
} else null;
|
|
_ = src_node;
|
|
|
|
const tag_type: ?Ref = if (small.has_tag_type) blk: {
|
|
const tag_type = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk @intToEnum(Ref, tag_type);
|
|
} else null;
|
|
_ = tag_type;
|
|
|
|
const body_len = if (small.has_body_len) blk: {
|
|
const body_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk body_len;
|
|
} else 0;
|
|
|
|
const fields_len = if (small.has_fields_len) blk: {
|
|
const fields_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk fields_len;
|
|
} else 0;
|
|
_ = fields_len;
|
|
|
|
const decls_len = if (small.has_decls_len) blk: {
|
|
const decls_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk decls_len;
|
|
} else 0;
|
|
|
|
var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
|
|
const decls_first_index = self.decls.items.len;
|
|
// Decl name lookahead for reserving slots in `scope` (and `decls`).
|
|
// Done to make sure that all decl refs can be resolved correctly,
|
|
// even if we haven't fully analyzed the decl yet.
|
|
{
|
|
var it = file.zir.declIterator(@intCast(u32, inst_index));
|
|
try self.decls.resize(self.arena, decls_first_index + it.decls_len);
|
|
var decls_slot_index = decls_first_index;
|
|
while (it.next()) |d| : (decls_slot_index += 1) {
|
|
const decl_name_index = file.zir.extra[d.sub_index + 5];
|
|
try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index);
|
|
}
|
|
}
|
|
|
|
extra_index = try self.walkDecls(
|
|
file,
|
|
&scope,
|
|
decls_first_index,
|
|
decls_len,
|
|
&decl_indexes,
|
|
&priv_decl_indexes,
|
|
extra_index,
|
|
);
|
|
|
|
// const body = file.zir.extra[extra_index..][0..body_len];
|
|
extra_index += body_len;
|
|
|
|
var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
{
|
|
var bit_bag_idx = extra_index;
|
|
var cur_bit_bag: u32 = undefined;
|
|
extra_index += std.math.divCeil(usize, fields_len, 32) catch unreachable;
|
|
|
|
var idx: usize = 0;
|
|
while (idx < fields_len) : (idx += 1) {
|
|
if (idx % 32 == 0) {
|
|
cur_bit_bag = file.zir.extra[bit_bag_idx];
|
|
bit_bag_idx += 1;
|
|
}
|
|
|
|
const has_value = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
|
|
const field_name_index = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
|
|
const doc_comment_index = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
|
|
const value_ref: ?Ref = if (has_value) blk: {
|
|
const value_ref = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk @intToEnum(Ref, value_ref);
|
|
} else null;
|
|
_ = value_ref;
|
|
|
|
const field_name = file.zir.nullTerminatedString(field_name_index);
|
|
|
|
try field_name_indexes.append(self.arena, self.ast_nodes.items.len);
|
|
const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
|
|
file.zir.nullTerminatedString(doc_comment_index)
|
|
else
|
|
null;
|
|
try self.ast_nodes.append(self.arena, .{
|
|
.name = field_name,
|
|
.docs = doc_comment,
|
|
});
|
|
}
|
|
}
|
|
|
|
self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items;
|
|
|
|
try self.types.append(self.arena, .{
|
|
.Enum = .{
|
|
.name = "todo_name",
|
|
.src = self_ast_node_index,
|
|
.privDecls = priv_decl_indexes.items,
|
|
.pubDecls = decl_indexes.items,
|
|
},
|
|
});
|
|
|
|
return DocData.WalkResult{ .type = self.types.items.len - 1 };
|
|
},
|
|
.struct_decl => {
|
|
var scope: Scope = .{ .parent = parent_scope };
|
|
|
|
const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
|
|
var extra_index: usize = extended.operand;
|
|
|
|
const src_node: ?i32 = if (small.has_src_node) blk: {
|
|
const src_node = @bitCast(i32, file.zir.extra[extra_index]);
|
|
extra_index += 1;
|
|
break :blk src_node;
|
|
} else null;
|
|
_ = src_node;
|
|
|
|
const body_len = if (small.has_body_len) blk: {
|
|
const body_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk body_len;
|
|
} else 0;
|
|
|
|
const fields_len = if (small.has_fields_len) blk: {
|
|
const fields_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk fields_len;
|
|
} else 0;
|
|
_ = fields_len;
|
|
|
|
const decls_len = if (small.has_decls_len) blk: {
|
|
const decls_len = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
break :blk decls_len;
|
|
} else 0;
|
|
|
|
var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
|
|
const decls_first_index = self.decls.items.len;
|
|
// Decl name lookahead for reserving slots in `scope` (and `decls`).
|
|
// Done to make sure that all decl refs can be resolved correctly,
|
|
// even if we haven't fully analyzed the decl yet.
|
|
{
|
|
var it = file.zir.declIterator(@intCast(u32, inst_index));
|
|
try self.decls.resize(self.arena, decls_first_index + it.decls_len);
|
|
var decls_slot_index = decls_first_index;
|
|
while (it.next()) |d| : (decls_slot_index += 1) {
|
|
const decl_name_index = file.zir.extra[d.sub_index + 5];
|
|
try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index);
|
|
}
|
|
}
|
|
|
|
extra_index = try self.walkDecls(
|
|
file,
|
|
&scope,
|
|
decls_first_index,
|
|
decls_len,
|
|
&decl_indexes,
|
|
&priv_decl_indexes,
|
|
extra_index,
|
|
);
|
|
|
|
// const body = file.zir.extra[extra_index..][0..body_len];
|
|
extra_index += body_len;
|
|
|
|
var field_type_refs: std.ArrayListUnmanaged(DocData.TypeRef) = .{};
|
|
var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
|
|
try self.collectStructFieldInfo(
|
|
file,
|
|
&scope,
|
|
fields_len,
|
|
&field_type_refs,
|
|
&field_name_indexes,
|
|
extra_index,
|
|
);
|
|
|
|
self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items;
|
|
|
|
try self.types.append(self.arena, .{
|
|
.Struct = .{
|
|
.name = "todo_name",
|
|
.src = self_ast_node_index,
|
|
.privDecls = priv_decl_indexes.items,
|
|
.pubDecls = decl_indexes.items,
|
|
.fields = field_type_refs.items,
|
|
},
|
|
});
|
|
|
|
return DocData.WalkResult{ .type = self.types.items.len - 1 };
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Called by `walkInstruction` when encountering a container type,
|
|
/// iterates over all decl definitions in its body.
|
|
/// It also analyzes each decl's body recursively.
|
|
///
|
|
/// Does not append to `self.decls` directly because `walkInstruction`
|
|
/// is expected to (look-ahead) scan all decls and reserve `body_len`
|
|
/// slots in `self.decls`, which are then filled out by `walkDecls`.
|
|
fn walkDecls(
|
|
self: *Autodoc,
|
|
file: *File,
|
|
scope: *Scope,
|
|
decls_first_index: usize,
|
|
decls_len: u32,
|
|
decl_indexes: *std.ArrayListUnmanaged(usize),
|
|
priv_decl_indexes: *std.ArrayListUnmanaged(usize),
|
|
extra_start: usize,
|
|
) error{OutOfMemory}!usize {
|
|
const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
|
|
var extra_index = extra_start + bit_bags_count;
|
|
var bit_bag_index: usize = extra_start;
|
|
var cur_bit_bag: u32 = undefined;
|
|
var decl_i: u32 = 0;
|
|
|
|
while (decl_i < decls_len) : (decl_i += 1) {
|
|
const decls_slot_index = decls_first_index + decl_i;
|
|
|
|
if (decl_i % 8 == 0) {
|
|
cur_bit_bag = file.zir.extra[bit_bag_index];
|
|
bit_bag_index += 1;
|
|
}
|
|
const is_pub = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
const is_exported = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
// const has_align = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
// const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
|
|
// const sub_index = extra_index;
|
|
|
|
// const hash_u32s = file.zir.extra[extra_index..][0..4];
|
|
extra_index += 4;
|
|
// const line = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
const decl_name_index = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
const decl_index = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
const doc_comment_index = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
|
|
// const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
|
|
// const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
|
|
// extra_index += 1;
|
|
// break :inst inst;
|
|
// };
|
|
// const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
|
|
// const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
|
|
// extra_index += 1;
|
|
// break :inst inst;
|
|
// };
|
|
// const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
|
|
// const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
|
|
// extra_index += 1;
|
|
// break :inst inst;
|
|
// };
|
|
|
|
// const pub_str = if (is_pub) "pub " else "";
|
|
// const hash_bytes = @bitCast([16]u8, hash_u32s.*);
|
|
|
|
const name: []const u8 = blk: {
|
|
if (decl_name_index == 0) {
|
|
break :blk if (is_exported) "usingnamespace" else "comptime";
|
|
} else if (decl_name_index == 1) {
|
|
break :blk "test";
|
|
} else {
|
|
const raw_decl_name = file.zir.nullTerminatedString(decl_name_index);
|
|
if (raw_decl_name.len == 0) {
|
|
break :blk file.zir.nullTerminatedString(decl_name_index + 1);
|
|
} else {
|
|
break :blk raw_decl_name;
|
|
}
|
|
}
|
|
};
|
|
|
|
const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
|
|
file.zir.nullTerminatedString(doc_comment_index)
|
|
else
|
|
null;
|
|
|
|
// astnode
|
|
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 = doc_comment,
|
|
.fields = null, // walkInstruction will fill `fields` if necessary
|
|
});
|
|
break :idx idx;
|
|
};
|
|
|
|
const walk_result = try self.walkInstruction(file, scope, decl_index);
|
|
|
|
if (is_pub) {
|
|
try decl_indexes.append(self.arena, decls_slot_index);
|
|
} else {
|
|
try priv_decl_indexes.append(self.arena, decls_slot_index);
|
|
}
|
|
|
|
// // decl.typeRef == decl.val...typeRef
|
|
// const decl_type_ref: DocData.TypeRef = switch (walk_result) {
|
|
// .int => |i| i.typeRef,
|
|
// .void => .{ .type = @enumToInt(Ref.void_type) },
|
|
// .@"undefined", .@"null" => |v| v,
|
|
// .@"unreachable" => .{ .type = @enumToInt(Ref.noreturn_type) },
|
|
// .@"struct" => |s| s.typeRef,
|
|
// .bool => .{ .type = @enumToInt(Ref.bool_type) },
|
|
// .type => .{ .type = @enumToInt(Ref.type_type) },
|
|
// // this last case is special becauese it's not pointing
|
|
// // at the type of the value, but rather at the value itself
|
|
// // the js better be aware ot this!
|
|
// .declRef => |d| .{ .declRef = d },
|
|
// };
|
|
|
|
self.decls.items[decls_slot_index] = .{
|
|
.name = name,
|
|
.src = ast_node_index,
|
|
// .typeRef = decl_type_ref,
|
|
.value = walk_result,
|
|
.kind = "const", // find where this information can be found
|
|
};
|
|
}
|
|
|
|
return extra_index;
|
|
}
|
|
|
|
fn collectUnionFieldInfo(
|
|
self: *Autodoc,
|
|
file: *File,
|
|
scope: *Scope,
|
|
fields_len: usize,
|
|
field_type_refs: *std.ArrayListUnmanaged(DocData.TypeRef),
|
|
field_name_indexes: *std.ArrayListUnmanaged(usize),
|
|
ei: usize,
|
|
) !void {
|
|
if (fields_len == 0) return;
|
|
var extra_index = ei;
|
|
|
|
const bits_per_field = 4;
|
|
const fields_per_u32 = 32 / bits_per_field;
|
|
const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
|
|
var bit_bag_index: usize = extra_index;
|
|
extra_index += bit_bags_count;
|
|
|
|
var cur_bit_bag: u32 = undefined;
|
|
var field_i: u32 = 0;
|
|
while (field_i < fields_len) : (field_i += 1) {
|
|
if (field_i % fields_per_u32 == 0) {
|
|
cur_bit_bag = file.zir.extra[bit_bag_index];
|
|
bit_bag_index += 1;
|
|
}
|
|
const has_type = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
const has_align = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
const has_tag = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
const unused = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
_ = unused;
|
|
|
|
const field_name = file.zir.nullTerminatedString(file.zir.extra[extra_index]);
|
|
extra_index += 1;
|
|
const doc_comment_index = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
const field_type = if (has_type)
|
|
@intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index])
|
|
else
|
|
.void_type;
|
|
extra_index += 1;
|
|
|
|
if (has_align) extra_index += 1;
|
|
if (has_tag) extra_index += 1;
|
|
|
|
// type
|
|
{
|
|
const walk_result = try self.walkRef(file, scope, field_type);
|
|
try field_type_refs.append(
|
|
self.arena,
|
|
walkResultToTypeRef(walk_result),
|
|
);
|
|
}
|
|
|
|
// ast node
|
|
{
|
|
try field_name_indexes.append(self.arena, self.ast_nodes.items.len);
|
|
const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
|
|
file.zir.nullTerminatedString(doc_comment_index)
|
|
else
|
|
null;
|
|
try self.ast_nodes.append(self.arena, .{
|
|
.name = field_name,
|
|
.docs = doc_comment,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
fn collectStructFieldInfo(
|
|
self: *Autodoc,
|
|
file: *File,
|
|
scope: *Scope,
|
|
fields_len: usize,
|
|
field_type_refs: *std.ArrayListUnmanaged(DocData.TypeRef),
|
|
field_name_indexes: *std.ArrayListUnmanaged(usize),
|
|
ei: usize,
|
|
) !void {
|
|
if (fields_len == 0) return;
|
|
var extra_index = ei;
|
|
|
|
const bits_per_field = 4;
|
|
const fields_per_u32 = 32 / bits_per_field;
|
|
const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
|
|
var bit_bag_index: usize = extra_index;
|
|
extra_index += bit_bags_count;
|
|
|
|
var cur_bit_bag: u32 = undefined;
|
|
var field_i: u32 = 0;
|
|
while (field_i < fields_len) : (field_i += 1) {
|
|
if (field_i % fields_per_u32 == 0) {
|
|
cur_bit_bag = file.zir.extra[bit_bag_index];
|
|
bit_bag_index += 1;
|
|
}
|
|
const has_align = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
const has_default = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
// const is_comptime = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
const unused = @truncate(u1, cur_bit_bag) != 0;
|
|
cur_bit_bag >>= 1;
|
|
_ = unused;
|
|
|
|
const field_name = file.zir.nullTerminatedString(file.zir.extra[extra_index]);
|
|
extra_index += 1;
|
|
const field_type = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
|
|
extra_index += 1;
|
|
const doc_comment_index = file.zir.extra[extra_index];
|
|
extra_index += 1;
|
|
|
|
if (has_align) extra_index += 1;
|
|
if (has_default) extra_index += 1;
|
|
|
|
// type
|
|
{
|
|
const walk_result = try self.walkRef(file, scope, field_type);
|
|
try field_type_refs.append(
|
|
self.arena,
|
|
walkResultToTypeRef(walk_result),
|
|
);
|
|
}
|
|
|
|
// ast node
|
|
{
|
|
try field_name_indexes.append(self.arena, self.ast_nodes.items.len);
|
|
const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
|
|
file.zir.nullTerminatedString(doc_comment_index)
|
|
else
|
|
null;
|
|
try self.ast_nodes.append(self.arena, .{
|
|
.name = field_name,
|
|
.docs = doc_comment,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
fn walkRef(
|
|
self: *Autodoc,
|
|
file: *File,
|
|
parent_scope: *Scope,
|
|
ref: Ref,
|
|
) !DocData.WalkResult {
|
|
const enum_value = @enumToInt(ref);
|
|
if (enum_value <= @enumToInt(Ref.anyerror_void_error_union_type)) {
|
|
// We can just return a type that indexes into `types` with the
|
|
// enum value because in the beginning we pre-filled `types` with
|
|
// the types that are listed in `Ref`.
|
|
return DocData.WalkResult{ .type = enum_value };
|
|
} else if (enum_value < Ref.typed_value_map.len) {
|
|
switch (ref) {
|
|
else => {
|
|
std.debug.panic("TODO: handle {s} in `walkRef`\n", .{
|
|
@tagName(ref),
|
|
});
|
|
},
|
|
.undef => {
|
|
return DocData.WalkResult{ .@"undefined" = .unspecified };
|
|
},
|
|
.zero => {
|
|
return DocData.WalkResult{ .int = .{
|
|
.typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
|
|
.value = 0,
|
|
} };
|
|
},
|
|
.one => {
|
|
return DocData.WalkResult{ .int = .{
|
|
.typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
|
|
.value = 1,
|
|
} };
|
|
},
|
|
|
|
.void_value => {
|
|
return DocData.WalkResult{ .void = {} };
|
|
},
|
|
.unreachable_value => {
|
|
return DocData.WalkResult{ .@"unreachable" = {} };
|
|
},
|
|
.null_value => {
|
|
return DocData.WalkResult{ .@"null" = .unspecified };
|
|
},
|
|
.bool_true => {
|
|
return DocData.WalkResult{ .bool = true };
|
|
},
|
|
.bool_false => {
|
|
return DocData.WalkResult{ .bool = false };
|
|
},
|
|
.empty_struct => {
|
|
return DocData.WalkResult{ .@"struct" = .{
|
|
.typeRef = .unspecified,
|
|
.fieldVals = &.{},
|
|
} };
|
|
},
|
|
.zero_usize => {
|
|
return DocData.WalkResult{ .int = .{
|
|
.typeRef = .{ .type = @enumToInt(Ref.usize_type) },
|
|
.value = 0,
|
|
} };
|
|
},
|
|
.one_usize => {
|
|
return DocData.WalkResult{ .int = .{
|
|
.typeRef = .{ .type = @enumToInt(Ref.usize_type) },
|
|
.value = 1,
|
|
} };
|
|
},
|
|
// TODO: dunno what to do with those
|
|
// .calling_convention_c => {
|
|
// return DocData.WalkResult{ .int = .{
|
|
// .type = @enumToInt(Ref.comptime_int_type),
|
|
// .value = 1,
|
|
// } };
|
|
// },
|
|
// .calling_convention_inline => {
|
|
// return DocData.WalkResult{ .int = .{
|
|
// .type = @enumToInt(Ref.comptime_int_type),
|
|
// .value = 1,
|
|
// } };
|
|
// },
|
|
// .generic_poison => {
|
|
// return DocData.WalkResult{ .int = .{
|
|
// .type = @enumToInt(Ref.comptime_int_type),
|
|
// .value = 1,
|
|
// } };
|
|
// },
|
|
}
|
|
} else {
|
|
const zir_index = enum_value - Ref.typed_value_map.len;
|
|
return self.walkInstruction(file, parent_scope, zir_index);
|
|
}
|
|
}
|
|
|
|
fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
|
|
return switch (wr) {
|
|
else => std.debug.panic(
|
|
"TODO: handle `{s}` in `walkResultToTypeRef.as_node.dest_type`\n",
|
|
.{@tagName(wr)},
|
|
),
|
|
|
|
.declRef => |v| .{ .declRef = v },
|
|
.type => |v| .{ .type = v },
|
|
};
|
|
}
|
|
|
|
//fn collectParamInfo(self: *Autodoc, file: *File, scope: *Scope, inst_idx: Zir.Index) void {
|
|
|
|
//}
|