mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
autodocs: added support for some non-type values
This commit is contained in:
parent
29771440b2
commit
ce40f34cbc
@ -177,8 +177,8 @@
|
||||
return renderUnknownDecl(lastDeclOrType);
|
||||
} else if (lastDeclOrType.kind === 'var') {
|
||||
return renderVar(lastDeclOrType);
|
||||
} else if (lastDeclOrType.kind === 'const' && lastDeclOrType.type != null) {
|
||||
var typeObj = zigAnalysis.types[lastDeclOrType.type];
|
||||
} else if (lastDeclOrType.kind === 'const' && "value" in lastDeclOrType && !("type" in lastDeclOrType.value)) {
|
||||
var typeObj = zigAnalysis.types[getDeclValTypeId(lastDeclOrType)];
|
||||
if (typeObj.kind === typeKinds.Fn) {
|
||||
return renderFn(lastDeclOrType);
|
||||
} else {
|
||||
@ -960,6 +960,7 @@
|
||||
|
||||
function renderValue(decl) {
|
||||
|
||||
var declTypeId = getDeclValTypeId(decl);
|
||||
var declValueText = "";
|
||||
switch(Object.keys(decl.value)[0]) {
|
||||
case "int":
|
||||
@ -971,7 +972,7 @@
|
||||
}
|
||||
|
||||
domFnProtoCode.innerHTML = '<span class="tok-kw">const</span> ' +
|
||||
escapeHtml(decl.name) + ': ' + typeIndexName(decl.type, true, true) +
|
||||
escapeHtml(decl.name) + ': ' + typeIndexName(declTypeId, true, true) +
|
||||
" = " + declValueText;
|
||||
|
||||
var docs = zigAnalysis.astNodes[decl.src].docs;
|
||||
@ -984,8 +985,9 @@
|
||||
}
|
||||
|
||||
function renderVar(decl) {
|
||||
var declTypeId = getDeclValTypeId(decl);
|
||||
domFnProtoCode.innerHTML = '<span class="tok-kw">var</span> ' +
|
||||
escapeHtml(decl.name) + ': ' + typeIndexName(decl.type, true, true);
|
||||
escapeHtml(decl.name) + ': ' + typeIndexName(declTypeId, true, true);
|
||||
|
||||
var docs = zigAnalysis.astNodes[decl.src].docs;
|
||||
if (docs != null) {
|
||||
@ -1011,10 +1013,8 @@
|
||||
if (decl.kind === 'var') {
|
||||
varsList.push(decl);
|
||||
continue;
|
||||
} else if (decl.kind === 'const' && decl.type != null) {
|
||||
if (decl.type === typeTypeId) {
|
||||
// todo: this actually makes sense for decl_vals too
|
||||
// the problem is, how should we get to the final type though?
|
||||
} else if (decl.kind === 'const' && "value" in decl) {
|
||||
if (declValTypeId === typeTypeId) {
|
||||
if (typeIsErrSet(declValTypeId)) {
|
||||
errSetsList.push(decl);
|
||||
} else if (typeIsStructWithNoFields(declValTypeId)) {
|
||||
@ -1023,8 +1023,9 @@
|
||||
typesList.push(decl);
|
||||
}
|
||||
} else {
|
||||
var typeKind = zigAnalysis.types[decl.type].kind;
|
||||
var typeKind = zigAnalysis.types[declValTypeId].kind;
|
||||
if (typeKind === typeKinds.Fn) {
|
||||
// TODO: this is broken but I don't understand functions yet
|
||||
if (allCompTimeFnCallsHaveTypeResult(decl.type, declValTypeId)) {
|
||||
typesList.push(decl);
|
||||
} else {
|
||||
@ -1125,8 +1126,8 @@
|
||||
if (typeof(field) === 'object') {
|
||||
if (field.failure === true) {
|
||||
html += '<span class="tok-kw" style="color:red;">#FAILURE#</span>';
|
||||
} else if ("decl_ref" in field) {
|
||||
var decl = zigAnalysis.decls[field.decl_ref];
|
||||
} else if ("declRef" in field) {
|
||||
var decl = zigAnalysis.decls[field.declRef];
|
||||
var valType = zigAnalysis.types[getDeclValTypeId(decl)];
|
||||
var valTypeName = valType.name;
|
||||
if (valType.kind === typeKinds.Struct) {
|
||||
@ -1174,7 +1175,7 @@
|
||||
tdNameA.setAttribute('href', navLinkDecl(decl.name));
|
||||
tdNameA.textContent = decl.name;
|
||||
|
||||
tdType.innerHTML = typeIndexName(decl.type, true, true);
|
||||
tdType.innerHTML = typeIndexName(getDeclValTypeId(decl), true, true);
|
||||
|
||||
var docs = zigAnalysis.astNodes[decl.src].docs;
|
||||
if (docs != null) {
|
||||
@ -1201,7 +1202,7 @@
|
||||
tdNameA.setAttribute('href', navLinkDecl(decl.name));
|
||||
tdNameA.textContent = decl.name;
|
||||
|
||||
tdType.innerHTML = typeIndexName(decl.type, true, true);
|
||||
tdType.innerHTML = typeIndexName(getDeclValTypeId(decl), true, true);
|
||||
|
||||
var docs = zigAnalysis.astNodes[decl.src].docs;
|
||||
if (docs != null) {
|
||||
@ -1359,12 +1360,26 @@
|
||||
return typeKind === typeKinds.ErrorSet || typeKindIsContainer(typeKind);
|
||||
}
|
||||
|
||||
// Handles both WalkResult and TypeRef
|
||||
function getDeclValTypeId(decl) {
|
||||
while ( "decl_ref" in decl.value) {
|
||||
decl = zigAnalysis.decls[decl.value.decl_ref];
|
||||
var val = decl.value;
|
||||
while (true) {
|
||||
if ( "declRef" in val) {
|
||||
val = zigAnalysis.decls[val.declRef].value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("int" in val) {
|
||||
val = val.int.typeRef;
|
||||
}
|
||||
|
||||
if ("type" in val) {
|
||||
return val.type;
|
||||
}
|
||||
|
||||
console.assert("type" in val);
|
||||
}
|
||||
console.assert("type" in decl.value);
|
||||
return decl.value.type;
|
||||
return val.type;
|
||||
}
|
||||
|
||||
function computeCanonDeclPaths() {
|
||||
|
||||
245
src/Autodoc.zig
245
src/Autodoc.zig
@ -210,7 +210,7 @@ const DocData = struct {
|
||||
name: []const u8,
|
||||
kind: []const u8, // TODO: where do we find this info?
|
||||
src: usize, // index into astNodes
|
||||
type: usize, // index into types
|
||||
// typeRef: TypeRef,
|
||||
value: WalkResult,
|
||||
};
|
||||
|
||||
@ -231,28 +231,65 @@ const DocData = struct {
|
||||
pubDecls: ?[]usize = null, // index into decls
|
||||
fields: ?[]WalkResult = null, // (use src->fields to find names)
|
||||
};
|
||||
|
||||
const WalkResult = union(enum) {
|
||||
failure: bool,
|
||||
const TypeRef = union(enum) {
|
||||
unspecified,
|
||||
declRef: usize, // index in `decls`
|
||||
type: usize, // index in `types`
|
||||
decl_ref: usize, // index in `decls`
|
||||
int: struct {
|
||||
type: usize, // index in `types`
|
||||
value: usize, // direct value
|
||||
negated: bool = false,
|
||||
},
|
||||
pub fn jsonStringify(
|
||||
self: WalkResult,
|
||||
self: TypeRef,
|
||||
_: std.json.StringifyOptions,
|
||||
w: anytype,
|
||||
) !void {
|
||||
switch (self) {
|
||||
.failure => |v| {
|
||||
.unspecified => {
|
||||
try w.print(
|
||||
\\{{ "failure":{} }}
|
||||
, .{v});
|
||||
\\{{ "unspecified":{{}} }}
|
||||
, .{});
|
||||
},
|
||||
.type, .decl_ref => |v| {
|
||||
.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,
|
||||
},
|
||||
|
||||
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 });
|
||||
@ -260,10 +297,21 @@ const DocData = struct {
|
||||
.int => |v| {
|
||||
const neg = if (v.negated) "-" else "";
|
||||
try w.print(
|
||||
\\{{ "int": {{ "type": {}, "value": {s}{} }} }}
|
||||
, .{ v.type, neg, v.value });
|
||||
\\{{ "int": {{ "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}" }}
|
||||
@ -288,17 +336,16 @@ fn walkInstruction(
|
||||
|
||||
switch (tags[inst_index]) {
|
||||
else => {
|
||||
std.debug.print(
|
||||
std.debug.panic(
|
||||
"TODO: implement `walkInstruction` for {s}\n\n",
|
||||
.{@tagName(tags[inst_index])},
|
||||
);
|
||||
return DocData.WalkResult{ .failure = true };
|
||||
},
|
||||
.int => {
|
||||
const int = data[inst_index].int;
|
||||
return DocData.WalkResult{
|
||||
.int = .{
|
||||
.type = @enumToInt(Ref.comptime_int_type),
|
||||
.typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
|
||||
.value = int,
|
||||
},
|
||||
};
|
||||
@ -313,16 +360,34 @@ fn walkInstruction(
|
||||
const pl_node = data[inst_index].pl_node;
|
||||
const extra = zir.extraData(Zir.Inst.As, pl_node.payload_index);
|
||||
const dest_type_walk = try self.walkRef(zir, parent_scope, extra.data.dest_type);
|
||||
const dest_type = dest_type_walk.type; // asserts that we got a type
|
||||
const dest_type_ref = walkResultToTypeRef(dest_type_walk);
|
||||
|
||||
var operand = try self.walkRef(zir, parent_scope, extra.data.operand);
|
||||
operand.int.type = dest_type; // only support ints for now
|
||||
|
||||
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{ .decl_ref = decls_slot_index };
|
||||
return DocData.WalkResult{ .declRef = decls_slot_index };
|
||||
},
|
||||
.int_type => {
|
||||
const int_type = data[inst_index].int_type;
|
||||
@ -348,23 +413,16 @@ fn walkInstruction(
|
||||
});
|
||||
|
||||
const break_operand = data[break_index].@"break".operand;
|
||||
return if (Zir.refToIndex(break_operand)) |bi|
|
||||
self.walkInstruction(zir, parent_scope, bi)
|
||||
else if (@enumToInt(break_operand) <= @enumToInt(Ref.anyerror_void_error_union_type))
|
||||
// we append all the types in ref first, so we can just do this if we encounter a ref that is a type
|
||||
return DocData.WalkResult{ .type = @enumToInt(break_operand) }
|
||||
else
|
||||
std.debug.todo("generate WalkResults for refs that are not types");
|
||||
return self.walkRef(zir, parent_scope, break_operand);
|
||||
},
|
||||
.extended => {
|
||||
const extended = data[inst_index].extended;
|
||||
switch (extended.opcode) {
|
||||
else => {
|
||||
std.debug.print(
|
||||
std.debug.panic(
|
||||
"TODO: implement `walkInstruction` (inside .extended case) for {s}\n\n",
|
||||
.{@tagName(extended.opcode)},
|
||||
);
|
||||
return DocData.WalkResult{ .failure = true };
|
||||
},
|
||||
.struct_decl => {
|
||||
var scope: Scope = .{ .parent = parent_scope };
|
||||
@ -457,6 +515,13 @@ fn walkInstruction(
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
zir: Zir,
|
||||
@ -562,15 +627,25 @@ fn walkDecls(
|
||||
try priv_decl_indexes.append(self.arena, decls_slot_index);
|
||||
}
|
||||
|
||||
const decl_type = switch (walk_result) {
|
||||
.int => |i| i.type,
|
||||
else => @enumToInt(Ref.type_type),
|
||||
};
|
||||
// // 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,
|
||||
.type = decl_type,
|
||||
// .typeRef = decl_type_ref,
|
||||
.value = walk_result,
|
||||
.kind = "const", // find where this information can be found
|
||||
};
|
||||
@ -649,11 +724,101 @@ fn walkRef(
|
||||
ref: Ref,
|
||||
) !DocData.WalkResult {
|
||||
const enum_value = @enumToInt(ref);
|
||||
if (enum_value < Zir.Inst.Ref.typed_value_map.len) {
|
||||
// TODO: well... Refs are not all types
|
||||
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(zir, parent_scope, zir_index);
|
||||
}
|
||||
}
|
||||
|
||||
fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
|
||||
return switch (wr) {
|
||||
else => std.debug.panic(
|
||||
"TODO: handle `{s}` in `walkInstruction.as_node.dest_type`\n",
|
||||
.{@tagName(wr)},
|
||||
),
|
||||
|
||||
.declRef => |v| .{ .declRef = v },
|
||||
.type => |v| .{ .type = v },
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user