Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

testDecl

AstGen.testDecl
fn testDecl(
    astgen: *AstGen,
    gz: *GenZir,
    scope: *Scope,
    wip_decls: *WipDecls,
    node: Ast.Node.Index,
) InnerError!void

File

lib/std/zig/AstGen.zig:4606

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));

    // Up top so the ZIR instruction index marks the start range of this
    // top-level declaration.
    const decl_inst = try gz.makeDeclaration(node);

    wip_decls.nextDecl(decl_inst);
    astgen.advanceSourceCursorToNode(node);

    // This is just needed for the `setDeclaration` call.
    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 not @"" syntax, just use raw token slice
            if (ident_name_raw[0] != '@') {
                if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
            }

            // Local variables, including function parameters.
            const name_str_index = try astgen.identAsString(test_name_token);
            var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
            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", .{}),
                            });
                        }
                        // We found a match but must continue looking for ambiguous references to decls.
                        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)) {

        // As our last action before the return, "pop" the error trace if needed
        _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node);

        // Add implicit return at end of function.
        _ = 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,

        // Tests don't have a prototype that needs hashing
        .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,
    });
}