zig/lib/std / zig.zig

Builds of the Zig compiler are distributed partly in source form. That source lives here. These APIs are provided as-is and have absolutely no API guarantees whatsoever.

//! Builds of the Zig compiler are distributed partly in source form. That
//! source lives here. These APIs are provided as-is and have absolutely no API
//! guarantees whatsoever.

ErrorBundle

zig/ErrorBundle.zig

Deprecated: use std.Target.Query.


pub const ErrorBundle = @import("zig/ErrorBundle.zig");

Server

zig/Server.zig

Determine whether stderr is a terminal or not automatically.

pub const Server = @import("zig/Server.zig");

Client

zig/Client.zig

Assume stderr is not a terminal.

pub const Client = @import("zig/Client.zig");

Token

Assume stderr is a terminal.

pub const Token = tokenizer.Token;

Tokenizer

There are many assumptions in the entire codebase that Zig source files can be byte-indexed with a u32 integer.

pub const Tokenizer = tokenizer.Tokenizer;

string_literal

zig/string_literal.zig

Does not include the trailing newline.

pub const string_literal = @import("zig/string_literal.zig");

number_literal

zig/number_literal.zig

Returns the standard file system basename of a binary generated by the Zig compiler.

pub const number_literal = @import("zig/number_literal.zig");

primitives

zig/primitives.zig

Result is byte values, *not* hex-encoded.

pub const primitives = @import("zig/primitives.zig");

isPrimitive

Input is byte values, *not* hex-encoded. Asserts bytes fits inside HexString

pub const isPrimitive = primitives.isPrimitive;

Ast

zig/Ast.zig

Converts UTF-8 text to a BuildId.

pub const Ast = @import("zig/Ast.zig");

AstGen

zig/AstGen.zig

Renders a std.Target.Cpu value into a textual representation that can be parsed via the -mcpu flag passed to the Zig compiler. Appends the result to buffer.

pub const AstGen = @import("zig/AstGen.zig");

Zir

zig/Zir.zig

Resolving a source location into a byte offset may require doing work that we would rather not do unless the error actually occurs. Therefore we need a data structure that contains the information necessary to lazily produce a SrcLoc as required. Most of the offsets in this data structure are relative to the containing Decl. This makes the source location resolve properly even when a Decl gets shifted up or down in the file, as long as the Decl's contents itself do not change.

pub const Zir = @import("zig/Zir.zig");

system

zig/system.zig

When this tag is set, the code that constructed this LazySrcLoc is asserting that all code paths which would need to resolve the source location are unreachable. If you are debugging this tag incorrectly being this value, look into using reverse-continue with a memory watchpoint to see where the value is being set to this tag.

pub const system = @import("zig/system.zig");
/// Deprecated: use `std.Target.Query`.

CrossTarget

Means the source location points to an entire file; not any particular location within the file. file_scope union field will be active.

pub const CrossTarget = std.Target.Query;

BuiltinFn

zig/BuiltinFn.zig

The source location points to a byte offset within a source file, offset from 0. The source file is determined contextually. Inside a SrcLoc, the file_scope union field will be active.

pub const BuiltinFn = @import("zig/BuiltinFn.zig");

AstRlAnnotate

zig/AstRlAnnotate.zig

The source location points to a token within a source file, offset from 0. The source file is determined contextually. Inside a SrcLoc, the file_scope union field will be active.

pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig");

LibCInstallation

zig/LibCInstallation.zig

The source location points to an AST node within a source file, offset from 0. The source file is determined contextually. Inside a SrcLoc, the file_scope union field will be active.

pub const LibCInstallation = @import("zig/LibCInstallation.zig");

WindowsSdk

zig/WindowsSdk.zig

The source location points to a byte offset within a source file, offset from the byte offset of the Decl within the file. The Decl is determined contextually.

pub const WindowsSdk = @import("zig/WindowsSdk.zig");

LibCDirs

zig/LibCDirs.zig

This data is the offset into the token list from the Decl token. The Decl is determined contextually.

pub const LibCDirs = @import("zig/LibCDirs.zig");

target

zig/target.zig

The source location points to an AST node, which is this value offset from its containing Decl node AST index. The Decl is determined contextually.

pub const target = @import("zig/target.zig");

ParsedCharLiteral

The source location points to the main token of an AST node, found by taking this AST node index offset from the containing Decl AST node. The Decl is determined contextually.


// Character literal parsing
pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;

parseCharLiteral

The source location points to the beginning of a struct initializer. The Decl is determined contextually.

pub const parseCharLiteral = string_literal.parseCharLiteral;

parseNumberLiteral

The source location points to a variable declaration type expression, found by taking this AST node index offset from the containing Decl AST node, which points to a variable declaration AST node. Next, navigate to the type expression. The Decl is determined contextually.

pub const parseNumberLiteral = number_literal.parseNumberLiteral;

c_builtins

zig/c_builtins.zig

The source location points to the alignment expression of a var decl. The Decl is determined contextually.


// Files needed by translate-c.
pub const c_builtins = @import("zig/c_builtins.zig");

c_translation

zig/c_translation.zig

The source location points to the linksection expression of a var decl. The Decl is determined contextually.

pub const c_translation = @import("zig/c_translation.zig");

SrcHasher

The source location points to the addrspace expression of a var decl. The Decl is determined contextually.


pub const SrcHasher = std.crypto.hash.Blake3;

SrcHash

The source location points to the initializer of a var decl. The Decl is determined contextually.

pub const SrcHash = [16]u8;

Color

The source location points to the first parameter of a builtin function call, found by taking this AST node index offset from the containing Decl AST node, which points to a builtin call AST node. Next, navigate to the first parameter. The Decl is determined contextually.


