diff options
| -rw-r--r-- | lib/docs/main.js | 49 | ||||
| -rw-r--r-- | src/Autodoc.zig | 233 |
2 files changed, 231 insertions, 51 deletions
diff --git a/lib/docs/main.js b/lib/docs/main.js index c41692d4e2..80ba13c537 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -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() { diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 437f3b479f..21aff1f36b 100644 --- a/src/Autodoc.zig +++ b/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 TypeRef = union(enum) { + unspecified, + declRef: usize, // index in `decls` + type: usize, // index in `types` + 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) { - failure: bool, + void, + @"unreachable", + @"null": TypeRef, + @"undefined": TypeRef, + @"struct": struct { + typeRef: TypeRef, + fieldVals: []struct { + name: []const u8, + val: WalkResult, + }, + }, + bool: bool, type: usize, // index in `types` - decl_ref: usize, // index in `decls` + declRef: usize, // index in `decls` int: struct { - type: usize, // index in `types` + typeRef: TypeRef, value: usize, // direct value negated: bool = false, }, + pub fn jsonStringify( self: WalkResult, - _: std.json.StringifyOptions, + options: std.json.StringifyOptions, w: anytype, ) !void { switch (self) { - .failure => |v| { + .void, + .@"unreachable", + => { try w.print( - \\{{ "failure":{} }} - , .{v}); + \\{{ "{s}":{{}} }} + , .{@tagName(self)}); }, - .type, .decl_ref => |v| { + .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 }, + }; +} |
