std.zig.Ast: improve type safety

This commits adds the following distinct integer types to std.zig.Ast:
- OptionalTokenIndex
- TokenOffset
- OptionalTokenOffset
- Node.OptionalIndex
- Node.Offset
- Node.OptionalOffset

The `Node.Index` type has also been converted to a distinct type while
`TokenIndex` remains unchanged.

`Ast.Node.Data` has also been changed to a (untagged) union to provide
safety checks.
This commit is contained in:
Techatrix committed 2025-03-07 22:22:01 +01:00
1 parent 6dcd8f4f75
commit ca6fb30e99
25 files changed
+4563 -5060

No files matched your search

+30 -60
View File
@@ -15,8 +15,7 @@ parent: Index,
pub const ExtraInfo = struct {
is_pub: bool,
name: []const u8,
/// This might not be a doc_comment token in which case there are no doc comments.
first_doc_comment: Ast.TokenIndex,
first_doc_comment: Ast.OptionalTokenIndex,
};
pub const Index = enum(u32) {
@@ -34,16 +33,14 @@ pub fn is_pub(d: *const Decl) bool {
pub fn extra_info(d: *const Decl) ExtraInfo {
const ast = d.file.get_ast();
const token_tags = ast.tokens.items(.tag);
const node_tags = ast.nodes.items(.tag);
switch (node_tags[d.ast_node]) {
switch (ast.nodeTag(d.ast_node)) {
.root => return .{
.name = "",
.is_pub = true,
.first_doc_comment = if (token_tags[0] == .container_doc_comment)
0
.first_doc_comment = if (ast.tokenTag(0) == .container_doc_comment)
.fromToken(0)
else
token_tags.len - 1,
.none,
},
.global_var_decl,
@@ -53,7 +50,7 @@ pub fn extra_info(d: *const Decl) ExtraInfo {
=> {
const var_decl = ast.fullVarDecl(d.ast_node).?;
const name_token = var_decl.ast.mut_token + 1;
assert(token_tags[name_token] == .identifier);
assert(ast.tokenTag(name_token) == .identifier);
const ident_name = ast.tokenSlice(name_token);
return .{
.name = ident_name,
@@ -71,7 +68,7 @@ pub fn extra_info(d: *const Decl) ExtraInfo {
var buf: [1]Ast.Node.Index = undefined;
const fn_proto = ast.fullFnProto(&buf, d.ast_node).?;
const name_token = fn_proto.name_token.?;
assert(token_tags[name_token] == .identifier);
assert(ast.tokenTag(name_token) == .identifier);
const ident_name = ast.tokenSlice(name_token);
return .{
.name = ident_name,
@@ -89,9 +86,7 @@ pub fn extra_info(d: *const Decl) ExtraInfo {
pub fn value_node(d: *const Decl) ?Ast.Node.Index {
const ast = d.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const token_tags = ast.tokens.items(.tag);
return switch (node_tags[d.ast_node]) {
return switch (ast.nodeTag(d.ast_node)) {
.fn_proto,
.fn_proto_multi,
.fn_proto_one,
@@ -106,8 +101,8 @@ pub fn value_node(d: *const Decl) ?Ast.Node.Index {
.aligned_var_decl,
=> {
const var_decl = ast.fullVarDecl(d.ast_node).?;
if (token_tags[var_decl.ast.mut_token] == .keyword_const)
return var_decl.ast.init_node;
if (ast.tokenTag(var_decl.ast.mut_token) == .keyword_const)
return var_decl.ast.init_node.unwrap();
return null;
},
@@ -148,19 +143,12 @@ pub fn get_child(decl: *const Decl, name: []const u8) ?Decl.Index {
pub fn get_type_fn_return_type_fn(decl: *const Decl) ?Decl.Index {
if (decl.get_type_fn_return_expr()) |return_expr| {
const ast = decl.file.get_ast();
const node_tags = ast.nodes.items(.tag);
switch (node_tags[return_expr]) {
.call, .call_comma, .call_one, .call_one_comma => {
const node_data = ast.nodes.items(.data);
const function = node_data[return_expr].lhs;
const token = ast.nodes.items(.main_token)[function];
const name = ast.tokenSlice(token);
if (decl.lookup(name)) |function_decl| {
return function_decl;
}
},
else => {},
var buffer: [1]Ast.Node.Index = undefined;
const call = ast.fullCall(&buffer, return_expr) orelse return null;
const token = ast.nodeMainToken(call.ast.fn_expr);
const name = ast.tokenSlice(token);
if (decl.lookup(name)) |function_decl| {
return function_decl;
}
}
return null;
@@ -171,35 +159,18 @@ pub fn get_type_fn_return_expr(decl: *const Decl) ?Ast.Node.Index {
switch (decl.categorize()) {
.type_function => {
const ast = decl.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const node_data = ast.nodes.items(.data);
const body_node = node_data[decl.ast_node].rhs;
if (body_node == 0) return null;
switch (node_tags[body_node]) {
.block, .block_semicolon => {
const statements = ast.extra_data[node_data[body_node].lhs..node_data[body_node].rhs];
// Look for the return statement
for (statements) |stmt| {
if (node_tags[stmt] == .@"return") {
return node_data[stmt].lhs;
}
}
return null;
},
.block_two, .block_two_semicolon => {
if (node_tags[node_data[body_node].lhs] == .@"return") {
return node_data[node_data[body_node].lhs].lhs;
}
if (node_data[body_node].rhs != 0 and
node_tags[node_data[body_node].rhs] == .@"return")
{
return node_data[node_data[body_node].rhs].lhs;
}
return null;
},
else => return null,
const body_node = ast.nodeData(decl.ast_node).node_and_node[1];
var buf: [2]Ast.Node.Index = undefined;
const statements = ast.blockStatements(&buf, body_node) orelse return null;
for (statements) |stmt| {
if (ast.nodeTag(stmt) == .@"return") {
return ast.nodeData(stmt).node;
}
}
return null;
},
else => return null,
}
@@ -269,16 +240,15 @@ pub fn append_parent_ns(list: *std.ArrayListUnmanaged(u8), parent: Decl.Index) O
}
}
pub fn findFirstDocComment(ast: *const Ast, token: Ast.TokenIndex) Ast.TokenIndex {
const token_tags = ast.tokens.items(.tag);
pub fn findFirstDocComment(ast: *const Ast, token: Ast.TokenIndex) Ast.OptionalTokenIndex {
var it = token;
while (it > 0) {
it -= 1;
if (token_tags[it] != .doc_comment) {
return it + 1;
if (ast.tokenTag(it) != .doc_comment) {
return .fromToken(it + 1);
}
}
return it;
return .none;
}
/// Successively looks up each component.
+71 -94
View File
@@ -91,12 +91,10 @@ pub const File = struct {
pub fn categorize_decl(file_index: File.Index, node: Ast.Node.Index) Category {
const ast = file_index.get_ast();
const node_tags = ast.nodes.items(.tag);
const token_tags = ast.tokens.items(.tag);
switch (node_tags[node]) {
switch (ast.nodeTag(node)) {
.root => {
for (ast.rootDecls()) |member| {
switch (node_tags[member]) {
switch (ast.nodeTag(member)) {
.container_field_init,
.container_field_align,
.container_field,
@@ -113,10 +111,12 @@ pub const File = struct {
.aligned_var_decl,
=> {
const var_decl = ast.fullVarDecl(node).?;
if (token_tags[var_decl.ast.mut_token] == .keyword_var)
if (ast.tokenTag(var_decl.ast.mut_token) == .keyword_var)
return .{ .global_variable = node };
const init_node = var_decl.ast.init_node.unwrap() orelse
return .{ .global_const = node };
return categorize_expr(file_index, var_decl.ast.init_node);
return categorize_expr(file_index, init_node);
},
.fn_proto,
@@ -139,7 +139,7 @@ pub const File = struct {
node: Ast.Node.Index,
full: Ast.full.FnProto,
) Category {
return switch (categorize_expr(file_index, full.ast.return_type)) {
return switch (categorize_expr(file_index, full.ast.return_type.unwrap().?)) {
.namespace, .container, .error_set, .type_type => .{ .type_function = node },
else => .{ .function = node },
};
@@ -155,12 +155,8 @@ pub const File = struct {
pub fn categorize_expr(file_index: File.Index, node: Ast.Node.Index) Category {
const file = file_index.get();
const ast = file_index.get_ast();
const node_tags = ast.nodes.items(.tag);
const node_datas = ast.nodes.items(.data);
const main_tokens = ast.nodes.items(.main_token);
const token_tags = ast.tokens.items(.tag);
//log.debug("categorize_expr tag {s}", .{@tagName(node_tags[node])});
return switch (node_tags[node]) {
//log.debug("categorize_expr tag {s}", .{@tagName(ast.nodeTag(node))});
return switch (ast.nodeTag(node)) {
.container_decl,
.container_decl_trailing,
.container_decl_arg,
@@ -176,11 +172,11 @@ pub const File = struct {
=> {
var buf: [2]Ast.Node.Index = undefined;
const container_decl = ast.fullContainerDecl(&buf, node).?;
if (token_tags[container_decl.ast.main_token] != .keyword_struct) {
if (ast.tokenTag(container_decl.ast.main_token) != .keyword_struct) {
return .{ .container = node };
}
for (container_decl.ast.members) |member| {
switch (node_tags[member]) {
switch (ast.nodeTag(member)) {
.container_field_init,
.container_field_align,
.container_field,
@@ -196,7 +192,7 @@ pub const File = struct {
=> .{ .error_set = node },
.identifier => {
const name_token = ast.nodes.items(.main_token)[node];
const name_token = ast.nodeMainToken(node);
const ident_name = ast.tokenSlice(name_token);
if (std.mem.eql(u8, ident_name, "type"))
return .type_type;
@@ -217,9 +213,7 @@ pub const File = struct {
},
.field_access => {
const object_node = node_datas[node].lhs;
const dot_token = main_tokens[node];
const field_ident = dot_token + 1;
const object_node, const field_ident = ast.nodeData(node).node_and_token;
const field_name = ast.tokenSlice(field_ident);
switch (categorize_expr(file_index, object_node)) {
@@ -259,9 +253,9 @@ pub const File = struct {
.@"if",
=> {
const if_full = ast.fullIf(node).?;
if (if_full.ast.else_expr != 0) {
if (if_full.ast.else_expr.unwrap()) |else_expr| {
const then_cat = categorize_expr_deep(file_index, if_full.ast.then_expr);
const else_cat = categorize_expr_deep(file_index, if_full.ast.else_expr);
const else_cat = categorize_expr_deep(file_index, else_expr);
if (then_cat == .type_type and else_cat == .type_type) {
return .type_type;
} else if (then_cat == .error_set and else_cat == .error_set) {
@@ -320,11 +314,10 @@ pub const File = struct {
params: []const Ast.Node.Index,
) Category {
const ast = file_index.get_ast();
const main_tokens = ast.nodes.items(.main_token);
const builtin_token = main_tokens[node];
const builtin_token = ast.nodeMainToken(node);
const builtin_name = ast.tokenSlice(builtin_token);
if (std.mem.eql(u8, builtin_name, "@import")) {
const str_lit_token = main_tokens[params[0]];
const str_lit_token = ast.nodeMainToken(params[0]);
const str_bytes = ast.tokenSlice(str_lit_token);
const file_path = std.zig.string_literal.parseAlloc(gpa, str_bytes) catch @panic("OOM");
defer gpa.free(file_path);
@@ -357,14 +350,12 @@ pub const File = struct {
fn categorize_switch(file_index: File.Index, node: Ast.Node.Index) Category {
const ast = file_index.get_ast();
const node_datas = ast.nodes.items(.data);
const extra = ast.extraData(node_datas[node].rhs, Ast.Node.SubRange);
const case_nodes = ast.extra_data[extra.start..extra.end];
const full = ast.fullSwitch(node).?;
var all_type_type = true;
var all_error_set = true;
var any_type = false;
if (case_nodes.len == 0) return .{ .global_const = node };
for (case_nodes) |case_node| {
if (full.ast.cases.len == 0) return .{ .global_const = node };
for (full.ast.cases) |case_node| {
const case = ast.fullSwitchCase(case_node).?;
switch (categorize_expr_deep(file_index, case.ast.target_expr)) {
.type_type => {
@@ -410,8 +401,8 @@ pub fn add_file(file_name: []const u8, bytes: []u8) !File.Index {
const scope = try gpa.create(Scope);
scope.* = .{ .tag = .top };
const decl_index = try file_index.add_decl(0, .none);
try struct_decl(&w, scope, decl_index, 0, ast.containerDeclRoot());
const decl_index = try file_index.add_decl(.root, .none);
try struct_decl(&w, scope, decl_index, .root, ast.containerDeclRoot());
const file = file_index.get();
shrinkToFit(&file.ident_decls);
@@ -505,13 +496,12 @@ pub const Scope = struct {
}
pub fn lookup(start_scope: *Scope, ast: *const Ast, name: []const u8) ?Ast.Node.Index {
const main_tokens = ast.nodes.items(.main_token);
var it: *Scope = start_scope;
while (true) switch (it.tag) {
.top => break,
.local => {
const local: *Local = @alignCast(@fieldParentPtr("base", it));
const name_token = main_tokens[local.var_node] + 1;
const name_token = ast.nodeMainToken(local.var_node) + 1;
const ident_name = ast.tokenSlice(name_token);
if (std.mem.eql(u8, ident_name, name)) {
return local.var_node;
@@ -538,8 +528,6 @@ fn struct_decl(
container_decl: Ast.full.ContainerDecl,
) Oom!void {
const ast = w.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const node_datas = ast.nodes.items(.data);
const namespace = try gpa.create(Scope.Namespace);
namespace.* = .{
@@ -549,7 +537,7 @@ fn struct_decl(
try w.file.get().scopes.putNoClobber(gpa, node, &namespace.base);
try w.scanDecls(namespace, container_decl.ast.members);
for (container_decl.ast.members) |member| switch (node_tags[member]) {
for (container_decl.ast.members) |member| switch (ast.nodeTag(member)) {
.container_field_init,
.container_field_align,
.container_field,
@@ -569,7 +557,7 @@ fn struct_decl(
try w.file.get().doctests.put(gpa, member, doctest_node);
}
const decl_index = try w.file.add_decl(member, parent_decl);
const body = if (node_tags[member] == .fn_decl) node_datas[member].rhs else 0;
const body = if (ast.nodeTag(member) == .fn_decl) ast.nodeData(member).node_and_node[1].toOptional() else .none;
try w.fn_decl(&namespace.base, decl_index, body, full);
},
@@ -584,9 +572,9 @@ fn struct_decl(
.@"comptime",
.@"usingnamespace",
=> try w.expr(&namespace.base, parent_decl, node_datas[member].lhs),
=> try w.expr(&namespace.base, parent_decl, ast.nodeData(member).node),
.test_decl => try w.expr(&namespace.base, parent_decl, node_datas[member].rhs),
.test_decl => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).opt_token_and_node[1]),
else => unreachable,
};
@@ -633,13 +621,13 @@ fn fn_decl(
w: *Walk,
scope: *Scope,
parent_decl: Decl.Index,
body: Ast.Node.Index,
body: Ast.Node.OptionalIndex,
full: Ast.full.FnProto,
) Oom!void {
for (full.ast.params) |param| {
try expr(w, scope, parent_decl, param);
}
try expr(w, scope, parent_decl, full.ast.return_type);
try expr(w, scope, parent_decl, full.ast.return_type.unwrap().?);
try maybe_expr(w, scope, parent_decl, full.ast.align_expr);
try maybe_expr(w, scope, parent_decl, full.ast.addrspace_expr);
try maybe_expr(w, scope, parent_decl, full.ast.section_expr);
@@ -647,17 +635,13 @@ fn fn_decl(
try maybe_expr(w, scope, parent_decl, body);
}
fn maybe_expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index) Oom!void {
if (node != 0) return expr(w, scope, parent_decl, node);
fn maybe_expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.OptionalIndex) Oom!void {
if (node.unwrap()) |n| return expr(w, scope, parent_decl, n);
}
fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index) Oom!void {
assert(node != 0);
const ast = w.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const node_datas = ast.nodes.items(.data);
const main_tokens = ast.nodes.items(.main_token);
switch (node_tags[node]) {
switch (ast.nodeTag(node)) {
.root => unreachable, // Top-level declaration.
.@"usingnamespace" => unreachable, // Top-level declaration.
.test_decl => unreachable, // Top-level declaration.
@@ -738,8 +722,9 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
.array_access,
.switch_range,
=> {
try expr(w, scope, parent_decl, node_datas[node].lhs);
try expr(w, scope, parent_decl, node_datas[node].rhs);
const lhs, const rhs = ast.nodeData(node).node_and_node;
try expr(w, scope, parent_decl, lhs);
try expr(w, scope, parent_decl, rhs);
},
.assign_destructure => {
@@ -752,35 +737,33 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
.bit_not,
.negation,
.negation_wrap,
.@"return",
.deref,
.address_of,
.optional_type,
.unwrap_optional,
.grouped_expression,
.@"comptime",
.@"nosuspend",
.@"suspend",
.@"await",
.@"resume",
.@"try",
=> try maybe_expr(w, scope, parent_decl, node_datas[node].lhs),
=> try expr(w, scope, parent_decl, ast.nodeData(node).node),
.unwrap_optional,
.grouped_expression,
=> try expr(w, scope, parent_decl, ast.nodeData(node).node_and_token[0]),
.@"return" => try maybe_expr(w, scope, parent_decl, ast.nodeData(node).opt_node),
.anyframe_type,
.@"break",
=> try maybe_expr(w, scope, parent_decl, node_datas[node].rhs),
.anyframe_type => try expr(w, scope, parent_decl, ast.nodeData(node).token_and_node[1]),
.@"break" => try maybe_expr(w, scope, parent_decl, ast.nodeData(node).opt_token_and_opt_node[1]),
.identifier => {
const ident_token = main_tokens[node];
const ident_token = ast.nodeMainToken(node);
const ident_name = ast.tokenSlice(ident_token);
if (scope.lookup(ast, ident_name)) |var_node| {
try w.file.get().ident_decls.put(gpa, ident_token, var_node);
}
},
.field_access => {
const object_node = node_datas[node].lhs;
const dot_token = main_tokens[node];
const field_ident = dot_token + 1;
const object_node, const field_ident = ast.nodeData(node).node_and_token;
try w.file.get().token_parents.put(gpa, field_ident, node);
// This will populate the left-most field object if it is an
// identifier, allowing rendering code to piece together the link.
@@ -857,9 +840,10 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
.for_simple, .@"for" => {
const full = ast.fullFor(node).?;
for (full.ast.inputs) |input| {
if (node_tags[input] == .for_range) {
try expr(w, scope, parent_decl, node_datas[input].lhs);
try maybe_expr(w, scope, parent_decl, node_datas[input].rhs);
if (ast.nodeTag(input) == .for_range) {
const start, const end = ast.nodeData(input).node_and_opt_node;
try expr(w, scope, parent_decl, start);
try maybe_expr(w, scope, parent_decl, end);
} else {
try expr(w, scope, parent_decl, input);
}
@@ -914,17 +898,16 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
},
.array_type_sentinel => {
const extra = ast.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
try expr(w, scope, parent_decl, node_datas[node].lhs);
const len_expr, const extra_index = ast.nodeData(node).node_and_extra;
const extra = ast.extraData(extra_index, Ast.Node.ArrayTypeSentinel);
try expr(w, scope, parent_decl, len_expr);
try expr(w, scope, parent_decl, extra.elem_type);
try expr(w, scope, parent_decl, extra.sentinel);
},
.@"switch", .switch_comma => {
const operand_node = node_datas[node].lhs;
try expr(w, scope, parent_decl, operand_node);
const extra = ast.extraData(node_datas[node].rhs, Ast.Node.SubRange);
const case_nodes = ast.extra_data[extra.start..extra.end];
for (case_nodes) |case_node| {
const full = ast.fullSwitch(node).?;
try expr(w, scope, parent_decl, full.ast.condition);
for (full.ast.cases) |case_node| {
const case = ast.fullSwitchCase(case_node).?;
for (case.ast.values) |value_node| {
try expr(w, scope, parent_decl, value_node);
@@ -973,7 +956,7 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
.fn_proto,
=> {
var buf: [1]Ast.Node.Index = undefined;
return fn_decl(w, scope, parent_decl, 0, ast.fullFnProto(&buf, node).?);
return fn_decl(w, scope, parent_decl, .none, ast.fullFnProto(&buf, node).?);
},
}
}
@@ -993,8 +976,7 @@ fn builtin_call(
params: []const Ast.Node.Index,
) Oom!void {
const ast = w.file.get_ast();
const main_tokens = ast.nodes.items(.main_token);
const builtin_token = main_tokens[node];
const builtin_token = ast.nodeMainToken(node);
const builtin_name = ast.tokenSlice(builtin_token);
if (std.mem.eql(u8, builtin_name, "@This")) {
try w.file.get().node_decls.put(gpa, node, scope.getNamespaceDecl());
@@ -1012,13 +994,11 @@ fn block(
statements: []const Ast.Node.Index,
) Oom!void {
const ast = w.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const node_datas = ast.nodes.items(.data);
var scope = parent_scope;
for (statements) |node| {
switch (node_tags[node]) {
switch (ast.nodeTag(node)) {
.global_var_decl,
.local_var_decl,
.simple_var_decl,
@@ -1039,11 +1019,10 @@ fn block(
log.debug("walk assign_destructure not implemented yet", .{});
},
.grouped_expression => try expr(w, scope, parent_decl, node_datas[node].lhs),
.grouped_expression => try expr(w, scope, parent_decl, ast.nodeData(node).node_and_token[0]),
.@"defer",
.@"errdefer",
=> try expr(w, scope, parent_decl, node_datas[node].rhs),
.@"defer" => try expr(w, scope, parent_decl, ast.nodeData(node).node),
.@"errdefer" => try expr(w, scope, parent_decl, ast.nodeData(node).opt_token_and_node[1]),
else => try expr(w, scope, parent_decl, node),
}
@@ -1059,18 +1038,14 @@ fn while_expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, full: Ast.full.W
fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.Index) Oom!void {
const ast = w.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const main_tokens = ast.nodes.items(.main_token);
const token_tags = ast.tokens.items(.tag);
const node_datas = ast.nodes.items(.data);
for (members) |member_node| {
const name_token = switch (node_tags[member_node]) {
const name_token = switch (ast.nodeTag(member_node)) {
.global_var_decl,
.local_var_decl,
.simple_var_decl,
.aligned_var_decl,
=> main_tokens[member_node] + 1,
=> ast.nodeMainToken(member_node) + 1,
.fn_proto_simple,
.fn_proto_multi,
@@ -1078,17 +1053,19 @@ fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.In
.fn_proto,
.fn_decl,
=> blk: {
const ident = main_tokens[member_node] + 1;
if (token_tags[ident] != .identifier) continue;
const ident = ast.nodeMainToken(member_node) + 1;
if (ast.tokenTag(ident) != .identifier) continue;
break :blk ident;
},
.test_decl => {
const ident_token = node_datas[member_node].lhs;
const is_doctest = token_tags[ident_token] == .identifier;
if (is_doctest) {
const token_bytes = ast.tokenSlice(ident_token);
try namespace.doctests.put(gpa, token_bytes, member_node);
const opt_ident_token = ast.nodeData(member_node).opt_token_and_node[0];
if (opt_ident_token.unwrap()) |ident_token| {
const is_doctest = ast.tokenTag(ident_token) == .identifier;
if (is_doctest) {
const token_bytes = ast.tokenSlice(ident_token);
try namespace.doctests.put(gpa, token_bytes, member_node);
}
}
continue;
},
+9 -18
View File
@@ -41,14 +41,10 @@ pub fn fileSourceHtml(
var field_access_buffer: std.ArrayListUnmanaged(u8) = .empty;
};
const token_tags = ast.tokens.items(.tag);
const token_starts = ast.tokens.items(.start);
const main_tokens = ast.nodes.items(.main_token);
const start_token = ast.firstToken(root_node);
const end_token = ast.lastToken(root_node) + 1;
var cursor: usize = token_starts[start_token];
var cursor: usize = ast.tokenStart(start_token);
var indent: usize = 0;
if (std.mem.lastIndexOf(u8, ast.source[0..cursor], "\n")) |newline_index| {
@@ -64,8 +60,8 @@ pub fn fileSourceHtml(
var next_annotate_index: usize = 0;
for (
token_tags[start_token..end_token],
token_starts[start_token..end_token],
ast.tokens.items(.tag)[start_token..end_token],
ast.tokens.items(.start)[start_token..end_token],
start_token..,
) |tag, start, token_index| {
const between = ast.source[cursor..start];
@@ -184,7 +180,7 @@ pub fn fileSourceHtml(
.identifier => i: {
if (options.fn_link != .none) {
const fn_link = options.fn_link.get();
const fn_token = main_tokens[fn_link.ast_node];
const fn_token = ast.nodeMainToken(fn_link.ast_node);
if (token_index == fn_token + 1) {
try out.appendSlice(gpa, "<a class=\"tok-fn\" href=\"#");
_ = missing_feature_url_escape;
@@ -196,7 +192,7 @@ pub fn fileSourceHtml(
}
}
if (token_index > 0 and token_tags[token_index - 1] == .keyword_fn) {
if (token_index > 0 and ast.tokenTag(token_index - 1) == .keyword_fn) {
try out.appendSlice(gpa, "<span class=\"tok-fn\">");
try appendEscaped(out, slice);
try out.appendSlice(gpa, "</span>");
@@ -358,16 +354,11 @@ fn walkFieldAccesses(
node: Ast.Node.Index,
) Oom!void {
const ast = file_index.get_ast();
const node_tags = ast.nodes.items(.tag);
assert(node_tags[node] == .field_access);
const node_datas = ast.nodes.items(.data);
const main_tokens = ast.nodes.items(.main_token);
const object_node = node_datas[node].lhs;
const dot_token = main_tokens[node];
const field_ident = dot_token + 1;
switch (node_tags[object_node]) {
assert(ast.nodeTag(node) == .field_access);
const object_node, const field_ident = ast.nodeData(node).node_and_token;
switch (ast.nodeTag(object_node)) {
.identifier => {
const lhs_ident = main_tokens[object_node];
const lhs_ident = ast.nodeMainToken(object_node);
try resolveIdentLink(file_index, out, lhs_ident);
},
.field_access => {
+34 -48
View File
@@ -124,7 +124,9 @@ fn query_exec_fallible(query: []const u8, ignore_case: bool) !void {
@memcpy(g.full_path_search_text_lower.items, g.full_path_search_text.items);
const ast = decl.file.get_ast();
try collect_docs(&g.doc_search_text, ast, info.first_doc_comment);
if (info.first_doc_comment.unwrap()) |first_doc_comment| {
try collect_docs(&g.doc_search_text, ast, first_doc_comment);
}
if (ignore_case) {
ascii_lower(g.full_path_search_text_lower.items);
@@ -227,18 +229,15 @@ const ErrorIdentifier = packed struct(u64) {
fn hasDocs(ei: ErrorIdentifier) bool {
const decl_index = ei.decl_index;
const ast = decl_index.get().file.get_ast();
const token_tags = ast.tokens.items(.tag);
const token_index = ei.token_index;
if (token_index == 0) return false;
return token_tags[token_index - 1] == .doc_comment;
return ast.tokenTag(token_index - 1) == .doc_comment;
}
fn html(ei: ErrorIdentifier, base_decl: Decl.Index, out: *std.ArrayListUnmanaged(u8)) Oom!void {
const decl_index = ei.decl_index;
const ast = decl_index.get().file.get_ast();
const name = ast.tokenSlice(ei.token_index);
const first_doc_comment = Decl.findFirstDocComment(ast, ei.token_index);
const has_docs = ast.tokens.items(.tag)[first_doc_comment] == .doc_comment;
const has_link = base_decl != decl_index;
try out.appendSlice(gpa, "<dt>");
@@ -253,7 +252,7 @@ const ErrorIdentifier = packed struct(u64) {
}
try out.appendSlice(gpa, "</dt>");
if (has_docs) {
if (Decl.findFirstDocComment(ast, ei.token_index).unwrap()) |first_doc_comment| {
try out.appendSlice(gpa, "<dd>");
try render_docs(out, decl_index, first_doc_comment, false);
try out.appendSlice(gpa, "</dd>");
@@ -319,17 +318,16 @@ fn addErrorsFromExpr(
) Oom!void {
const decl = decl_index.get();
const ast = decl.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const node_datas = ast.nodes.items(.data);
switch (decl.file.categorize_expr(node)) {
.error_set => |n| switch (node_tags[n]) {
.error_set => |n| switch (ast.nodeTag(n)) {
.error_set_decl => {
try addErrorsFromNode(decl_index, out, node);
},
.merge_error_sets => {
try addErrorsFromExpr(decl_index, out, node_datas[node].lhs);
try addErrorsFromExpr(decl_index, out, node_datas[node].rhs);
const lhs, const rhs = ast.nodeData(n).node_and_node;
try addErrorsFromExpr(decl_index, out, lhs);
try addErrorsFromExpr(decl_index, out, rhs);
},
else => unreachable,
},
@@ -347,11 +345,9 @@ fn addErrorsFromNode(
) Oom!void {
const decl = decl_index.get();
const ast = decl.file.get_ast();
const main_tokens = ast.nodes.items(.main_token);
const token_tags = ast.tokens.items(.tag);
const error_token = main_tokens[node];
const error_token = ast.nodeMainToken(node);
var tok_i = error_token + 2;
while (true) : (tok_i += 1) switch (token_tags[tok_i]) {
while (true) : (tok_i += 1) switch (ast.tokenTag(tok_i)) {
.doc_comment, .comma => {},
.identifier => {
const name = ast.tokenSlice(tok_i);
@@ -391,15 +387,13 @@ fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index {
switch (decl.categorize()) {
.type_function => {
const node_tags = ast.nodes.items(.tag);
// If the type function returns a reference to another type function, get the fields from there
if (decl.get_type_fn_return_type_fn()) |function_decl| {
return decl_fields_fallible(function_decl);
}
// If the type function returns a container, such as a `struct`, read that container's fields
if (decl.get_type_fn_return_expr()) |return_expr| {
switch (node_tags[return_expr]) {
switch (ast.nodeTag(return_expr)) {
.container_decl, .container_decl_trailing, .container_decl_two, .container_decl_two_trailing, .container_decl_arg, .container_decl_arg_trailing => {
return ast_decl_fields_fallible(ast, return_expr);
},
@@ -420,10 +414,9 @@ fn ast_decl_fields_fallible(ast: *Ast, ast_index: Ast.Node.Index) ![]Ast.Node.In
var result: std.ArrayListUnmanaged(Ast.Node.Index) = .empty;
};
g.result.clearRetainingCapacity();
const node_tags = ast.nodes.items(.tag);
var buf: [2]Ast.Node.Index = undefined;
const container_decl = ast.fullContainerDecl(&buf, ast_index) orelse return &.{};
for (container_decl.ast.members) |member_node| switch (node_tags[member_node]) {
for (container_decl.ast.members) |member_node| switch (ast.nodeTag(member_node)) {
.container_field_init,
.container_field_align,
.container_field,
@@ -478,9 +471,8 @@ fn decl_field_html_fallible(
try out.appendSlice(gpa, "</code></pre>");
const field = ast.fullContainerField(field_node).?;
const first_doc_comment = Decl.findFirstDocComment(ast, field.firstToken());
if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) {
if (Decl.findFirstDocComment(ast, field.firstToken()).unwrap()) |first_doc_comment| {
try out.appendSlice(gpa, "<div class=\"fieldDocs\">");
try render_docs(out, decl_index, first_doc_comment, false);
try out.appendSlice(gpa, "</div>");
@@ -494,14 +486,13 @@ fn decl_param_html_fallible(
) !void {
const decl = decl_index.get();
const ast = decl.file.get_ast();
const token_tags = ast.tokens.items(.tag);
const colon = ast.firstToken(param_node) - 1;
const name_token = colon - 1;
const first_doc_comment = f: {
var it = ast.firstToken(param_node);
while (it > 0) {
it -= 1;
switch (token_tags[it]) {
switch (ast.tokenTag(it)) {
.doc_comment, .colon, .identifier, .keyword_comptime, .keyword_noalias => {},
else => break,
}
@@ -516,7 +507,7 @@ fn decl_param_html_fallible(
try fileSourceHtml(decl.file, out, param_node, .{});
try out.appendSlice(gpa, "</code></pre>");
if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) {
if (ast.tokenTag(first_doc_comment) == .doc_comment) {
try out.appendSlice(gpa, "<div class=\"fieldDocs\">");
try render_docs(out, decl_index, first_doc_comment, false);
try out.appendSlice(gpa, "</div>");
@@ -526,10 +517,8 @@ fn decl_param_html_fallible(
export fn decl_fn_proto_html(decl_index: Decl.Index, linkify_fn_name: bool) String {
const decl = decl_index.get();
const ast = decl.file.get_ast();
const node_tags = ast.nodes.items(.tag);
const node_datas = ast.nodes.items(.data);
const proto_node = switch (node_tags[decl.ast_node]) {
.fn_decl => node_datas[decl.ast_node].lhs,
const proto_node = switch (ast.nodeTag(decl.ast_node)) {
.fn_decl => ast.nodeData(decl.ast_node).node_and_node[0],
.fn_proto,
.fn_proto_one,
@@ -586,17 +575,16 @@ export fn decl_parent(decl_index: Decl.Index) Decl.Index {
return decl.parent;
}
export fn fn_error_set(decl_index: Decl.Index) Ast.Node.Index {
export fn fn_error_set(decl_index: Decl.Index) Ast.Node.OptionalIndex {
const decl = decl_index.get();
const ast = decl.file.get_ast();
var buf: [1]Ast.Node.Index = undefined;
const full = ast.fullFnProto(&buf, decl.ast_node).?;
const node_tags = ast.nodes.items(.tag);
const node_datas = ast.nodes.items(.data);
return switch (node_tags[full.ast.return_type]) {
.error_set_decl => full.ast.return_type,
.error_union => node_datas[full.ast.return_type].lhs,
else => 0,
const return_type = full.ast.return_type.unwrap().?;
return switch (ast.nodeTag(return_type)) {
.error_set_decl => return_type.toOptional(),
.error_union => ast.nodeData(return_type).node_and_node[0].toOptional(),
else => .none,
};
}
@@ -609,21 +597,19 @@ export fn decl_file_path(decl_index: Decl.Index) String {
export fn decl_category_name(decl_index: Decl.Index) String {
const decl = decl_index.get();
const ast = decl.file.get_ast();
const token_tags = ast.tokens.items(.tag);
const name = switch (decl.categorize()) {
.namespace, .container => |node| {
const node_tags = ast.nodes.items(.tag);
if (node_tags[decl.ast_node] == .root)
if (ast.nodeTag(decl.ast_node) == .root)
return String.init("struct");
string_result.clearRetainingCapacity();
var buf: [2]Ast.Node.Index = undefined;
const container_decl = ast.fullContainerDecl(&buf, node).?;
if (container_decl.layout_token) |t| {
if (token_tags[t] == .keyword_extern) {
if (ast.tokenTag(t) == .keyword_extern) {
string_result.appendSlice(gpa, "extern ") catch @panic("OOM");
}
}
const main_token_tag = token_tags[container_decl.ast.main_token];
const main_token_tag = ast.tokenTag(container_decl.ast.main_token);
string_result.appendSlice(gpa, main_token_tag.lexeme().?) catch @panic("OOM");
return String.init(string_result.items);
},
@@ -656,7 +642,9 @@ export fn decl_name(decl_index: Decl.Index) String {
export fn decl_docs_html(decl_index: Decl.Index, short: bool) String {
const decl = decl_index.get();
string_result.clearRetainingCapacity();
render_docs(&string_result, decl_index, decl.extra_info().first_doc_comment, short) catch @panic("OOM");
if (decl.extra_info().first_doc_comment.unwrap()) |first_doc_comment| {
render_docs(&string_result, decl_index, first_doc_comment, short) catch @panic("OOM");
}
return String.init(string_result.items);
}
@@ -665,10 +653,9 @@ fn collect_docs(
ast: *const Ast,
first_doc_comment: Ast.TokenIndex,
) Oom!void {
const token_tags = ast.tokens.items(.tag);
list.clearRetainingCapacity();
var it = first_doc_comment;
while (true) : (it += 1) switch (token_tags[it]) {
while (true) : (it += 1) switch (ast.tokenTag(it)) {
.doc_comment, .container_doc_comment => {
// It is tempting to trim this string but think carefully about how
// that will affect the markdown parser.
@@ -687,12 +674,11 @@ fn render_docs(
) Oom!void {
const decl = decl_index.get();
const ast = decl.file.get_ast();
const token_tags = ast.tokens.items(.tag);
var parser = try markdown.Parser.init(gpa);
defer parser.deinit();
var it = first_doc_comment;
while (true) : (it += 1) switch (token_tags[it]) {
while (true) : (it += 1) switch (ast.tokenTag(it)) {
.doc_comment, .container_doc_comment => {
const line = ast.tokenSlice(it)[3..];
if (short and line.len == 0) break;
@@ -767,9 +753,9 @@ export fn decl_type_html(decl_index: Decl.Index) String {
t: {
// If there is an explicit type, use it.
if (ast.fullVarDecl(decl.ast_node)) |var_decl| {
if (var_decl.ast.type_node != 0) {
if (var_decl.ast.type_node.unwrap()) |type_node| {
string_result.appendSlice(gpa, "<code>") catch @panic("OOM");
fileSourceHtml(decl.file, &string_result, var_decl.ast.type_node, .{
fileSourceHtml(decl.file, &string_result, type_node, .{
.skip_comments = true,
.collapse_whitespace = true,
}) catch |e| {