pub const Color = enum {
    /// Determine whether stderr is a terminal or not automatically.
    auto,
    /// Assume stderr is not a terminal.
    off,
    /// Assume stderr is a terminal.
    on,

get_tty_conf()

Same as node_offset_builtin_call_arg0 except arg index 1.


    pub fn get_tty_conf(color: Color) std.io.tty.Config {
        return switch (color) {
            .auto => std.io.tty.detectConfig(std.io.getStdErr()),
            .on => .escape_codes,
            .off => .no_color,
        };
    }

renderOptions()

Like node_offset_builtin_call_arg0 but recurses through arbitrarily many calls to pointer cast builtins.


    pub fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
        const ttyconf = get_tty_conf(color);
        return .{
            .ttyconf = ttyconf,
            .include_source_line = ttyconf != .no_color,
            .include_reference_trace = ttyconf != .no_color,
        };
    }
};

max_src_size

The source location points to the index expression of an array access expression, found by taking this AST node index offset from the containing Decl AST node, which points to an array access AST node. Next, navigate to the index expression. The Decl is determined contextually.


/// There are many assumptions in the entire codebase that Zig source files can
/// be byte-indexed with a u32 integer.
pub const max_src_size = std.math.maxInt(u32);

hashSrc()

The source location points to the LHS of a slice expression expression, found by taking this AST node index offset from the containing Decl AST node, which points to a slice AST node. Next, navigate to the sentinel expression. The Decl is determined contextually.


pub fn hashSrc(src: []const u8) SrcHash {
    var out: SrcHash = undefined;
    SrcHasher.hash(src, &out, .{});
    return out;

binNameAlloc()

The source location points to start expression of a slice expression expression, found by taking this AST node index offset from the containing Decl AST node, which points to a slice AST node. Next, navigate to the sentinel expression. The Decl is determined contextually.

}

hashName()

The source location points to the end expression of a slice expression, found by taking this AST node index offset from the containing Decl AST node, which points to a slice AST node. Next, navigate to the sentinel expression. The Decl is determined contextually.


pub fn srcHashEql(a: SrcHash, b: SrcHash) bool {
    return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));

binNameAlloc()

The source location points to the sentinel expression of a slice expression, found by taking this AST node index offset from the containing Decl AST node, which points to a slice AST node. Next, navigate to the sentinel expression. The Decl is determined contextually.

}

eql()

The source location points to the callee expression of a function call expression, found by taking this AST node index offset from the containing Decl AST node, which points to a function call AST node. Next, navigate to the callee expression. The Decl is determined contextually.


pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash {
    var out: SrcHash = undefined;
    var hasher = SrcHasher.init(.{});
    hasher.update(&parent_hash);
    hasher.update(sep);
    hasher.update(name);
    hasher.final(&out);
    return out;

binNameAlloc()

The payload is offset from the containing Decl AST node. The source location points to the field name of: * a field access expression (a.b), or * the callee of a method call (a.b()) The Decl is determined contextually.

}

lineDelta()

The payload is offset from the containing Decl AST node. The source location points to the field name of the operand ("b" node) of a field initialization expression (.a = b) The Decl is determined contextually.


pub const Loc = struct {
    line: usize,
    column: usize,
    /// Does not include the trailing newline.
    source_line: []const u8,

BinNameOptions

The source location points to the pointer of a pointer deref expression, found by taking this AST node index offset from the containing Decl AST node, which points to a pointer deref AST node. Next, navigate to the pointer expression. The Decl is determined contextually.


    pub fn eql(a: Loc, b: Loc) bool {
        return a.line == b.line and a.column == b.column and std.mem.eql(u8, a.source_line, b.source_line);
    }
};

binNameAlloc()

The source location points to the assembly source code of an inline assembly expression, found by taking this AST node index offset from the containing Decl AST node, which points to inline assembly AST node. Next, navigate to the asm template source code. The Decl is determined contextually.


pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc {
    var line: usize = 0;
    var column: usize = 0;
    var line_start: usize = 0;
    var i: usize = 0;
    while (i < byte_offset) : (i += 1) {
        switch (source[i]) {
            '\n' => {
                line += 1;
                column = 0;
                line_start = i + 1;
            },
            else => {
                column += 1;
            },
        }
    }
    while (i < source.len and source[i] != '\n') {
        i += 1;
    }
    return .{
        .line = line,
        .column = column,
        .source_line = source[line_start..i],
    };
}

BuildId

The source location points to the return type of an inline assembly expression, found by taking this AST node index offset from the containing Decl AST node, which points to inline assembly AST node. Next, navigate to the return type expression. The Decl is determined contextually.


pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
    var line: isize = 0;
    if (end >= start) {
        for (source[start..end]) |byte| switch (byte) {
            '\n' => line += 1,
            else => continue,
        };
    } else {
        for (source[end..start]) |byte| switch (byte) {
            '\n' => line -= 1,
            else => continue,
        };
    }
    return line;
}

eql()

The source location points to the condition expression of an if expression, found by taking this AST node index offset from the containing Decl AST node, which points to an if expression AST node. Next, navigate to the condition expression. The Decl is determined contextually.


pub const BinNameOptions = struct {
    root_name: []const u8,
    target: std.Target,
    output_mode: std.builtin.OutputMode,
    link_mode: ?std.builtin.LinkMode = null,
    version: ?std.SemanticVersion = null,
};

HexString

The source location points to a binary expression, such as a + b, found by taking this AST node index offset from the containing Decl AST node. The Decl is determined contextually.


