feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.testDecl
fn testDecl(
astgen: *AstGen,
gz: *GenZir,
scope: *Scope,
wip_decls: *WipDecls,
node: Ast.Node.Index,
) InnerError!void
File
Code
fn testDecl(
astgen: *AstGen,
gz: *GenZir,
scope: *Scope,
wip_decls: *WipDecls,
node: Ast.Node.Index,
) InnerError!void {
const tree = astgen.tree;
_, const body_node = tree.nodeData(node).opt_token_and_node;
const old_hasher = astgen.src_hasher;
defer astgen.src_hasher = old_hasher;
astgen.src_hasher = std.zig.SrcHasher.init(.{});
astgen.src_hasher.update(tree.getNodeSource(node));
astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
// top-level declaration.
const decl_inst = try gz.makeDeclaration(node);
wip_decls.nextDecl(decl_inst);
astgen.advanceSourceCursorToNode(node);
var dummy_gz: GenZir = gz.makeSubBlock(scope);
defer dummy_gz.unstack();
var decl_block: GenZir = .{
.is_comptime = true,
.decl_node_index = node,
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
.instructions = dummy_gz.instructions,
.instructions_top = dummy_gz.instructions.items.len,
};
defer decl_block.unstack();
const decl_column = astgen.source_column;
const test_token = tree.nodeMainToken(node);
const test_name_token = test_token + 1;
const test_name: Zir.NullTerminatedString = switch (tree.tokenTag(test_name_token)) {
else => .empty,
.string_literal => name: {
const name = try astgen.strLitAsString(test_name_token);
const slice = astgen.string_bytes.items[@backingInt(name.index)..][0..name.len];
if (mem.findScalar(u8, slice, 0) != null) {
return astgen.failTok(test_name_token, "test name cannot contain null bytes", .{});
} else if (slice.len == 0) {
return astgen.failTok(test_name_token, "empty test name must be omitted", .{});
}
break :name name.index;
},
.identifier => name: {
const ident_name_raw = tree.tokenSlice(test_name_token);
if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
if (ident_name_raw[0] != '@') {
if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
}
const name_str_index = try astgen.identAsString(test_name_token);
var found_already: ?Ast.Node.Index = null;
var num_namespaces_out: u32 = 0;
var capturing_namespace: ?*Scope.Namespace = null;
find_scope: switch (scope.unwrap()) {
.local_val => |local_val| {
if (local_val.name == name_str_index) {
local_val.used = .fromToken(test_name_token);
return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
@tagName(local_val.id_cat),
}, &[_]u32{
try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{
@tagName(local_val.id_cat),
}),
});
}
continue :find_scope local_val.parent.unwrap();
},
.local_ptr => |local_ptr| {
if (local_ptr.name == name_str_index) {
local_ptr.used = .fromToken(test_name_token);
return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
@tagName(local_ptr.id_cat),
}, &[_]u32{
try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{
@tagName(local_ptr.id_cat),
}),
});
}
continue :find_scope local_ptr.parent.unwrap();
},
.gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
.defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
.namespace => |ns| {
if (ns.decls.get(name_str_index)) |i| {
if (found_already) |f| {
return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
try astgen.errNoteNode(f, "declared here", .{}),
try astgen.errNoteNode(i, "also declared here", .{}),
});
}
found_already = i;
}
num_namespaces_out += 1;
capturing_namespace = ns;
continue :find_scope ns.parent.unwrap();
},
.top => break :find_scope,
}
if (found_already == null) {
const ident_name = try astgen.identifierTokenString(test_name_token);
return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
}
break :name try astgen.identAsString(test_name_token);
},
};
var fn_block: GenZir = .{
.is_comptime = false,
.decl_node_index = node,
.decl_line = decl_block.decl_line,
.parent = &decl_block.base,
.astgen = astgen,
.instructions = decl_block.instructions,
.instructions_top = decl_block.instructions.items.len,
};
defer fn_block.unstack();
const prev_within_fn = astgen.within_fn;
const prev_fn_block = astgen.fn_block;
const prev_fn_ret_ty = astgen.fn_ret_ty;
astgen.within_fn = true;
astgen.fn_block = &fn_block;
astgen.fn_ret_ty = .anyerror_void_error_union_type;
defer {
astgen.within_fn = prev_within_fn;
astgen.fn_block = prev_fn_block;
astgen.fn_ret_ty = prev_fn_ret_ty;
}
astgen.advanceSourceCursorToNode(body_node);
const lbrace_line = astgen.source_line - decl_block.decl_line;
const lbrace_column = astgen.source_column;
const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal);
if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
_ = try fn_block.addRestoreErrRetIndex(.ret, .always, node);
_ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
}
const func_inst = try decl_block.addFunc(.{
.src_node = node,
.cc_ref = .none,
.cc_gz = null,
.ret_ref = .anyerror_void_error_union_type,
.ret_gz = null,
.ret_param_refs = &.{},
.param_insts = &.{},
.ret_ty_is_generic = false,
.lbrace_line = lbrace_line,
.lbrace_column = lbrace_column,
.param_block = decl_inst,
.body_gz = &fn_block,
.is_var_args = false,
.is_inferred_error = false,
.is_noinline = false,
.noalias_bits = 0,
.proto_hash = @splat(0),
});
_ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
var hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&hash);
try setDeclaration(decl_inst, .{
.src_hash = hash,
.src_line = decl_block.decl_line,
.src_column = decl_column,
.kind = switch (tree.tokenTag(test_name_token)) {
.string_literal => .@"test",
.identifier => .decltest,
else => .unnamed_test,
},
.name = test_name,
.is_pub = false,
.is_threadlocal = false,
.linkage = .normal,
.type_gz = &dummy_gz,
.align_gz = &dummy_gz,
.linksection_gz = &dummy_gz,
.addrspace_gz = &dummy_gz,
.value_gz = &decl_block,
});
}