/// Returns the standard file system basename of a binary generated by the Zig compiler.
pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
    const root_name = options.root_name;
    const t = options.target;
    switch (t.ofmt) {
        .coff => switch (options.output_mode) {
            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
            .Lib => {
                const suffix = switch (options.link_mode orelse .static) {
                    .static => ".lib",
                    .dynamic => ".dll",
                };
                return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
            },
            .Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}),
        },
        .elf => switch (options.output_mode) {
            .Exe => return allocator.dupe(u8, root_name),
            .Lib => {
                switch (options.link_mode orelse .static) {
                    .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
                        t.libPrefix(), root_name,
                    }),
                    .dynamic => {
                        if (options.version) |ver| {
                            return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
                                t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
                            });
                        } else {
                            return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
                                t.libPrefix(), root_name,
                            });
                        }
                    },
                }
            },
            .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
        },
        .macho => switch (options.output_mode) {
            .Exe => return allocator.dupe(u8, root_name),
            .Lib => {
                switch (options.link_mode orelse .static) {
                    .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
                        t.libPrefix(), root_name,
                    }),
                    .dynamic => {
                        if (options.version) |ver| {
                            return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
                                t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
                            });
                        } else {
                            return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
                                t.libPrefix(), root_name,
                            });
                        }
                    },
                }
            },
            .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
        },
        .wasm => switch (options.output_mode) {
            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
            .Lib => {
                switch (options.link_mode orelse .static) {
                    .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
                        t.libPrefix(), root_name,
                    }),
                    .dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
                }
            },
            .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
        },
        .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
        .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
        .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
        .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
        .plan9 => switch (options.output_mode) {
            .Exe => return allocator.dupe(u8, root_name),
            .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{
                root_name, t.ofmt.fileExt(t.cpu.arch),
            }),
            .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
                t.libPrefix(), root_name,
            }),
        },
        .nvptx => return std.fmt.allocPrint(allocator, "{s}.ptx", .{root_name}),
        .dxcontainer => return std.fmt.allocPrint(allocator, "{s}.dxil", .{root_name}),
    }
}

toSlice()

The source location points to the LHS of a binary expression, found by taking this AST node index offset from the containing Decl AST node, which points to a binary expression AST node. Next, navigate to the LHS. The Decl is determined contextually.


pub const BuildId = union(enum) {
    none,
    fast,
    uuid,
    sha1,
    md5,
    hexstring: HexString,

initHexString()

The source location points to the RHS of a binary expression, found by taking this AST node index offset from the containing Decl AST node, which points to a binary expression AST node. Next, navigate to the RHS. The Decl is determined contextually.


    pub fn eql(a: BuildId, b: BuildId) bool {
        const Tag = @typeInfo(BuildId).Union.tag_type.?;
        const a_tag: Tag = a;
        const b_tag: Tag = b;
        if (a_tag != b_tag) return false;
        return switch (a) {
            .none, .fast, .uuid, .sha1, .md5 => true,
            .hexstring => |a_hexstring| std.mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()),
        };
    }

parse()

The source location points to the operand of a switch expression, found by taking this AST node index offset from the containing Decl AST node, which points to a switch expression AST node. Next, navigate to the operand. The Decl is determined contextually.


    pub const HexString = struct {
        bytes: [32]u8,
        len: u8,

Test: parse

The source location points to the else/_ prong of a switch expression, found by taking this AST node index offset from the containing Decl AST node, which points to a switch expression AST node. Next, navigate to the else/_ prong. The Decl is determined contextually.


        /// Result is byte values, *not* hex-encoded.
        pub fn toSlice(hs: *const HexString) []const u8 {
            return hs.bytes[0..hs.len];
        }
    };

serializeCpu()

The source location points to all the ranges of a switch expression, found by taking this AST node index offset from the containing Decl AST node, which points to a switch expression AST node. Next, navigate to any of the range nodes. The error applies to all of them. The Decl is determined contextually.


    /// Input is byte values, *not* hex-encoded.
    /// Asserts `bytes` fits inside `HexString`
    pub fn initHexString(bytes: []const u8) BuildId {
        var result: BuildId = .{ .hexstring = .{
            .bytes = undefined,
            .len = @intCast(bytes.len),
        } };
        @memcpy(result.hexstring.bytes[0..bytes.len], bytes);
        return result;
    }

serializeCpuAlloc()

The source location points to the capture of a switch_prong. The Decl is determined contextually.


    /// Converts UTF-8 text to a `BuildId`.
    pub fn parse(text: []const u8) !BuildId {
        if (std.mem.eql(u8, text, "none")) {
            return .none;
        } else if (std.mem.eql(u8, text, "fast")) {
            return .fast;
        } else if (std.mem.eql(u8, text, "uuid")) {
            return .uuid;
        } else if (std.mem.eql(u8, text, "sha1") or std.mem.eql(u8, text, "tree")) {
            return .sha1;
        } else if (std.mem.eql(u8, text, "md5")) {
            return .md5;
        } else if (std.mem.startsWith(u8, text, "0x")) {
            var result: BuildId = .{ .hexstring = undefined };
            const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]);
            result.hexstring.len = @as(u8, @intCast(slice.len));
            return result;
        }
        return error.InvalidBuildIdStyle;
    }

DeclIndex

The source location points to the tag capture of a switch_prong. The Decl is determined contextually.


    test parse {
        try std.testing.expectEqual(BuildId.md5, try parse("md5"));
        try std.testing.expectEqual(BuildId.none, try parse("none"));
        try std.testing.expectEqual(BuildId.fast, try parse("fast"));
        try std.testing.expectEqual(BuildId.uuid, try parse("uuid"));
        try std.testing.expectEqual(BuildId.sha1, try parse("sha1"));
        try std.testing.expectEqual(BuildId.sha1, try parse("tree"));

toOptional()

The source location points to the align expr of a function type expression, found by taking this AST node index offset from the containing Decl AST node, which points to a function type AST node. Next, navigate to the calling convention node. The Decl is determined contextually.


        try std.testing.expect(BuildId.initHexString("").eql(try parse("0x")));
        try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456")));
        try std.testing.expectError(error.InvalidLength, parse("0x12-34"));
        try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
        try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
    }
};

OptionalDeclIndex

The source location points to the addrspace expr of a function type expression, found by taking this AST node index offset from the containing Decl AST node, which points to a function type AST node. Next, navigate to the calling convention node. The Decl is determined contextually.


/// Renders a `std.Target.Cpu` value into a textual representation that can be parsed
/// via the `-mcpu` flag passed to the Zig compiler.
/// Appends the result to `buffer`.
pub fn serializeCpu(buffer: *std.ArrayList(u8), cpu: std.Target.Cpu) Allocator.Error!void {
    const all_features = cpu.arch.allFeaturesList();
    var populated_cpu_features = cpu.model.features;
    populated_cpu_features.populateDependencies(all_features);

init()

The source location points to the linksection expr of a function type expression, found by taking this AST node index offset from the containing Decl AST node, which points to a function type AST node. Next, navigate to the calling convention node. The Decl is determined contextually.


    try buffer.appendSlice(cpu.model.name);

unwrap()

The source location points to the calling convention of a function type expression, found by taking this AST node index offset from the containing Decl AST node, which points to a function type AST node. Next, navigate to the calling convention node. The Decl is determined contextually.


    if (populated_cpu_features.eql(cpu.features)) {
        // The CPU name alone is sufficient.
        return;
    }

LazySrcLoc

The source location points to the return type of a function type expression, found by taking this AST node index offset from the containing Decl AST node, which points to a function type AST node. Next, navigate to the return type node. The Decl is determined contextually.


    for (all_features, 0..) |feature, i_usize| {
        const i: std.Target.Cpu.Feature.Set.Index = @intCast(i_usize);
        const in_cpu_set = populated_cpu_features.isEnabled(i);
        const in_actual_set = cpu.features.isEnabled(i);
        try buffer.ensureUnusedCapacity(feature.name.len + 1);
        if (in_cpu_set and !in_actual_set) {
            buffer.appendAssumeCapacity('-');
            buffer.appendSliceAssumeCapacity(feature.name);
        } else if (!in_cpu_set and in_actual_set) {
            buffer.appendAssumeCapacity('+');
            buffer.appendSliceAssumeCapacity(feature.name);
        }
    }
}

nodeOffset

The source location points to the type expression of an anyframe->T expression, found by taking this AST node index offset from the containing Decl AST node, which points to a anyframe->T expression AST node. Next, navigate to the type expression. The Decl is determined contextually.


pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![]u8 {
    var buffer = std.ArrayList(u8).init(ally);
    try serializeCpu(&buffer, cpu);
    return buffer.toOwnedSlice();
}

TracedOffset

The source location points to the string literal of extern "foo", found by taking this AST node index offset from the containing Decl AST node, which points to a function prototype or variable declaration expression AST node. Next, navigate to the string literal of the extern "foo". The Decl is determined contextually.


pub const DeclIndex = enum(u32) {
    _,

fmtId()

The source location points to the len expression of an [N:S]T expression, found by taking this AST node index offset from the containing Decl AST node, which points to an [N:S]T expression AST node. Next, navigate to the len expression. The Decl is determined contextually.


    pub fn toOptional(i: DeclIndex) OptionalDeclIndex {
        return @enumFromInt(@intFromEnum(i));
    }
};

Test: fmtId

The source location points to the sentinel expression of an [N:S]T expression, found by taking this AST node index offset from the containing Decl AST node, which points to an [N:S]T expression AST node. Next, navigate to the sentinel expression. The Decl is determined contextually.


pub const OptionalDeclIndex = enum(u32) {
    none = std.math.maxInt(u32),
    _,

fmtEscapes()

The source location points to the elem expression of an [N:S]T expression, found by taking this AST node index offset from the containing Decl AST node, which points to an [N:S]T expression AST node. Next, navigate to the elem expression. The Decl is determined contextually.


    pub fn init(oi: ?DeclIndex) OptionalDeclIndex {
        return @enumFromInt(@intFromEnum(oi orelse return .none));
    }

Test: fmtEscapes

The source location points to the operand of an unary expression. The Decl is determined contextually.


    pub fn unwrap(oi: OptionalDeclIndex) ?DeclIndex {
        if (oi == .none) return null;
        return @enumFromInt(@intFromEnum(oi));
    }
};

stringEscape()

The source location points to the elem type of a pointer. The Decl is determined contextually.


/// Resolving a source location into a byte offset may require doing work
/// that we would rather not do unless the error actually occurs.
/// Therefore we need a data structure that contains the information necessary
/// to lazily produce a `SrcLoc` as required.
/// Most of the offsets in this data structure are relative to the containing Decl.
/// This makes the source location resolve properly even when a Decl gets
/// shifted up or down in the file, as long as the Decl's contents itself
/// do not change.
pub const LazySrcLoc = union(enum) {
    /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
    /// that all code paths which would need to resolve the source location are
    /// unreachable. If you are debugging this tag incorrectly being this value,
    /// look into using reverse-continue with a memory watchpoint to see where the
    /// value is being set to this tag.
    unneeded,
    /// Means the source location points to an entire file; not any particular
    /// location within the file. `file_scope` union field will be active.
    entire_file,
    /// The source location points to a byte offset within a source file,
    /// offset from 0. The source file is determined contextually.
    /// Inside a `SrcLoc`, the `file_scope` union field will be active.
    byte_abs: u32,
    /// The source location points to a token within a source file,
    /// offset from 0. The source file is determined contextually.
    /// Inside a `SrcLoc`, the `file_scope` union field will be active.
    token_abs: u32,
    /// The source location points to an AST node within a source file,
    /// offset from 0. The source file is determined contextually.
    /// Inside a `SrcLoc`, the `file_scope` union field will be active.
    node_abs: u32,
    /// The source location points to a byte offset within a source file,
    /// offset from the byte offset of the Decl within the file.
    /// The Decl is determined contextually.
    byte_offset: u32,
    /// This data is the offset into the token list from the Decl token.
    /// The Decl is determined contextually.
    token_offset: u32,
    /// The source location points to an AST node, which is this value offset
    /// from its containing Decl node AST index.
    /// The Decl is determined contextually.
    node_offset: TracedOffset,
    /// The source location points to the main token of an AST node, found
    /// by taking this AST node index offset from the containing Decl AST node.
    /// The Decl is determined contextually.
    node_offset_main_token: i32,
    /// The source location points to the beginning of a struct initializer.
    /// The Decl is determined contextually.
    node_offset_initializer: i32,
    /// The source location points to a variable declaration type expression,
    /// found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a variable declaration AST node. Next, navigate
    /// to the type expression.
    /// The Decl is determined contextually.
    node_offset_var_decl_ty: i32,
    /// The source location points to the alignment expression of a var decl.
    /// The Decl is determined contextually.
    node_offset_var_decl_align: i32,
    /// The source location points to the linksection expression of a var decl.
    /// The Decl is determined contextually.
    node_offset_var_decl_section: i32,
    /// The source location points to the addrspace expression of a var decl.
    /// The Decl is determined contextually.
    node_offset_var_decl_addrspace: i32,
    /// The source location points to the initializer of a var decl.
    /// The Decl is determined contextually.
    node_offset_var_decl_init: i32,
    /// The source location points to the first parameter of a builtin
    /// function call, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a builtin call AST node. Next, navigate
    /// to the first parameter.
    /// The Decl is determined contextually.
    node_offset_builtin_call_arg0: i32,
    /// Same as `node_offset_builtin_call_arg0` except arg index 1.
    node_offset_builtin_call_arg1: i32,
    node_offset_builtin_call_arg2: i32,
    node_offset_builtin_call_arg3: i32,
    node_offset_builtin_call_arg4: i32,
    node_offset_builtin_call_arg5: i32,
    /// Like `node_offset_builtin_call_arg0` but recurses through arbitrarily many calls
    /// to pointer cast builtins.
    node_offset_ptrcast_operand: i32,
    /// The source location points to the index expression of an array access
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to an array access AST node. Next, navigate
    /// to the index expression.
    /// The Decl is determined contextually.
    node_offset_array_access_index: i32,
    /// The source location points to the LHS of a slice expression
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a slice AST node. Next, navigate
    /// to the sentinel expression.
    /// The Decl is determined contextually.
    node_offset_slice_ptr: i32,
    /// The source location points to start expression of a slice expression
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a slice AST node. Next, navigate
    /// to the sentinel expression.
    /// The Decl is determined contextually.
    node_offset_slice_start: i32,
    /// The source location points to the end expression of a slice
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a slice AST node. Next, navigate
    /// to the sentinel expression.
    /// The Decl is determined contextually.
    node_offset_slice_end: i32,
    /// The source location points to the sentinel expression of a slice
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a slice AST node. Next, navigate
    /// to the sentinel expression.
    /// The Decl is determined contextually.
    node_offset_slice_sentinel: i32,
    /// The source location points to the callee expression of a function
    /// call expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a function call AST node. Next, navigate
    /// to the callee expression.
    /// The Decl is determined contextually.
    node_offset_call_func: i32,
    /// The payload is offset from the containing Decl AST node.
    /// The source location points to the field name of:
    ///  * a field access expression (`a.b`), or
    ///  * the callee of a method call (`a.b()`)
    /// The Decl is determined contextually.
    node_offset_field_name: i32,
    /// The payload is offset from the containing Decl AST node.
    /// The source location points to the field name of the operand ("b" node)
    /// of a field initialization expression (`.a = b`)
    /// The Decl is determined contextually.
    node_offset_field_name_init: i32,
    /// The source location points to the pointer of a pointer deref expression,
    /// found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a pointer deref AST node. Next, navigate
    /// to the pointer expression.
    /// The Decl is determined contextually.
    node_offset_deref_ptr: i32,
    /// The source location points to the assembly source code of an inline assembly
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to inline assembly AST node. Next, navigate
    /// to the asm template source code.
    /// The Decl is determined contextually.
    node_offset_asm_source: i32,
    /// The source location points to the return type of an inline assembly
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to inline assembly AST node. Next, navigate
    /// to the return type expression.
    /// The Decl is determined contextually.
    node_offset_asm_ret_ty: i32,
    /// The source location points to the condition expression of an if
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to an if expression AST node. Next, navigate
    /// to the condition expression.
    /// The Decl is determined contextually.
    node_offset_if_cond: i32,
    /// The source location points to a binary expression, such as `a + b`, found
    /// by taking this AST node index offset from the containing Decl AST node.
    /// The Decl is determined contextually.
    node_offset_bin_op: i32,
    /// The source location points to the LHS of a binary expression, found
    /// by taking this AST node index offset from the containing Decl AST node,
    /// which points to a binary expression AST node. Next, navigate to the LHS.
    /// The Decl is determined contextually.
    node_offset_bin_lhs: i32,
    /// The source location points to the RHS of a binary expression, found
    /// by taking this AST node index offset from the containing Decl AST node,
    /// which points to a binary expression AST node. Next, navigate to the RHS.
    /// The Decl is determined contextually.
    node_offset_bin_rhs: i32,
    /// The source location points to the operand of a switch expression, found
    /// by taking this AST node index offset from the containing Decl AST node,
    /// which points to a switch expression AST node. Next, navigate to the operand.
    /// The Decl is determined contextually.
    node_offset_switch_operand: i32,
    /// The source location points to the else/`_` prong of a switch expression, found
    /// by taking this AST node index offset from the containing Decl AST node,
    /// which points to a switch expression AST node. Next, navigate to the else/`_` prong.
    /// The Decl is determined contextually.
    node_offset_switch_special_prong: i32,
    /// The source location points to all the ranges of a switch expression, found
    /// by taking this AST node index offset from the containing Decl AST node,
    /// which points to a switch expression AST node. Next, navigate to any of the
    /// range nodes. The error applies to all of them.
    /// The Decl is determined contextually.
    node_offset_switch_range: i32,
    /// The source location points to the capture of a switch_prong.
    /// The Decl is determined contextually.
    node_offset_switch_prong_capture: i32,
    /// The source location points to the tag capture of a switch_prong.
    /// The Decl is determined contextually.
    node_offset_switch_prong_tag_capture: i32,
    /// The source location points to the align expr of a function type
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a function type AST node. Next, navigate to
    /// the calling convention node.
    /// The Decl is determined contextually.
    node_offset_fn_type_align: i32,
    /// The source location points to the addrspace expr of a function type
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a function type AST node. Next, navigate to
    /// the calling convention node.
    /// The Decl is determined contextually.
    node_offset_fn_type_addrspace: i32,
    /// The source location points to the linksection expr of a function type
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a function type AST node. Next, navigate to
    /// the calling convention node.
    /// The Decl is determined contextually.
    node_offset_fn_type_section: i32,
    /// The source location points to the calling convention of a function type
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a function type AST node. Next, navigate to
    /// the calling convention node.
    /// The Decl is determined contextually.
    node_offset_fn_type_cc: i32,
    /// The source location points to the return type of a function type
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a function type AST node. Next, navigate to
    /// the return type node.
    /// The Decl is determined contextually.
    node_offset_fn_type_ret_ty: i32,
    node_offset_param: i32,
    token_offset_param: i32,
    /// The source location points to the type expression of an `anyframe->T`
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to a `anyframe->T` expression AST node. Next, navigate
    /// to the type expression.
    /// The Decl is determined contextually.
    node_offset_anyframe_type: i32,
    /// The source location points to the string literal of `extern "foo"`, found
    /// by taking this AST node index offset from the containing
    /// Decl AST node, which points to a function prototype or variable declaration
    /// expression AST node. Next, navigate to the string literal of the `extern "foo"`.
    /// The Decl is determined contextually.
    node_offset_lib_name: i32,
    /// The source location points to the len expression of an `[N:S]T`
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
    /// to the len expression.
    /// The Decl is determined contextually.
    node_offset_array_type_len: i32,
    /// The source location points to the sentinel expression of an `[N:S]T`
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
    /// to the sentinel expression.
    /// The Decl is determined contextually.
    node_offset_array_type_sentinel: i32,
    /// The source location points to the elem expression of an `[N:S]T`
    /// expression, found by taking this AST node index offset from the containing
    /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
    /// to the elem expression.
    /// The Decl is determined contextually.
    node_offset_array_type_elem: i32,
    /// The source location points to the operand of an unary expression.
    /// The Decl is determined contextually.
    node_offset_un_op: i32,
    /// The source location points to the elem type of a pointer.
    /// The Decl is determined contextually.
    node_offset_ptr_elem: i32,
    /// The source location points to the sentinel of a pointer.
    /// The Decl is determined contextually.
    node_offset_ptr_sentinel: i32,
    /// The source location points to the align expr of a pointer.
    /// The Decl is determined contextually.
    node_offset_ptr_align: i32,
    /// The source location points to the addrspace expr of a pointer.
    /// The Decl is determined contextually.
    node_offset_ptr_addrspace: i32,
    /// The source location points to the bit-offset of a pointer.
    /// The Decl is determined contextually.
    node_offset_ptr_bitoffset: i32,
    /// The source location points to the host size of a pointer.
    /// The Decl is determined contextually.
    node_offset_ptr_hostsize: i32,
    /// The source location points to the tag type of an union or an enum.
    /// The Decl is determined contextually.
    node_offset_container_tag: i32,
    /// The source location points to the default value of a field.
    /// The Decl is determined contextually.
    node_offset_field_default: i32,
    /// The source location points to the type of an array or struct initializer.
    /// The Decl is determined contextually.
    node_offset_init_ty: i32,
    /// The source location points to the LHS of an assignment.
    /// The Decl is determined contextually.
    node_offset_store_ptr: i32,
    /// The source location points to the RHS of an assignment.
    /// The Decl is determined contextually.
    node_offset_store_operand: i32,
    /// The source location points to the operand of a `return` statement, or
    /// the `return` itself if there is no explicit operand.
    /// The Decl is determined contextually.
    node_offset_return_operand: i32,
    /// The source location points to a for loop input.
    /// The Decl is determined contextually.
    for_input: struct {
        /// Points to the for loop AST node.
        for_node_offset: i32,
        /// Picks one of the inputs from the condition.
        input_index: u32,
    },
    /// The source location points to one of the captures of a for loop, found
    /// by taking this AST node index offset from the containing
    /// Decl AST node, which points to one of the input nodes of a for loop.
    /// Next, navigate to the corresponding capture.
    /// The Decl is determined contextually.
    for_capture_from_input: i32,
    /// The source location points to the argument node of a function call.
    call_arg: struct {
        decl: DeclIndex,
        /// Points to the function call AST node.
        call_node_offset: i32,
        /// The index of the argument the source location points to.
        arg_index: u32,
    },
    fn_proto_param: struct {
        decl: DeclIndex,
        /// Points to the function prototype AST node.
        fn_proto_node_offset: i32,
        /// The index of the parameter the source location points to.
        param_index: u32,
    },
    array_cat_lhs: ArrayCat,
    array_cat_rhs: ArrayCat,

isValidId()

The source location points to the sentinel of a pointer. The Decl is determined contextually.


    const ArrayCat = struct {
        /// Points to the array concat AST node.
        array_cat_offset: i32,
        /// The index of the element the source location points to.
        elem_index: u32,
    };

Test: isValidId

The source location points to the align expr of a pointer. The Decl is determined contextually.


    pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;

isUnderscore()

The source location points to the addrspace expr of a pointer. The Decl is determined contextually.


    noinline fn nodeOffsetDebug(node_offset: i32) LazySrcLoc {
        var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } };
        result.node_offset.trace.addAddr(@returnAddress(), "init");
        return result;
    }

Test: isUnderscore

The source location points to the bit-offset of a pointer. The Decl is determined contextually.


    fn nodeOffsetRelease(node_offset: i32) LazySrcLoc {
        return .{ .node_offset = .{ .x = node_offset } };
    }

readSourceFileToEndAlloc()

The source location points to the host size of a pointer. The Decl is determined contextually.


    /// This wraps a simple integer in debug builds so that later on we can find out
    /// where in semantic analysis the value got set.
    pub const TracedOffset = struct {
        x: i32,
        trace: std.debug.Trace = std.debug.Trace.init,

printAstErrorsToStderr()

The source location points to the tag type of an union or an enum. The Decl is determined contextually.


        const want_tracing = false;
    };
};

putAstErrorsIntoBundle()

The source location points to the default value of a field. The Decl is determined contextually.


const std = @import("std.zig");
const tokenizer = @import("zig/tokenizer.zig");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;

resolveTargetQueryOrFatal()

The source location points to the type of an array or struct initializer. The Decl is determined contextually.


/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
///
/// - An empty `{}` format specifier escapes invalid identifiers, identifiers that shadow primitives
///   and the reserved `_` identifier.
/// - Add `p` to the specifier to render identifiers that shadow primitives unescaped.
/// - Add `_` to the specifier to render the reserved `_` identifier unescaped.
/// - `p` and `_` can be combined, e.g. `{p_}`.
///
pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) {
    return .{ .data = bytes };
}

parseTargetQueryOrReportFatalError()

The source location points to the LHS of an assignment. The Decl is determined contextually.


test fmtId {
    const expectFmt = std.testing.expectFmt;
    try expectFmt("@\"while\"", "{}", .{fmtId("while")});
    try expectFmt("@\"while\"", "{p}", .{fmtId("while")});
    try expectFmt("@\"while\"", "{_}", .{fmtId("while")});
    try expectFmt("@\"while\"", "{p_}", .{fmtId("while")});
    try expectFmt("@\"while\"", "{_p}", .{fmtId("while")});

fatal()

The source location points to the RHS of an assignment. The Decl is determined contextually.


    try expectFmt("hello", "{}", .{fmtId("hello")});
    try expectFmt("hello", "{p}", .{fmtId("hello")});
    try expectFmt("hello", "{_}", .{fmtId("hello")});
    try expectFmt("hello", "{p_}", .{fmtId("hello")});
    try expectFmt("hello", "{_p}", .{fmtId("hello")});

EnvVar

The source location points to the operand of a return statement, or the return itself if there is no explicit operand. The Decl is determined contextually.


    try expectFmt("@\"type\"", "{}", .{fmtId("type")});
    try expectFmt("type", "{p}", .{fmtId("type")});
    try expectFmt("@\"type\"", "{_}", .{fmtId("type")});
    try expectFmt("type", "{p_}", .{fmtId("type")});
    try expectFmt("type", "{_p}", .{fmtId("type")});

isSet()

The source location points to a for loop input. The Decl is determined contextually.


    try expectFmt("@\"_\"", "{}", .{fmtId("_")});
    try expectFmt("@\"_\"", "{p}", .{fmtId("_")});
    try expectFmt("_", "{_}", .{fmtId("_")});
    try expectFmt("_", "{p_}", .{fmtId("_")});
    try expectFmt("_", "{_p}", .{fmtId("_")});

get()

Points to the for loop AST node.


    try expectFmt("@\"i123\"", "{}", .{fmtId("i123")});
    try expectFmt("i123", "{p}", .{fmtId("i123")});
    try expectFmt("@\"4four\"", "{}", .{fmtId("4four")});
    try expectFmt("_underscore", "{}", .{fmtId("_underscore")});
    try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")});
    try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")});

getPosix()

Picks one of the inputs from the condition.


    // These are technically not currently legal in Zig.
    try expectFmt("@\"\"", "{}", .{fmtId("")});
    try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")});
}

/// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.
fn formatId(
    bytes: []const u8,
    comptime fmt: []const u8,
    options: std.fmt.FormatOptions,
    writer: anytype,
) !void {
    const allow_primitive, const allow_underscore = comptime parse_fmt: {
        var allow_primitive = false;
        var allow_underscore = false;
        for (fmt) |char| {
            switch (char) {
                'p' => if (!allow_primitive) {
                    allow_primitive = true;
                    continue;
                },
                '_' => if (!allow_underscore) {
                    allow_underscore = true;
                    continue;
                },
                else => {},
            }
            @compileError("expected {}, {p}, {_}, {p_} or {_p}, found {" ++ fmt ++ "}");
        }
        break :parse_fmt .{ allow_primitive, allow_underscore };
    };

    if (isValidId(bytes) and
        (allow_primitive or !std.zig.isPrimitive(bytes)) and
        (allow_underscore or !isUnderscore(bytes)))
    {
        return writer.writeAll(bytes);
    }
    try writer.writeAll("@\"");
    try stringEscape(bytes, "", options, writer);
    try writer.writeByte('"');
}

/// Return a Formatter for Zig Escapes of a double quoted string.
/// The format specifier must be one of:
///  * `{}` treats contents as a double-quoted string.
///  * `{'}` treats contents as a single-quoted string.
pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) {
    return .{ .data = bytes };
}

test fmtEscapes {
    const expectFmt = std.testing.expectFmt;
    try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")});
    try expectFmt(
        \\" \\ hi \x07 \x11 " derp \'"
    , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
    try expectFmt(
        \\" \\ hi \x07 \x11 \" derp '"
    , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
}

/// Print the string as escaped contents of a double quoted or single-quoted string.
/// Format `{}` treats contents as a double-quoted string.
/// Format `{'}` treats contents as a single-quoted string.
pub fn stringEscape(
    bytes: []const u8,
    comptime f: []const u8,
    options: std.fmt.FormatOptions,
    writer: anytype,
) !void {
    _ = options;
    for (bytes) |byte| switch (byte) {
        '\n' => try writer.writeAll("\\n"),
        '\r' => try writer.writeAll("\\r"),
        '\t' => try writer.writeAll("\\t"),
        '\\' => try writer.writeAll("\\\\"),
        '"' => {
            if (f.len == 1 and f[0] == '\'') {
                try writer.writeByte('"');
            } else if (f.len == 0) {
                try writer.writeAll("\\\"");
            } else {
                @compileError("expected {} or {'}, found {" ++ f ++ "}");
            }
        },
        '\'' => {
            if (f.len == 1 and f[0] == '\'') {
                try writer.writeAll("\\'");
            } else if (f.len == 0) {
                try writer.writeByte('\'');
            } else {
                @compileError("expected {} or {'}, found {" ++ f ++ "}");
            }
        },
        ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
        // Use hex escapes for rest any unprintable characters.
        else => {
            try writer.writeAll("\\x");
            try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer);
        },
    };
}

pub fn isValidId(bytes: []const u8) bool {
    if (bytes.len == 0) return false;
    for (bytes, 0..) |c, i| {
        switch (c) {
            '_', 'a'...'z', 'A'...'Z' => {},
            '0'...'9' => if (i == 0) return false,
            else => return false,
        }
    }
    return std.zig.Token.getKeyword(bytes) == null;
}

test isValidId {
    try std.testing.expect(!isValidId(""));
    try std.testing.expect(isValidId("foobar"));
    try std.testing.expect(!isValidId("a b c"));
    try std.testing.expect(!isValidId("3d"));
    try std.testing.expect(!isValidId("enum"));
    try std.testing.expect(isValidId("i386"));
}

pub fn isUnderscore(bytes: []const u8) bool {
    return bytes.len == 1 and bytes[0] == '_';
}

test isUnderscore {
    try std.testing.expect(isUnderscore("_"));
    try std.testing.expect(!isUnderscore("__"));
    try std.testing.expect(!isUnderscore("_foo"));
    try std.testing.expect(isUnderscore("\x5f"));
    try std.testing.expect(!isUnderscore("\\x5f"));
}

pub fn readSourceFileToEndAlloc(
    allocator: Allocator,
    input: std.fs.File,
    size_hint: ?usize,
) ![:0]u8 {
    const source_code = input.readToEndAllocOptions(
        allocator,
        max_src_size,
        size_hint,
        @alignOf(u16),
        0,
    ) catch |err| switch (err) {
        error.ConnectionResetByPeer => unreachable,
        error.ConnectionTimedOut => unreachable,
        error.NotOpenForReading => unreachable,
        else => |e| return e,
    };
    errdefer allocator.free(source_code);

    // Detect unsupported file types with their Byte Order Mark
    const unsupported_boms = [_][]const u8{
        "\xff\xfe\x00\x00", // UTF-32 little endian
        "\xfe\xff\x00\x00", // UTF-32 big endian
        "\xfe\xff", // UTF-16 big endian
    };
    for (unsupported_boms) |bom| {
        if (std.mem.startsWith(u8, source_code, bom)) {
            return error.UnsupportedEncoding;
        }
    }

    // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
    if (std.mem.startsWith(u8, source_code, "\xff\xfe")) {
        const source_code_utf16_le = std.mem.bytesAsSlice(u16, source_code);
        const source_code_utf8 = std.unicode.utf16LeToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) {
            error.DanglingSurrogateHalf => error.UnsupportedEncoding,
            error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
            error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
            else => |e| return e,
        };

        allocator.free(source_code);
        return source_code_utf8;
    }

    return source_code;
}

pub fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
    var wip_errors: std.zig.ErrorBundle.Wip = undefined;
    try wip_errors.init(gpa);
    defer wip_errors.deinit();

    try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);

    var error_bundle = try wip_errors.toOwnedBundle("");
    defer error_bundle.deinit(gpa);
    error_bundle.renderToStdErr(color.renderOptions());
}

pub fn putAstErrorsIntoBundle(
    gpa: Allocator,
    tree: Ast,
    path: []const u8,
    wip_errors: *std.zig.ErrorBundle.Wip,
) Allocator.Error!void {
    var zir = try AstGen.generate(gpa, tree);
    defer zir.deinit(gpa);

    try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
}

pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
    return std.zig.system.resolveTargetQuery(target_query) catch |err|
        fatal("unable to resolve target: {s}", .{@errorName(err)});
}

pub fn parseTargetQueryOrReportFatalError(
    allocator: Allocator,
    opts: std.Target.Query.ParseOptions,
) std.Target.Query {
    var opts_with_diags = opts;
    var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
    if (opts_with_diags.diagnostics == null) {
        opts_with_diags.diagnostics = &diags;
    }
    return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
        error.UnknownCpuModel => {
            help: {
                var help_text = std.ArrayList(u8).init(allocator);
                defer help_text.deinit();
                for (diags.arch.?.allCpuModels()) |cpu| {
                    help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
                }
                std.log.info("available CPUs for architecture '{s}':\n{s}", .{
                    @tagName(diags.arch.?), help_text.items,
                });
            }
            fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
        },
        error.UnknownCpuFeature => {
            help: {
                var help_text = std.ArrayList(u8).init(allocator);
                defer help_text.deinit();
                for (diags.arch.?.allFeaturesList()) |feature| {
                    help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
                }
                std.log.info("available CPU features for architecture '{s}':\n{s}", .{
                    @tagName(diags.arch.?), help_text.items,
                });
            }
            fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
        },
        error.UnknownObjectFormat => {
            help: {
                var help_text = std.ArrayList(u8).init(allocator);
                defer help_text.deinit();
                inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| {
                    help_text.writer().print(" {s}\n", .{field.name}) catch break :help;
                }
                std.log.info("available object formats:\n{s}", .{help_text.items});
            }
            fatal("unknown object format: '{s}'", .{opts.object_format.?});
        },
        else => |e| fatal("unable to parse target query '{s}': {s}", .{
            opts.arch_os_abi, @errorName(e),
        }),
    };
}

pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
    std.log.err(format, args);
    std.process.exit(1);
}

/// Collects all the environment variables that Zig could possibly inspect, so
/// that we can do reflection on this and print them with `zig env`.
pub const EnvVar = enum {
    ZIG_GLOBAL_CACHE_DIR,
    ZIG_LOCAL_CACHE_DIR,
    ZIG_LIB_DIR,
    ZIG_LIBC,
    ZIG_BUILD_RUNNER,
    ZIG_VERBOSE_LINK,
    ZIG_VERBOSE_CC,
    ZIG_BTRFS_WORKAROUND,
    ZIG_DEBUG_CMD,
    CC,
    NO_COLOR,
    CLICOLOR_FORCE,
    XDG_CACHE_HOME,
    HOME,

    pub fn isSet(comptime ev: EnvVar) bool {
        return std.process.hasEnvVarConstant(@tagName(ev));
    }

    pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 {
        if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
            return value;
        } else |err| switch (err) {
            error.EnvironmentVariableNotFound => return null,
            else => |e| return e,
        }
    }

    pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
        return std.posix.getenvZ(@tagName(ev));
    }
};

test {
    _ = Ast;
    _ = AstRlAnnotate;
    _ = BuiltinFn;
    _ = Client;
    _ = ErrorBundle;
    _ = LibCDirs;
    _ = LibCInstallation;
    _ = Server;
    _ = WindowsSdk;
    _ = number_literal;
    _ = primitives;
    _ = string_literal;
    _ = system;
    _ = target;
    _ = c_translation;
}