From a4433d88b0a05b64a536a242d5d51d3371477e52 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Thu, 16 Jul 2026 20:18:39 -0700 Subject: [PATCH 01/26] Fix parsing into align pointers --- lib/std/zon/parse.zig | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 16294f3df6d763df2587b48fbb99e3b6615d478f..a56e2895aec1f23c0086b2306d0ae295df49aee7 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -518,8 +518,13 @@ const Parser = struct { .@"enum" => return self.parseEnumLiteral(T, node), .pointer => |pointer| switch (pointer.size) { .one => { - const result = try self.gpa.create(pointer.child); - errdefer self.gpa.destroy(result); + const slice = try self.gpa.alignedAlloc( + pointer.child, + if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, + 1, + ); + errdefer self.gpa.free(slice); + const result = &slice[0]; result.* = try self.parseExprInner(pointer.child, node); return result; }, @@ -3568,3 +3573,25 @@ test "std.zon errors without diagnostics" { try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".a", null, .{})); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".{ .b = 8 }", null, .{})); } + +test "std.zon aligned pointers" { + const gpa = std.testing.allocator; + + const n: u8 align(8) = 10; + const Foo = struct { + inner: *align(8) const u8, + }; + + const expected: Foo = .{ + .inner = &n, + }; + const found = try fromSliceAlloc( + Foo, + gpa, + ".{ .inner = 10 }", + null, + .{}, + ); + defer free(gpa, found); + try std.testing.expectEqualDeep(expected.inner, found.inner); +} -- 2.54.0 From db34b1f9ab306ab0568ad6e7409c0c577fb1e0e6 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Thu, 16 Jul 2026 20:21:54 -0700 Subject: [PATCH 02/26] Parse nontrivial types into out pointer internally This doesn't change any functionality, but it's a step towards supporting an update style API. --- lib/std/zon/parse.zig | 137 +++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 67 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index a56e2895aec1f23c0086b2306d0ae295df49aee7..b200d5206ee18423d614d5d64ab36697b084ab7f 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -398,7 +398,9 @@ pub fn fromZoirNodeAlloc( .diag = diag, }; - return parser.parseExpr(T, node); + var out: T = undefined; + try parser.parseExprInto(node, &out); + return out; } /// Frees ZON values. @@ -488,34 +490,36 @@ const Parser = struct { const ParseExprError = error{ ParseZon, OutOfMemory }; - fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprError!T { - return self.parseExprInner(T, node) catch |err| switch (err) { - error.WrongType => return self.failExpectedType(T, node), + fn parseExprInto(self: *@This(), node: Zoir.Node.Index, out: anytype) ParseExprError!void { + return self.parseExprInnerInto(node, out) catch |err| switch (err) { + error.WrongType => return self.failExpectedType(@TypeOf(out.*), node), else => |e| return e, }; } const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType }; - fn parseExprInner( + fn parseExprInnerInto( self: *@This(), - T: type, node: Zoir.Node.Index, - ) ParseExprInnerError!T { - if (T == Zoir.Node.Index) { - return node; + out: anytype, + ) ParseExprInnerError!void { + if (@TypeOf(out.*) == Zoir.Node.Index) { + out.* = node; + return; } - switch (@typeInfo(T)) { + switch (@typeInfo(@TypeOf(out.*))) { .optional => |optional| if (node.get(self.zoir) == .null) { - return null; + out.* = null; } else { - return try self.parseExprInner(optional.child, node); + out.* = @as(optional.child, undefined); // Make non-null + try self.parseExprInnerInto(node, &out.*.?); // Parse value into non-null pointer }, - .bool => return self.parseBool(node), - .int => return self.parseInt(T, node), - .float => return self.parseFloat(T, node), - .@"enum" => return self.parseEnumLiteral(T, node), + .bool => out.* = try self.parseBool(node), + .int => out.* = try self.parseInt(@TypeOf(out.*), node), + .float => out.* = try self.parseFloat(@TypeOf(out.*), node), + .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), .pointer => |pointer| switch (pointer.size) { .one => { const slice = try self.gpa.alignedAlloc( @@ -524,23 +528,20 @@ const Parser = struct { 1, ); errdefer self.gpa.free(slice); - const result = &slice[0]; - result.* = try self.parseExprInner(pointer.child, node); - return result; + const ptr = &slice[0]; + try self.parseExprInnerInto(node, ptr); + out.* = ptr; }, - .slice => return self.parseSlicePointer(T, node), + .slice => out.* = try self.parseSlicePointer(@TypeOf(out.*), node), else => comptime unreachable, }, - .array => return self.parseArray(T, node), - .vector => |vector| { - const A = [vector.len]vector.child; - return try self.parseArray(A, node); - }, + .array => try self.parseArrayInto(node, out), + .vector => try self.parseVectorInto(node, out), .@"struct" => |@"struct"| if (@"struct".is_tuple) - return self.parseTuple(T, node) + try self.parseTupleInto(node, out) else - return self.parseStruct(T, node), - .@"union" => return self.parseUnion(T, node), + try self.parseStructInto(node, out), + .@"union" => try self.parseUnionInto(node, out), else => comptime unreachable, } @@ -758,53 +759,61 @@ const Parser = struct { free(self.gpa, item); } }; - elem.* = try self.parseExpr(pointer.child, nodes.at(@intCast(i))); + try self.parseExprInto(nodes.at(@intCast(i)), elem); } return slice; } - fn parseArray(self: *@This(), T: type, node: Zoir.Node.Index) !T { + fn parseArrayInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, }; - const array_info = @typeInfo(T).array; + const array = @typeInfo(@TypeOf(out.*)).array; // Check if the size matches - if (nodes.len < array_info.len) { + if (nodes.len < array.len) { return self.failNodeFmt( node, "expected {} array elements; found {}", - .{ array_info.len, nodes.len }, + .{ array.len, nodes.len }, ); - } else if (nodes.len > array_info.len) { + } else if (nodes.len > array.len) { return self.failNodeFmt( - nodes.at(array_info.len), + nodes.at(array.len), "index {} outside of array of length {}", - .{ array_info.len, array_info.len }, + .{ array.len, array.len }, ); } // Parse the elements and return the array - var result: T = undefined; - for (&result, 0..) |*elem, i| { + inline for (out, 0..) |*elem, i| { // If we fail to parse this field, free all fields before it errdefer if (self.options.free_on_error) { - for (result[0..i]) |item| { + for (out[0..i]) |item| { free(self.gpa, item); } }; - elem.* = try self.parseExpr(array_info.child, nodes.at(@intCast(i))); + try self.parseExprInto(nodes.at(@intCast(i)), elem); } - if (array_info.sentinel()) |s| result[result.len] = s; - return result; + + if (array.sentinel()) |s| { + out[array.len] = s; + } + } + + fn parseVectorInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { + const vector = @typeInfo(@TypeOf(out.*)).vector; + var array: [vector.len]vector.child = undefined; + try self.parseArrayInto(node, &array); + out.* = array; } - fn parseStruct(self: *@This(), T: type, node: Zoir.Node.Index) !T { + fn parseStructInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { const repr = node.get(self.zoir); const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { .struct_literal => |nodes| nodes, @@ -812,7 +821,7 @@ const Parser = struct { else => return error.WrongType, }; - const info = @typeInfo(T).@"struct"; + const info = @typeInfo(@TypeOf(out.*)).@"struct"; // Build a map from field name to index. // The special value `comptime_field` indicates that this is actually a comptime field. @@ -826,7 +835,6 @@ const Parser = struct { }; // Parse the struct - var result: T = undefined; var field_found: [info.field_names.len]bool = @splat(false); // If we fail partway through, free all already initialized fields @@ -836,7 +844,7 @@ const Parser = struct { switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) { inline 0...(info.field_names.len - 1) => |name_index| { const name = info.field_names[name_index]; - free(self.gpa, @field(result, name)); + free(self.gpa, @field(out, name)); }, else => unreachable, // Can't be out of bounds } @@ -848,7 +856,7 @@ const Parser = struct { const name = fields.names[i].get(self.zoir); const field_index = field_indices.get(name) orelse { if (self.options.ignore_unknown_fields) continue; - return self.failUnexpected(T, "field", node, i, name); + return self.failUnexpected(@TypeOf(out.*), "field", node, i, name); }; if (field_index == comptime_field) { return self.failComptimeField(node, i); @@ -863,9 +871,9 @@ const Parser = struct { inline 0...(info.field_names.len - 1) => |j| { if (info.field_attrs[j].@"comptime") unreachable; - @field(result, info.field_names[j]) = try self.parseExpr( - info.field_types[j], + try self.parseExprInto( fields.vals.at(@intCast(i)), + &@field(out, info.field_names[j]), ); }, else => unreachable, // Can't be out of bounds @@ -879,7 +887,7 @@ const Parser = struct { if (!found) { const field_attrs = info.field_attrs[i]; if (field_attrs.defaultValue(info.field_types[i])) |default| { - @field(result, info.field_names[i]) = default; + @field(out, info.field_names[i]) = default; } else { return self.failNodeFmt( node, @@ -889,19 +897,16 @@ const Parser = struct { } } } - - return result; } - fn parseTuple(self: *@This(), T: type, node: Zoir.Node.Index) !T { + fn parseTupleInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, }; - var result: T = undefined; - const info = @typeInfo(T).@"struct"; + const info = @typeInfo(@TypeOf(out.*)).@"struct"; if (nodes.len > info.field_names.len) { return self.failNodeFmt( @@ -915,7 +920,7 @@ const Parser = struct { // Check if we're out of bounds if (i >= nodes.len) { if (info.field_attrs[i].defaultValue(info.field_types[i])) |default| { - @field(result, info.field_names[i]) = default; + @field(out.*, info.field_names[i]) = default; } else { return self.failNodeFmt(node, "missing tuple field with index {}", .{i}); } @@ -924,23 +929,21 @@ const Parser = struct { errdefer if (self.options.free_on_error) { inline for (0..i) |j| { if (j >= i) break; - free(self.gpa, result[j]); + free(self.gpa, out[j]); } }; if (info.field_attrs[i].@"comptime") { return self.failComptimeField(node, i); } else { - result[i] = try self.parseExpr(info.field_types[i], nodes.at(i)); + try self.parseExprInto(nodes.at(i), &out[i]); } } } - - return result; } - fn parseUnion(self: *@This(), T: type, node: Zoir.Node.Index) !T { - const @"union" = @typeInfo(T).@"union"; + fn parseUnionInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { + const @"union" = @typeInfo(@TypeOf(out.*)).@"union"; if (@"union".field_names.len == 0) comptime unreachable; @@ -966,7 +969,7 @@ const Parser = struct { const field_index = b: { const field_name_str = field_name.get(self.zoir); break :b field_indices.get(field_name_str) orelse - return self.failUnexpected(T, "field", node, null, field_name_str); + return self.failUnexpected(@TypeOf(out.*), "field", node, null, field_name_str); }; // Initialize the union from the given field. @@ -977,7 +980,7 @@ const Parser = struct { return self.failNode(node, "expected union"); // Instantiate the union - return @unionInit(T, @"union".field_names[i], {}); + out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], {}); }, else => unreachable, // Can't be out of bounds } @@ -992,15 +995,15 @@ const Parser = struct { const field_name_str = field_name.get(self.zoir); const field_val = struct_fields.vals.at(0); const field_index = field_indices.get(field_name_str) orelse - return self.failUnexpected(T, "field", node, 0, field_name_str); + return self.failUnexpected(@TypeOf(out.*), "field", node, 0, field_name_str); switch (field_index) { inline 0...@"union".field_names.len - 1 => |i| { if (@"union".field_types[i] == void) { return self.failNode(field_val, "expected type 'void'"); } else { - const value = try self.parseExpr(@"union".field_types[i], field_val); - return @unionInit(T, @"union".field_names[i], value); + out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined); + try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i])); } }, else => unreachable, // Can't be out of bounds -- 2.54.0 From 9ef02702cebc0de136384f0b7dc9d19aff13f27b Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Fri, 17 Jul 2026 01:34:34 -0700 Subject: [PATCH 03/26] Add `updateFrom*` variants to parse functions --- lib/std/zon/parse.zig | 461 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 421 insertions(+), 40 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index b200d5206ee18423d614d5d64ab36697b084ab7f..b57e6284e27fa56ef0845f20bb27934f078dff44 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -6,6 +6,8 @@ //! * `fromZoir`/`fromZoirAlloc` //! * `fromZoirNode`/`fromZoirNodeAlloc` //! +//! To update an existing values, see the `updateFrom*` variants. +//! //! For lower level control over parsing, see `std.zig.Zoir`. const std = @import("std"); @@ -23,14 +25,39 @@ const ArrayList = std.ArrayList; /// Rename when adding or removing support for a type. const valid_types = {}; -/// Configuration for the runtime parser. -pub const Options = struct { +/// Deprecated. +pub const Options = FromOptions; + +/// Configuration for the `from*` functions. +pub const FromOptions = struct { /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, /// If true, the parser cleans up partially parsed values on error. This requires some extra /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because /// you're using arena allocation.) free_on_error: bool = true, + + fn toParserOptions(self: FromOptions) Parser.Options { + return .{ + .ignore_unknown_fields = self.ignore_unknown_fields, + .ignore_missing_fields = false, + .free_on_error = self.free_on_error, + }; + } +}; + +/// Configuration for the `updateFrom*` variants. +pub const UpdateFromOptions = struct { + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, + + fn toParserOptions(self: UpdateFromOptions) Parser.Options { + return .{ + .ignore_unknown_fields = self.ignore_unknown_fields, + .ignore_missing_fields = true, + .free_on_error = false, + }; + } }; pub const Error = union(enum) { @@ -260,50 +287,92 @@ pub const Diagnostics = struct { /// /// An allocator is still required for temporary allocations made during parsing. pub fn fromSlice( + /// The type to deserialize into. Only the following types are supported, unsupported types will + /// result in a compiler error: + /// + /// * Optionals of supported types + /// * Booleans + /// * Integers except for `comptime_int` + /// * Floats except for `comptime_float` + /// * Enums + /// * Single item pointers to supported types + /// * Slices of supported types + /// * Arrays of supported types + /// * Vectors of supported types + /// * Structures of supported types + /// * Tuples of supported types + /// * Unions T: type, gpa: Allocator, source: [:0]const u8, diag: ?*Diagnostics, - options: Options, + options: FromOptions, ) error{ OutOfMemory, ParseZon }!T { comptime assert(!requiresAllocator(T)); return fromSliceAlloc(T, gpa, source, diag, options); } +/// Similar to `fromSlice`, but updates an existing value `current`. Fields not specified by ZON are +/// left at their current values. Pointers are replaced with new values not mutated in place. +pub fn updateFromSlice( + T: type, + gpa: Allocator, + current: *T, + source: [:0]const u8, + diag: ?*Diagnostics, + options: UpdateFromOptions, +) error{ OutOfMemory, ParseZon }!void { + comptime assert(!requiresAllocator(T)); + try updateFromSliceAlloc(T, gpa, current, source, diag, options); +} + /// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see /// `free`. pub fn fromSliceAlloc( - /// The type to deserialize into. May not be or contain any of the following types: - /// * Any comptime-only type, except in a comptime field - /// * `type` - /// * `void`, except as a union payload - /// * `noreturn` - /// * An error set/error union - /// * A many-pointer or C-pointer - /// * An opaque type, including `anyopaque` - /// * An async frame type, including `anyframe` and `anyframe->T` - /// * A function - /// - /// All other types are valid. Unsupported types will fail at compile time. T: type, gpa: Allocator, source: [:0]const u8, diag: ?*Diagnostics, - options: Options, + options: FromOptions, ) error{ OutOfMemory, ParseZon }!T { + var result: T = undefined; + try parseSliceAlloc(T, &result, gpa, source, diag, options.toParserOptions()); + return result; +} + +/// Like updateFromSlice`, but the result may contain pointers. Requires an arena for cleanup. +pub fn updateFromSliceAlloc( + T: type, + arena: Allocator, + current: *T, + source: [:0]const u8, + diag: ?*Diagnostics, + options: UpdateFromOptions, +) error{ OutOfMemory, ParseZon }!void { + try parseSliceAlloc(T, current, arena, source, diag, options.toParserOptions()); +} + +fn parseSliceAlloc( + T: type, + current: *T, + arena: Allocator, + source: [:0]const u8, + diag: ?*Diagnostics, + options: Parser.Options, +) error{ OutOfMemory, ParseZon }!void { if (diag) |s| s.assertEmpty(); - var ast = try std.zig.Ast.parse(gpa, source, .{ .mode = .zon }); - defer if (diag == null) ast.deinit(gpa); + var ast = try std.zig.Ast.parse(arena, source, .{ .mode = .zon }); + defer if (diag == null) ast.deinit(arena); if (diag) |s| s.ast = ast; // If there's no diagnostics, Zoir exists for the lifetime of this function. If there is a // diagnostics, ownership is transferred to diagnostics. - var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); - defer if (diag == null) zoir.deinit(gpa); + var zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); + defer if (diag == null) zoir.deinit(arena); if (diag) |s| s.* = .{}; - return fromZoirAlloc(T, gpa, ast, zoir, diag, options); + try parseZoirAlloc(T, arena, current, ast, zoir, diag, options); } /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. @@ -312,7 +381,7 @@ pub fn fromZoir( ast: Ast, zoir: Zoir, diag: ?*Diagnostics, - options: Options, + options: FromOptions, ) error{ParseZon}!T { comptime assert(!requiresAllocator(T)); var buf: [0]u8 = .{}; @@ -330,6 +399,32 @@ pub fn fromZoir( }; } +/// Like `updateSlice`, but operates on `Zoir` instead of ZON source. +pub fn updateFromZoir( + T: type, + current: *T, + ast: Ast, + zoir: Zoir, + diag: ?*Diagnostics, + options: UpdateFromOptions, +) error{ParseZon}!void { + comptime assert(!requiresAllocator(T)); + var buf: [0]u8 = .{}; + var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); + updateFromZoirAlloc( + T, + failing_allocator.allocator(), + current, + ast, + zoir, + diag, + options, + ) catch |err| switch (err) { + error.OutOfMemory => unreachable, // Checked by comptime assertion above + else => |e| return e, + }; +} + /// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source. pub fn fromZoirAlloc( T: type, @@ -337,11 +432,36 @@ pub fn fromZoirAlloc( ast: Ast, zoir: Zoir, diag: ?*Diagnostics, - options: Options, + options: FromOptions, ) error{ OutOfMemory, ParseZon }!T { return fromZoirNodeAlloc(T, gpa, ast, zoir, .root, diag, options); } +/// Like updateSliceAlloc`, but operates on `Zoir` instead of ZON source. +pub fn updateFromZoirAlloc( + T: type, + arena: Allocator, + current: *T, + ast: Ast, + zoir: Zoir, + diag: ?*Diagnostics, + options: UpdateFromOptions, +) error{ OutOfMemory, ParseZon }!void { + try parseZoirAlloc(T, arena, current, ast, zoir, diag, options.toParserOptions()); +} + +fn parseZoirAlloc( + T: type, + gpa: Allocator, + current: *T, + ast: Ast, + zoir: Zoir, + diag: ?*Diagnostics, + options: Parser.Options, +) error{ OutOfMemory, ParseZon }!void { + try parseZoirNodeAlloc(T, gpa, current, ast, zoir, .root, diag, options); +} + /// Like `fromZoir`, but the parse starts at `node` instead of root. pub fn fromZoirNode( T: type, @@ -349,7 +469,7 @@ pub fn fromZoirNode( zoir: Zoir, node: Zoir.Node.Index, diag: ?*Diagnostics, - options: Options, + options: FromOptions, ) error{ParseZon}!T { comptime assert(!requiresAllocator(T)); var buf: [0]u8 = .{}; @@ -368,6 +488,34 @@ pub fn fromZoirNode( }; } +/// Like `updateZoir`, but the parse starts at `node` instead of root. +pub fn updateFromZoirNode( + T: type, + current: *T, + ast: Ast, + zoir: Zoir, + node: Zoir.Node.Index, + diag: ?*Diagnostics, + options: UpdateFromOptions, +) error{ParseZon}!void { + comptime assert(!requiresAllocator(T)); + var buf: [0]u8 = .{}; + var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); + updateFromZoirNodeAlloc( + T, + failing_allocator.allocator(), + current, + ast, + zoir, + node, + diag, + options, + ) catch |err| switch (err) { + error.OutOfMemory => unreachable, // Checked by comptime assertion above + else => |e| return e, + }; +} + /// Like `fromZoirAlloc`, but the parse starts at `node` instead of root. pub fn fromZoirNodeAlloc( T: type, @@ -376,8 +524,37 @@ pub fn fromZoirNodeAlloc( zoir: Zoir, node: Zoir.Node.Index, diag: ?*Diagnostics, - options: Options, + options: FromOptions, ) error{ OutOfMemory, ParseZon }!T { + var out: T = undefined; + try parseZoirNodeAlloc(T, gpa, &out, ast, zoir, node, diag, options.toParserOptions()); + return out; +} + +/// Like `updateZoirNodeAlloc`, but the parse starts at `node` instead of root. +pub fn updateFromZoirNodeAlloc( + T: type, + arena: Allocator, + current: *T, + ast: Ast, + zoir: Zoir, + node: Zoir.Node.Index, + diag: ?*Diagnostics, + options: UpdateFromOptions, +) error{ OutOfMemory, ParseZon }!void { + return parseZoirNodeAlloc(T, arena, current, ast, zoir, node, diag, options.toParserOptions()); +} + +fn parseZoirNodeAlloc( + T: type, + gpa: Allocator, + out: *T, + ast: Ast, + zoir: Zoir, + node: Zoir.Node.Index, + diag: ?*Diagnostics, + options: Parser.Options, +) error{ OutOfMemory, ParseZon }!void { comptime assert(canParseType(T)); if (diag) |s| { @@ -398,9 +575,7 @@ pub fn fromZoirNodeAlloc( .diag = diag, }; - var out: T = undefined; - try parser.parseExprInto(node, &out); - return out; + try parser.parseExprInto(node, out); } /// Frees ZON values. @@ -486,7 +661,14 @@ const Parser = struct { ast: Ast, zoir: Zoir, diag: ?*Diagnostics, - options: Options, + options: Parser.Options, + pointer_depth: usize = 0, + + pub const Options = struct { + ignore_unknown_fields: bool, + free_on_error: bool, + ignore_missing_fields: bool, + }; const ParseExprError = error{ ParseZon, OutOfMemory }; @@ -529,6 +711,8 @@ const Parser = struct { ); errdefer self.gpa.free(slice); const ptr = &slice[0]; + self.pointer_depth += 1; + defer self.pointer_depth -= 1; try self.parseExprInnerInto(node, ptr); out.* = ptr; }, @@ -883,17 +1067,19 @@ const Parser = struct { } // Fill in any missing default fields - inline for (field_found, 0..) |found, i| { - if (!found) { - const field_attrs = info.field_attrs[i]; - if (field_attrs.defaultValue(info.field_types[i])) |default| { - @field(out, info.field_names[i]) = default; - } else { - return self.failNodeFmt( - node, - "missing required field {s}", - .{info.field_names[i]}, - ); + if (!self.options.ignore_missing_fields or self.pointer_depth > 0) { + inline for (field_found, 0..) |found, i| { + if (!found) { + const field_attrs = info.field_attrs[i]; + if (field_attrs.defaultValue(info.field_types[i])) |default| { + @field(out, info.field_names[i]) = default; + } else { + return self.failNodeFmt( + node, + "missing required field {s}", + .{info.field_names[i]}, + ); + } } } } @@ -3598,3 +3784,198 @@ test "std.zon aligned pointers" { defer free(gpa, found); try std.testing.expectEqualDeep(expected.inner, found.inner); } + +test "std.zon update semantics" { + // Test that the update logic works as expected + + var arena_allocator: std.heap.ArenaAllocator = .init(std.testing.allocator); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const Vector = struct { x: f32, y: f32, z: f32 }; + const MyStruct = struct { + foo: u32 = 123, + bar: bool, + baz: struct { + a: [2]u32, + b: enum { c, d } = .d, + }, + ptr: *Vector, + optional: ?u32 = null, + str: []const u8 = "hello, world", + }; + + var v: Vector = .{ .x = 1, .y = 2, .z = 3 }; + var expected: MyStruct = .{ + .foo = 10, + .bar = true, + .baz = .{ .a = .{ 1, 2 }, .b = .c }, + .ptr = &v, + }; + var found = expected; + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{}", + null, + .{}, + ); + try std.testing.expectEqual(expected, found); + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{ .bar = false }", + null, + .{}, + ); + expected.bar = false; + try std.testing.expectEqual(expected, found); + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{ .baz = .{ .a = .{ 2, 4 } } }", + null, + .{}, + ); + expected.baz.a[0] = 2; + expected.baz.a[1] = 4; + try std.testing.expectEqual(expected, found); + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{ .foo = 11, .baz = .{ .b = .d } }", + null, + .{}, + ); + expected.foo = 11; + expected.baz.b = .d; + try std.testing.expectEqual(expected, found); + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{ .optional = 10 }", + null, + .{}, + ); + expected.optional = 10; + try std.testing.expectEqual(expected, found); + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{ .optional = null }", + null, + .{}, + ); + expected.optional = null; + try std.testing.expectEqual(expected, found); + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", + null, + .{}, + ); + expected.ptr.x = 10; + expected.ptr.y = 20; + expected.ptr.z = 30; + try std.testing.expectEqualDeep(expected, found); + + try updateFromSliceAlloc( + MyStruct, + arena, + &found, + ".{ .str = \"foo\" }", + null, + .{}, + ); + expected.str = "foo"; + try std.testing.expectEqualDeep(expected, found); +} + +test "std.zon variants" { + // Just a smoke test, we don't need to test each variant thoroughly because they all call into + // the same code. + + var arena_allocator: std.heap.ArenaAllocator = .init(std.testing.allocator); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const Struct = struct { a: u32, b: u32 }; + const start: Struct = .{ .a = 10, .b = 20 }; + const end: Struct = .{ .a = 100, .b = 20 }; + + // Update + { + const ast = try std.zig.Ast.parse(arena, ".{ .a = 100 }", .{ .mode = .zon }); + const zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); + + var curr = start; + try updateFromSlice(Struct, arena, &curr, ".{ .a = 100 }", null, .{}); + try std.testing.expectEqual(end, curr); + + curr = start; + try updateFromSliceAlloc(Struct, arena, &curr, ".{ .a = 100 }", null, .{}); + try std.testing.expectEqual(end, curr); + + curr = start; + try updateFromZoir(Struct, &curr, ast, zoir, null, .{}); + try std.testing.expectEqual(end, curr); + + curr = start; + try updateFromZoirAlloc(Struct, arena, &curr, ast, zoir, null, .{}); + try std.testing.expectEqual(end, curr); + + curr = start; + try updateFromZoirNode(Struct, &curr, ast, zoir, .root, null, .{}); + try std.testing.expectEqual(end, curr); + + curr = start; + try updateFromZoirNodeAlloc(Struct, arena, &curr, ast, zoir, .root, null, .{}); + try std.testing.expectEqual(end, curr); + } + + // From + { + const ast = try std.zig.Ast.parse(arena, ".{ .a = 100, .b = 20 }", .{ .mode = .zon }); + const zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); + + try std.testing.expectEqual( + end, + try fromSlice(Struct, arena, ".{ .a = 100, .b = 20 }", null, .{}), + ); + try std.testing.expectEqual( + end, + try fromSliceAlloc(Struct, arena, ".{ .a = 100, .b = 20 }", null, .{}), + ); + try std.testing.expectEqual( + try fromZoir(Struct, ast, zoir, null, .{}), + end, + ); + try std.testing.expectEqual( + try fromZoirAlloc(Struct, arena, ast, zoir, null, .{}), + end, + ); + try std.testing.expectEqual( + try fromZoirNode(Struct, ast, zoir, .root, null, .{}), + end, + ); + try std.testing.expectEqual( + try fromZoirNodeAlloc(Struct, arena, ast, zoir, .root, null, .{}), + end, + ); + } +} -- 2.54.0 From 09e69d88ed0e3b8cf3c65aec4559614a835021a9 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Fri, 17 Jul 2026 03:23:14 -0700 Subject: [PATCH 04/26] `updateFrom*` functions no longer require an arena --- lib/std/zon/parse.zig | 186 ++++++++++++++++++++++++++---------------- 1 file changed, 115 insertions(+), 71 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index b57e6284e27fa56ef0845f20bb27934f078dff44..1d92db2e399fd3fd0f78a1d0b2ce77ae1ac352e8 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -42,6 +42,7 @@ pub const FromOptions = struct { .ignore_unknown_fields = self.ignore_unknown_fields, .ignore_missing_fields = false, .free_on_error = self.free_on_error, + .free_on_write = false, }; } }; @@ -50,12 +51,17 @@ pub const FromOptions = struct { pub const UpdateFromOptions = struct { /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, + /// If true, the old values of overriden fields are freed. You may want to turn this off if you + /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. + /// In this case, you can use arena allocation. + free_on_write: bool = true, fn toParserOptions(self: UpdateFromOptions) Parser.Options { return .{ .ignore_unknown_fields = self.ignore_unknown_fields, .ignore_missing_fields = true, .free_on_error = false, + .free_on_write = true, }; } }; @@ -313,7 +319,8 @@ pub fn fromSlice( } /// Similar to `fromSlice`, but updates an existing value `current`. Fields not specified by ZON are -/// left at their current values. Pointers are replaced with new values not mutated in place. +/// left at their current values. Pointers are replaced with new values not mutated in place. On +/// failure, values that were updated prior to the failure are left updated. pub fn updateFromSlice( T: type, gpa: Allocator, @@ -340,39 +347,39 @@ pub fn fromSliceAlloc( return result; } -/// Like updateFromSlice`, but the result may contain pointers. Requires an arena for cleanup. +/// Like updateFromSlice`, but the result may contain pointers. pub fn updateFromSliceAlloc( T: type, - arena: Allocator, + gpa: Allocator, current: *T, source: [:0]const u8, diag: ?*Diagnostics, options: UpdateFromOptions, ) error{ OutOfMemory, ParseZon }!void { - try parseSliceAlloc(T, current, arena, source, diag, options.toParserOptions()); + try parseSliceAlloc(T, current, gpa, source, diag, options.toParserOptions()); } fn parseSliceAlloc( T: type, current: *T, - arena: Allocator, + gpa: Allocator, source: [:0]const u8, diag: ?*Diagnostics, options: Parser.Options, ) error{ OutOfMemory, ParseZon }!void { if (diag) |s| s.assertEmpty(); - var ast = try std.zig.Ast.parse(arena, source, .{ .mode = .zon }); - defer if (diag == null) ast.deinit(arena); + var ast = try std.zig.Ast.parse(gpa, source, .{ .mode = .zon }); + defer if (diag == null) ast.deinit(gpa); if (diag) |s| s.ast = ast; // If there's no diagnostics, Zoir exists for the lifetime of this function. If there is a // diagnostics, ownership is transferred to diagnostics. - var zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); - defer if (diag == null) zoir.deinit(arena); + var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); + defer if (diag == null) zoir.deinit(gpa); if (diag) |s| s.* = .{}; - try parseZoirAlloc(T, arena, current, ast, zoir, diag, options); + try parseZoirAlloc(T, gpa, current, ast, zoir, diag, options); } /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. @@ -440,14 +447,14 @@ pub fn fromZoirAlloc( /// Like updateSliceAlloc`, but operates on `Zoir` instead of ZON source. pub fn updateFromZoirAlloc( T: type, - arena: Allocator, + gpa: Allocator, current: *T, ast: Ast, zoir: Zoir, diag: ?*Diagnostics, options: UpdateFromOptions, ) error{ OutOfMemory, ParseZon }!void { - try parseZoirAlloc(T, arena, current, ast, zoir, diag, options.toParserOptions()); + try parseZoirAlloc(T, gpa, current, ast, zoir, diag, options.toParserOptions()); } fn parseZoirAlloc( @@ -534,7 +541,7 @@ pub fn fromZoirNodeAlloc( /// Like `updateZoirNodeAlloc`, but the parse starts at `node` instead of root. pub fn updateFromZoirNodeAlloc( T: type, - arena: Allocator, + gpa: Allocator, current: *T, ast: Ast, zoir: Zoir, @@ -542,7 +549,7 @@ pub fn updateFromZoirNodeAlloc( diag: ?*Diagnostics, options: UpdateFromOptions, ) error{ OutOfMemory, ParseZon }!void { - return parseZoirNodeAlloc(T, arena, current, ast, zoir, node, diag, options.toParserOptions()); + return parseZoirNodeAlloc(T, gpa, current, ast, zoir, node, diag, options.toParserOptions()); } fn parseZoirNodeAlloc( @@ -667,6 +674,7 @@ const Parser = struct { pub const Options = struct { ignore_unknown_fields: bool, free_on_error: bool, + free_on_write: bool, ignore_missing_fields: bool, }; @@ -687,21 +695,22 @@ const Parser = struct { out: anytype, ) ParseExprInnerError!void { if (@TypeOf(out.*) == Zoir.Node.Index) { - out.* = node; + self.write(out, node); return; } switch (@typeInfo(@TypeOf(out.*))) { .optional => |optional| if (node.get(self.zoir) == .null) { - out.* = null; + self.write(out, null); } else { - out.* = @as(optional.child, undefined); // Make non-null - try self.parseExprInnerInto(node, &out.*.?); // Parse value into non-null pointer + var new: optional.child = undefined; + try self.parseExprInnerInto(node, &new); + self.write(out, new); }, - .bool => out.* = try self.parseBool(node), - .int => out.* = try self.parseInt(@TypeOf(out.*), node), - .float => out.* = try self.parseFloat(@TypeOf(out.*), node), - .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), + .bool => self.write(out, try self.parseBool(node)), + .int => self.write(out, try self.parseInt(@TypeOf(out.*), node)), + .float => self.write(out, try self.parseFloat(@TypeOf(out.*), node)), + .@"enum" => self.write(out, try self.parseEnumLiteral(@TypeOf(out.*), node)), .pointer => |pointer| switch (pointer.size) { .one => { const slice = try self.gpa.alignedAlloc( @@ -710,13 +719,23 @@ const Parser = struct { 1, ); errdefer self.gpa.free(slice); - const ptr = &slice[0]; - self.pointer_depth += 1; - defer self.pointer_depth -= 1; - try self.parseExprInnerInto(node, ptr); - out.* = ptr; + const new = &slice[0]; + { + self.pointer_depth += 1; + defer self.pointer_depth -= 1; + try self.parseExprInnerInto(node, new); + } + self.write(out, new); + }, + .slice => { + const new = b: { + self.pointer_depth += 1; + defer self.pointer_depth -= 1; + const new = try self.parseSlicePointer(@TypeOf(out.*), node); + break :b new; + }; + self.write(out, new); }, - .slice => out.* = try self.parseSlicePointer(@TypeOf(out.*), node), else => comptime unreachable, }, .array => try self.parseArrayInto(node, out), @@ -994,7 +1013,7 @@ const Parser = struct { const vector = @typeInfo(@TypeOf(out.*)).vector; var array: [vector.len]vector.child = undefined; try self.parseArrayInto(node, &array); - out.* = array; + self.write(out, array); } fn parseStructInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { @@ -1166,7 +1185,7 @@ const Parser = struct { return self.failNode(node, "expected union"); // Instantiate the union - out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], {}); + self.write(out, @unionInit(@TypeOf(out.*), @"union".field_names[i], {})); }, else => unreachable, // Can't be out of bounds } @@ -1188,7 +1207,7 @@ const Parser = struct { if (@"union".field_types[i] == void) { return self.failNode(field_val, "expected type 'void'"); } else { - out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined); + self.write(out, @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined)); try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i])); } }, @@ -1363,6 +1382,16 @@ const Parser = struct { .note = null, }); } + + /// Write to out, freeing the old value first if necessary. + fn write(self: *@This(), out: anytype, new: @TypeOf(out.*)) void { + if (@typeInfo(@TypeOf(out)) == .pointer) { + if (self.options.free_on_write and self.pointer_depth == 0) { + free(self.gpa, out.*); + } + } + out.* = new; + } }; fn intFromFloatExact(T: type, value: anytype) ?T { @@ -3785,12 +3814,8 @@ test "std.zon aligned pointers" { try std.testing.expectEqualDeep(expected.inner, found.inner); } -test "std.zon update semantics" { - // Test that the update logic works as expected - - var arena_allocator: std.heap.ArenaAllocator = .init(std.testing.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); +test "std.zon update" { + const gpa = std.testing.allocator; const Vector = struct { x: f32, y: f32, z: f32 }; const MyStruct = struct { @@ -3802,42 +3827,58 @@ test "std.zon update semantics" { }, ptr: *Vector, optional: ?u32 = null, - str: []const u8 = "hello, world", + str: []const u8, }; - var v: Vector = .{ .x = 1, .y = 2, .z = 3 }; var expected: MyStruct = .{ .foo = 10, .bar = true, .baz = .{ .a = .{ 1, 2 }, .b = .c }, - .ptr = &v, + .ptr = b: { + const v = gpa.create(Vector) catch @panic("oom"); + v.* = .{ .x = 1, .y = 2, .z = 3 }; + break :b v; + }, + .str = gpa.dupe(u8, "hello, world") catch @panic("oom"), }; - var found = expected; + defer free(gpa, expected); + var found: MyStruct = .{ + .foo = 10, + .bar = true, + .baz = .{ .a = .{ 1, 2 }, .b = .c }, + .ptr = b: { + const v = gpa.create(Vector) catch @panic("oom"); + v.* = .{ .x = 1, .y = 2, .z = 3 }; + break :b v; + }, + .str = gpa.dupe(u8, "hello, world") catch @panic("oom"), + }; + defer free(gpa, found); try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{}", null, .{}, ); - try std.testing.expectEqual(expected, found); + try std.testing.expectEqualDeep(expected, found); try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{ .bar = false }", null, .{}, ); expected.bar = false; - try std.testing.expectEqual(expected, found); + try std.testing.expectEqualDeep(expected, found); try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{ .baz = .{ .a = .{ 2, 4 } } }", null, @@ -3845,11 +3886,11 @@ test "std.zon update semantics" { ); expected.baz.a[0] = 2; expected.baz.a[1] = 4; - try std.testing.expectEqual(expected, found); + try std.testing.expectEqualDeep(expected, found); try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{ .foo = 11, .baz = .{ .b = .d } }", null, @@ -3857,33 +3898,33 @@ test "std.zon update semantics" { ); expected.foo = 11; expected.baz.b = .d; - try std.testing.expectEqual(expected, found); + try std.testing.expectEqualDeep(expected, found); try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{ .optional = 10 }", null, .{}, ); expected.optional = 10; - try std.testing.expectEqual(expected, found); + try std.testing.expectEqualDeep(expected, found); try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{ .optional = null }", null, .{}, ); expected.optional = null; - try std.testing.expectEqual(expected, found); + try std.testing.expectEqualDeep(expected, found); try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", null, @@ -3896,13 +3937,14 @@ test "std.zon update semantics" { try updateFromSliceAlloc( MyStruct, - arena, + gpa, &found, ".{ .str = \"foo\" }", null, .{}, ); - expected.str = "foo"; + gpa.free(expected.str); + expected.str = gpa.dupe(u8, "foo") catch @panic("oom"); try std.testing.expectEqualDeep(expected, found); } @@ -3910,9 +3952,7 @@ test "std.zon variants" { // Just a smoke test, we don't need to test each variant thoroughly because they all call into // the same code. - var arena_allocator: std.heap.ArenaAllocator = .init(std.testing.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); + const gpa = std.testing.allocator; const Struct = struct { a: u32, b: u32 }; const start: Struct = .{ .a = 10, .b = 20 }; @@ -3920,15 +3960,17 @@ test "std.zon variants" { // Update { - const ast = try std.zig.Ast.parse(arena, ".{ .a = 100 }", .{ .mode = .zon }); - const zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); + var ast = try std.zig.Ast.parse(gpa, ".{ .a = 100 }", .{ .mode = .zon }); + defer ast.deinit(gpa); + const zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); + defer zoir.deinit(gpa); var curr = start; - try updateFromSlice(Struct, arena, &curr, ".{ .a = 100 }", null, .{}); + try updateFromSlice(Struct, gpa, &curr, ".{ .a = 100 }", null, .{}); try std.testing.expectEqual(end, curr); curr = start; - try updateFromSliceAlloc(Struct, arena, &curr, ".{ .a = 100 }", null, .{}); + try updateFromSliceAlloc(Struct, gpa, &curr, ".{ .a = 100 }", null, .{}); try std.testing.expectEqual(end, curr); curr = start; @@ -3936,7 +3978,7 @@ test "std.zon variants" { try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoirAlloc(Struct, arena, &curr, ast, zoir, null, .{}); + try updateFromZoirAlloc(Struct, gpa, &curr, ast, zoir, null, .{}); try std.testing.expectEqual(end, curr); curr = start; @@ -3944,29 +3986,31 @@ test "std.zon variants" { try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoirNodeAlloc(Struct, arena, &curr, ast, zoir, .root, null, .{}); + try updateFromZoirNodeAlloc(Struct, gpa, &curr, ast, zoir, .root, null, .{}); try std.testing.expectEqual(end, curr); } // From { - const ast = try std.zig.Ast.parse(arena, ".{ .a = 100, .b = 20 }", .{ .mode = .zon }); - const zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); + var ast = try std.zig.Ast.parse(gpa, ".{ .a = 100, .b = 20 }", .{ .mode = .zon }); + defer ast.deinit(gpa); + const zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); + defer zoir.deinit(gpa); try std.testing.expectEqual( end, - try fromSlice(Struct, arena, ".{ .a = 100, .b = 20 }", null, .{}), + try fromSlice(Struct, gpa, ".{ .a = 100, .b = 20 }", null, .{}), ); try std.testing.expectEqual( end, - try fromSliceAlloc(Struct, arena, ".{ .a = 100, .b = 20 }", null, .{}), + try fromSliceAlloc(Struct, gpa, ".{ .a = 100, .b = 20 }", null, .{}), ); try std.testing.expectEqual( try fromZoir(Struct, ast, zoir, null, .{}), end, ); try std.testing.expectEqual( - try fromZoirAlloc(Struct, arena, ast, zoir, null, .{}), + try fromZoirAlloc(Struct, gpa, ast, zoir, null, .{}), end, ); try std.testing.expectEqual( @@ -3974,7 +4018,7 @@ test "std.zon variants" { end, ); try std.testing.expectEqual( - try fromZoirNodeAlloc(Struct, arena, ast, zoir, .root, null, .{}), + try fromZoirNodeAlloc(Struct, gpa, ast, zoir, .root, null, .{}), end, ); } -- 2.54.0 From 9e87e807ab65449845d12eaa3d98e2792194354d Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sat, 18 Jul 2026 14:26:42 -0700 Subject: [PATCH 05/26] Simplify by requiring node when parsing ZOIR --- lib/std/zon/parse.zig | 164 +++++------------------------------------- 1 file changed, 18 insertions(+), 146 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 1d92db2e399fd3fd0f78a1d0b2ce77ae1ac352e8..6ac9bdec91e0eae7a91d238d0c66a08cac16d0ca 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -4,7 +4,6 @@ //! //! Parsing from individual Zoir nodes is also available: //! * `fromZoir`/`fromZoirAlloc` -//! * `fromZoirNode`/`fromZoirNodeAlloc` //! //! To update an existing values, see the `updateFrom*` variants. //! @@ -293,21 +292,6 @@ pub const Diagnostics = struct { /// /// An allocator is still required for temporary allocations made during parsing. pub fn fromSlice( - /// The type to deserialize into. Only the following types are supported, unsupported types will - /// result in a compiler error: - /// - /// * Optionals of supported types - /// * Booleans - /// * Integers except for `comptime_int` - /// * Floats except for `comptime_float` - /// * Enums - /// * Single item pointers to supported types - /// * Slices of supported types - /// * Arrays of supported types - /// * Vectors of supported types - /// * Structures of supported types - /// * Tuples of supported types - /// * Unions T: type, gpa: Allocator, source: [:0]const u8, @@ -379,7 +363,7 @@ fn parseSliceAlloc( defer if (diag == null) zoir.deinit(gpa); if (diag) |s| s.* = .{}; - try parseZoirAlloc(T, gpa, current, ast, zoir, diag, options); + try parseZoirAlloc(T, gpa, current, ast, zoir, .root, diag, options); } /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. @@ -387,17 +371,17 @@ pub fn fromZoir( T: type, ast: Ast, zoir: Zoir, + node: Zoir.Node.Index, diag: ?*Diagnostics, options: FromOptions, ) error{ParseZon}!T { comptime assert(!requiresAllocator(T)); - var buf: [0]u8 = .{}; - var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); return fromZoirAlloc( T, - failing_allocator.allocator(), + .failing, ast, zoir, + node, diag, options, ) catch |err| switch (err) { @@ -412,18 +396,18 @@ pub fn updateFromZoir( current: *T, ast: Ast, zoir: Zoir, + node: Zoir.Node.Index, diag: ?*Diagnostics, options: UpdateFromOptions, ) error{ParseZon}!void { comptime assert(!requiresAllocator(T)); - var buf: [0]u8 = .{}; - var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); updateFromZoirAlloc( T, - failing_allocator.allocator(), + .failing, current, ast, zoir, + node, diag, options, ) catch |err| switch (err) { @@ -438,10 +422,13 @@ pub fn fromZoirAlloc( gpa: Allocator, ast: Ast, zoir: Zoir, + node: Zoir.Node.Index, diag: ?*Diagnostics, options: FromOptions, ) error{ OutOfMemory, ParseZon }!T { - return fromZoirNodeAlloc(T, gpa, ast, zoir, .root, diag, options); + var out: T = undefined; + try parseZoirAlloc(T, gpa, &out, ast, zoir, node, diag, options.toParserOptions()); + return out; } /// Like updateSliceAlloc`, but operates on `Zoir` instead of ZON source. @@ -451,108 +438,14 @@ pub fn updateFromZoirAlloc( current: *T, ast: Ast, zoir: Zoir, + node: Zoir.Node.Index, diag: ?*Diagnostics, options: UpdateFromOptions, ) error{ OutOfMemory, ParseZon }!void { - try parseZoirAlloc(T, gpa, current, ast, zoir, diag, options.toParserOptions()); + return parseZoirAlloc(T, gpa, current, ast, zoir, node, diag, options.toParserOptions()); } fn parseZoirAlloc( - T: type, - gpa: Allocator, - current: *T, - ast: Ast, - zoir: Zoir, - diag: ?*Diagnostics, - options: Parser.Options, -) error{ OutOfMemory, ParseZon }!void { - try parseZoirNodeAlloc(T, gpa, current, ast, zoir, .root, diag, options); -} - -/// Like `fromZoir`, but the parse starts at `node` instead of root. -pub fn fromZoirNode( - T: type, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: FromOptions, -) error{ParseZon}!T { - comptime assert(!requiresAllocator(T)); - var buf: [0]u8 = .{}; - var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); - return fromZoirNodeAlloc( - T, - failing_allocator.allocator(), - ast, - zoir, - node, - diag, - options, - ) catch |err| switch (err) { - error.OutOfMemory => unreachable, // Checked by comptime assertion above - else => |e| return e, - }; -} - -/// Like `updateZoir`, but the parse starts at `node` instead of root. -pub fn updateFromZoirNode( - T: type, - current: *T, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: UpdateFromOptions, -) error{ParseZon}!void { - comptime assert(!requiresAllocator(T)); - var buf: [0]u8 = .{}; - var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); - updateFromZoirNodeAlloc( - T, - failing_allocator.allocator(), - current, - ast, - zoir, - node, - diag, - options, - ) catch |err| switch (err) { - error.OutOfMemory => unreachable, // Checked by comptime assertion above - else => |e| return e, - }; -} - -/// Like `fromZoirAlloc`, but the parse starts at `node` instead of root. -pub fn fromZoirNodeAlloc( - T: type, - gpa: Allocator, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: FromOptions, -) error{ OutOfMemory, ParseZon }!T { - var out: T = undefined; - try parseZoirNodeAlloc(T, gpa, &out, ast, zoir, node, diag, options.toParserOptions()); - return out; -} - -/// Like `updateZoirNodeAlloc`, but the parse starts at `node` instead of root. -pub fn updateFromZoirNodeAlloc( - T: type, - gpa: Allocator, - current: *T, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: UpdateFromOptions, -) error{ OutOfMemory, ParseZon }!void { - return parseZoirNodeAlloc(T, gpa, current, ast, zoir, node, diag, options.toParserOptions()); -} - -fn parseZoirNodeAlloc( T: type, gpa: Allocator, out: *T, @@ -3760,12 +3653,7 @@ test "std.zon no alloc" { try std.testing.expectEqual( Nested{ 1, 2, .{ 3, 4 } }, - try fromZoir(Nested, ast, zoir, null, .{}), - ); - - try std.testing.expectEqual( - Nested{ 1, 2, .{ 3, 4 } }, - try fromZoirNode(Nested, ast, zoir, .root, null, .{}), + try fromZoir(Nested, ast, zoir, .root, null, .{}), ); } @@ -3974,19 +3862,11 @@ test "std.zon variants" { try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoir(Struct, &curr, ast, zoir, null, .{}); + try updateFromZoir(Struct, &curr, ast, zoir, .root, null, .{}); try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoirAlloc(Struct, gpa, &curr, ast, zoir, null, .{}); - try std.testing.expectEqual(end, curr); - - curr = start; - try updateFromZoirNode(Struct, &curr, ast, zoir, .root, null, .{}); - try std.testing.expectEqual(end, curr); - - curr = start; - try updateFromZoirNodeAlloc(Struct, gpa, &curr, ast, zoir, .root, null, .{}); + try updateFromZoirAlloc(Struct, gpa, &curr, ast, zoir, .root, null, .{}); try std.testing.expectEqual(end, curr); } @@ -4006,19 +3886,11 @@ test "std.zon variants" { try fromSliceAlloc(Struct, gpa, ".{ .a = 100, .b = 20 }", null, .{}), ); try std.testing.expectEqual( - try fromZoir(Struct, ast, zoir, null, .{}), + try fromZoir(Struct, ast, zoir, .root, null, .{}), end, ); try std.testing.expectEqual( - try fromZoirAlloc(Struct, gpa, ast, zoir, null, .{}), - end, - ); - try std.testing.expectEqual( - try fromZoirNode(Struct, ast, zoir, .root, null, .{}), - end, - ); - try std.testing.expectEqual( - try fromZoirNodeAlloc(Struct, gpa, ast, zoir, .root, null, .{}), + try fromZoirAlloc(Struct, gpa, ast, zoir, .root, null, .{}), end, ); } -- 2.54.0 From 07149649ce9201399233628d2dea61ebb061c430 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sat, 18 Jul 2026 21:39:17 -0700 Subject: [PATCH 06/26] Accept options structs instead of args I wrote this API, and even I can't remember what order the arguments are in. Furthermore, the types won't be enough to distinguish between them when I add an arena alongside the GPA in a followup. --- lib/std/zon/parse.zig | 1923 ++++++++++++++++++++++++++++------------- 1 file changed, 1303 insertions(+), 620 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 6ac9bdec91e0eae7a91d238d0c66a08cac16d0ca..c8eb480ea4b5698673ceed6ee2440829653ba1d7 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -24,47 +24,6 @@ const ArrayList = std.ArrayList; /// Rename when adding or removing support for a type. const valid_types = {}; -/// Deprecated. -pub const Options = FromOptions; - -/// Configuration for the `from*` functions. -pub const FromOptions = struct { - /// If true, unknown fields do not error. - ignore_unknown_fields: bool = false, - /// If true, the parser cleans up partially parsed values on error. This requires some extra - /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because - /// you're using arena allocation.) - free_on_error: bool = true, - - fn toParserOptions(self: FromOptions) Parser.Options { - return .{ - .ignore_unknown_fields = self.ignore_unknown_fields, - .ignore_missing_fields = false, - .free_on_error = self.free_on_error, - .free_on_write = false, - }; - } -}; - -/// Configuration for the `updateFrom*` variants. -pub const UpdateFromOptions = struct { - /// If true, unknown fields do not error. - ignore_unknown_fields: bool = false, - /// If true, the old values of overriden fields are freed. You may want to turn this off if you - /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. - /// In this case, you can use arena allocation. - free_on_write: bool = true, - - fn toParserOptions(self: UpdateFromOptions) Parser.Options { - return .{ - .ignore_unknown_fields = self.ignore_unknown_fields, - .ignore_missing_fields = true, - .free_on_error = false, - .free_on_write = true, - }; - } -}; - pub const Error = union(enum) { zoir: Zoir.CompileError, type_check: Error.TypeCheckFailure, @@ -279,6 +238,19 @@ pub const Diagnostics = struct { } }; +pub const FromSliceOptions = struct { + gpa: Allocator, + source: [:0]const u8, + /// Write human readable error messages here. + diag: ?*Diagnostics, + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, + /// If true, the parser cleans up partially parsed values on error. This requires some extra + /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because + /// you're using arena allocation.) + free_on_error: bool = true, +}; + /// Parses the given slice as ZON. /// /// Returns `error.OutOfMemory` on allocation failure, or `error.ParseZon` error if the ZON is @@ -291,15 +263,27 @@ pub const Diagnostics = struct { /// doesn't need to be freed. /// /// An allocator is still required for temporary allocations made during parsing. -pub fn fromSlice( - T: type, - gpa: Allocator, - source: [:0]const u8, - diag: ?*Diagnostics, - options: FromOptions, -) error{ OutOfMemory, ParseZon }!T { +pub fn fromSlice(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { comptime assert(!requiresAllocator(T)); - return fromSliceAlloc(T, gpa, source, diag, options); + return fromSliceAlloc(T, options); +} + +pub fn UpdateFromSliceOptions(T: type) type { + return struct { + gpa: Allocator, + /// The value to update. + value: *T, + /// The ZON source. + source: [:0]const u8, + /// Write human readable error messages here. + diag: ?*Diagnostics, + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, + /// If true, the old values of overriden fields are freed. You may want to turn this off if you + /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. + /// In this case, you can use arena allocation. + free_on_write: bool = true, + }; } /// Similar to `fromSlice`, but updates an existing value `current`. Fields not specified by ZON are @@ -307,45 +291,52 @@ pub fn fromSlice( /// failure, values that were updated prior to the failure are left updated. pub fn updateFromSlice( T: type, - gpa: Allocator, - current: *T, - source: [:0]const u8, - diag: ?*Diagnostics, - options: UpdateFromOptions, + options: UpdateFromSliceOptions(T), ) error{ OutOfMemory, ParseZon }!void { comptime assert(!requiresAllocator(T)); - try updateFromSliceAlloc(T, gpa, current, source, diag, options); + try updateFromSliceAlloc(T, options); } +pub const FromSliceAllocOptions = FromSliceOptions; + /// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see /// `free`. -pub fn fromSliceAlloc( - T: type, - gpa: Allocator, - source: [:0]const u8, - diag: ?*Diagnostics, - options: FromOptions, -) error{ OutOfMemory, ParseZon }!T { +pub fn fromSliceAlloc(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { var result: T = undefined; - try parseSliceAlloc(T, &result, gpa, source, diag, options.toParserOptions()); + try parseSliceAlloc(T, &result, options.gpa, options.source, options.diag, .{ + .ignore_unknown_fields = options.ignore_unknown_fields, + .ignore_missing_fields = false, + .free_on_error = options.free_on_error, + .free_on_write = false, + }); return result; } +pub const UpdateFromSliceAllocOptions = UpdateFromSliceOptions; + /// Like updateFromSlice`, but the result may contain pointers. pub fn updateFromSliceAlloc( T: type, - gpa: Allocator, - current: *T, - source: [:0]const u8, - diag: ?*Diagnostics, - options: UpdateFromOptions, + options: UpdateFromSliceAllocOptions(T), ) error{ OutOfMemory, ParseZon }!void { - try parseSliceAlloc(T, current, gpa, source, diag, options.toParserOptions()); + try parseSliceAlloc( + T, + options.value, + options.gpa, + options.source, + options.diag, + .{ + .ignore_unknown_fields = options.ignore_unknown_fields, + .ignore_missing_fields = true, + .free_on_error = false, + .free_on_write = options.free_on_write, + }, + ); } fn parseSliceAlloc( T: type, - current: *T, + out: *T, gpa: Allocator, source: [:0]const u8, diag: ?*Diagnostics, @@ -363,92 +354,167 @@ fn parseSliceAlloc( defer if (diag == null) zoir.deinit(gpa); if (diag) |s| s.* = .{}; - try parseZoirAlloc(T, gpa, current, ast, zoir, .root, diag, options); + try parseZoirAlloc(T, out, gpa, ast, zoir, .root, diag, options); } +pub const FromZoirOptions = struct { + /// The AST for this this ZON value. + ast: Ast, + /// The zig object intermediate representation of this ZON value. + zoir: Zoir, + /// The node to start parsing at. + node: Zoir.Node.Index = .root, + /// Write human readable error messages here. + diag: ?*Diagnostics, + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, + /// If true, the parser cleans up partially parsed values on error. This requires some extra + /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because + /// you're using arena allocation.) + free_on_error: bool = true, +}; + /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. -pub fn fromZoir( - T: type, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: FromOptions, -) error{ParseZon}!T { +pub fn fromZoir(T: type, options: FromZoirOptions) error{ParseZon}!T { comptime assert(!requiresAllocator(T)); - return fromZoirAlloc( - T, - .failing, - ast, - zoir, - node, - diag, - options, - ) catch |err| switch (err) { + return fromZoirAlloc(T, .{ + .gpa = .failing, + .ast = options.ast, + .zoir = options.zoir, + .node = options.node, + .diag = options.diag, + .ignore_unknown_fields = options.ignore_unknown_fields, + .free_on_error = options.free_on_error, + }) catch |err| switch (err) { error.OutOfMemory => unreachable, // Checked by comptime assertion above else => |e| return e, }; } +pub fn UpdateFromZoirOptions(T: type) type { + return struct { + value: *T, + /// The AST for this ZON value. + ast: Ast, + /// The zig object intermediate representation of this ZON value. + zoir: Zoir, + /// The node to start parsing at. + node: Zoir.Node.Index = .root, + /// Write human readable error messages here. + diag: ?*Diagnostics, + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, + /// If true, the old values of overriden fields are freed. You may want to turn this off if you + /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. + /// In this case, you can use arena allocation. + free_on_write: bool = true, + }; +} + /// Like `updateSlice`, but operates on `Zoir` instead of ZON source. pub fn updateFromZoir( T: type, - current: *T, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: UpdateFromOptions, + options: UpdateFromZoirOptions(T), ) error{ParseZon}!void { comptime assert(!requiresAllocator(T)); - updateFromZoirAlloc( - T, - .failing, - current, - ast, - zoir, - node, - diag, - options, - ) catch |err| switch (err) { + updateFromZoirAlloc(T, .{ + .gpa = .failing, + .value = options.value, + .ast = options.ast, + .zoir = options.zoir, + .node = options.node, + .diag = options.diag, + .ignore_unknown_fields = options.ignore_unknown_fields, + .free_on_write = options.free_on_write, + }) catch |err| switch (err) { error.OutOfMemory => unreachable, // Checked by comptime assertion above else => |e| return e, }; } +pub const FromZoirAllocOptions = struct { + gpa: Allocator, + /// The AST for this ZON value. + ast: Ast, + /// The zig object intermediate representation of this ZON value. + zoir: Zoir, + /// The node to start parsing at. + node: Zoir.Node.Index = .root, + /// Write human readable error messages here. + diag: ?*Diagnostics, + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, + /// If true, the parser cleans up partially parsed values on error. This requires some extra + /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because + /// you're using arena allocation.) + free_on_error: bool = true, +}; + /// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source. -pub fn fromZoirAlloc( - T: type, - gpa: Allocator, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: FromOptions, -) error{ OutOfMemory, ParseZon }!T { +pub fn fromZoirAlloc(T: type, options: FromZoirAllocOptions) error{ OutOfMemory, ParseZon }!T { var out: T = undefined; - try parseZoirAlloc(T, gpa, &out, ast, zoir, node, diag, options.toParserOptions()); + try parseZoirAlloc( + T, + &out, + options.gpa, + options.ast, + options.zoir, + options.node, + options.diag, + .{ + .ignore_unknown_fields = options.ignore_unknown_fields, + .ignore_missing_fields = false, + .free_on_error = options.free_on_error, + .free_on_write = false, + }, + ); return out; } +pub fn UpdateFromZoirAllocOptions(T: type) type { + return struct { + gpa: Allocator, + value: *T, + ast: Ast, + zoir: Zoir, + node: Zoir.Node.Index = .root, + /// Write human readable error messages here. + diag: ?*Diagnostics, + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, + /// If true, the old values of overriden fields are freed. You may want to turn this off if you + /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. + /// In this case, you can use arena allocation. + free_on_write: bool = true, + }; +} + /// Like updateSliceAlloc`, but operates on `Zoir` instead of ZON source. pub fn updateFromZoirAlloc( T: type, - gpa: Allocator, - current: *T, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index, - diag: ?*Diagnostics, - options: UpdateFromOptions, + options: UpdateFromZoirAllocOptions(T), ) error{ OutOfMemory, ParseZon }!void { - return parseZoirAlloc(T, gpa, current, ast, zoir, node, diag, options.toParserOptions()); + return parseZoirAlloc( + T, + options.value, + options.gpa, + options.ast, + options.zoir, + options.node, + options.diag, + .{ + .ignore_unknown_fields = options.ignore_unknown_fields, + .ignore_missing_fields = true, + .free_on_error = false, + .free_on_write = options.free_on_write, + }, + ); } fn parseZoirAlloc( T: type, - gpa: Allocator, out: *T, + gpa: Allocator, ast: Ast, zoir: Zoir, node: Zoir.Node.Index, @@ -1449,7 +1515,7 @@ test "std.zon ast errors" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &diag, .{}), + fromSlice(struct {}, .{ .gpa = gpa, .source = ".{.x = 1 .y = 2}", .diag = &diag }), ); try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{diag}); } @@ -1457,20 +1523,28 @@ test "std.zon ast errors" { test "std.zon comments" { const gpa = std.testing.allocator; - try std.testing.expectEqual(@as(u8, 10), fromSlice(u8, gpa, + try std.testing.expectEqual(@as(u8, 10), fromSlice(u8, .{ + .gpa = gpa, + .source = \\// comment \\10 // comment \\// comment - , null, .{})); + , + .diag = null, + })); { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = \\//! comment \\10 // comment \\// comment - , &diag, .{})); + , + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: expected expression, found 'a document comment'\n", "{f}", @@ -1481,26 +1555,24 @@ test "std.zon comments" { test "std.zon failure/oom formatting" { const gpa = std.testing.allocator; - var failing_allocator = std.testing.FailingAllocator.init(gpa, .{ - .fail_index = 0, - .resize_fail_index = 0, - }); var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.OutOfMemory, fromSliceAlloc( - []const u8, - failing_allocator.allocator(), - "\"foo\"", - &diag, - .{}, - )); + try std.testing.expectError(error.OutOfMemory, fromSliceAlloc([]const u8, .{ + .gpa = .failing, + .source = "\"foo\"", + .diag = &diag, + })); try std.testing.expectFmt("", "{f}", .{diag}); } test "std.zon fromSliceAlloc syntax error" { try std.testing.expectError( error.ParseZon, - fromSlice(u8, std.testing.allocator, ".{", null, .{}), + fromSlice(u8, .{ + .gpa = std.testing.allocator, + .source = ".{", + .diag = null, + }), ); } @@ -1509,17 +1581,33 @@ test "std.zon optional" { // Basic usage { - const none = try fromSlice(?u32, gpa, "null", null, .{}); + const none = try fromSlice(?u32, .{ + .gpa = gpa, + .source = "null", + .diag = null, + }); try std.testing.expect(none == null); - const some = try fromSlice(?u32, gpa, "1", null, .{}); + const some = try fromSlice(?u32, .{ + .gpa = gpa, + .source = "1", + .diag = null, + }); try std.testing.expect(some.? == 1); } // Deep free { - const none = try fromSliceAlloc(?[]const u8, gpa, "null", null, .{}); + const none = try fromSliceAlloc(?[]const u8, .{ + .gpa = gpa, + .source = "null", + .diag = null, + }); try std.testing.expect(none == null); - const some = try fromSliceAlloc(?[]const u8, gpa, "\"foo\"", null, .{}); + const some = try fromSliceAlloc(?[]const u8, .{ + .gpa = gpa, + .source = "\"foo\"", + .diag = null, + }); defer free(gpa, some); try std.testing.expectEqualStrings("foo", some.?); } @@ -1533,18 +1621,42 @@ test "std.zon unions" { const Tagged = union(enum) { x: f32, @"y y": bool, z, @"z z" }; const Untagged = union { x: f32, @"y y": bool, z: void, @"z z": void }; - const tagged_x = try fromSlice(Tagged, gpa, ".{.x = 1.5}", null, .{}); + const tagged_x = try fromSlice(Tagged, .{ + .gpa = gpa, + .source = ".{.x = 1.5}", + .diag = null, + }); try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x); - const tagged_y = try fromSlice(Tagged, gpa, ".{.@\"y y\" = true}", null, .{}); + const tagged_y = try fromSlice(Tagged, .{ + .gpa = gpa, + .source = ".{.@\"y y\" = true}", + .diag = null, + }); try std.testing.expectEqual(Tagged{ .@"y y" = true }, tagged_y); - const tagged_z_shorthand = try fromSlice(Tagged, gpa, ".z", null, .{}); + const tagged_z_shorthand = try fromSlice(Tagged, .{ + .gpa = gpa, + .source = ".z", + .diag = null, + }); try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand); - const tagged_zz_shorthand = try fromSlice(Tagged, gpa, ".@\"z z\"", null, .{}); + const tagged_zz_shorthand = try fromSlice(Tagged, .{ + .gpa = gpa, + .source = ".@\"z z\"", + .diag = null, + }); try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand); - const untagged_x = try fromSlice(Untagged, gpa, ".{.x = 1.5}", null, .{}); + const untagged_x = try fromSlice(Untagged, .{ + .gpa = gpa, + .source = ".{.x = 1.5}", + .diag = null, + }); try std.testing.expect(untagged_x.x == 1.5); - const untagged_y = try fromSlice(Untagged, gpa, ".{.@\"y y\" = true}", null, .{}); + const untagged_y = try fromSlice(Untagged, .{ + .gpa = gpa, + .source = ".{.@\"y y\" = true}", + .diag = null, + }); try std.testing.expect(untagged_y.@"y y"); } @@ -1552,10 +1664,18 @@ test "std.zon unions" { { const Union = union(enum) { bar: []const u8, baz: bool }; - const noalloc = try fromSliceAlloc(Union, gpa, ".{.baz = false}", null, .{}); + const noalloc = try fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".{.baz = false}", + .diag = null, + }); try std.testing.expectEqual(Union{ .baz = false }, noalloc); - const alloc = try fromSliceAlloc(Union, gpa, ".{.bar = \"qux\"}", null, .{}); + const alloc = try fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".{.bar = \"qux\"}", + .diag = null, + }); defer free(gpa, alloc); try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc); } @@ -1567,7 +1687,7 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, gpa, ".{.z=2.5}", &diag, .{}), + fromSliceAlloc(Union, .{ .gpa = gpa, .source = ".{.z=2.5}", .diag = &diag }), ); try std.testing.expectFmt( \\1:4: error: unexpected field 'z' @@ -1586,7 +1706,7 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, gpa, ".{.x=1}", &diag, .{}), + fromSliceAlloc(Union, .{ .gpa = gpa, .source = ".{.x=1}", .diag = &diag }), ); try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{diag}); } @@ -1598,7 +1718,11 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}), + fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".{.x = 1.5, .y = true}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } @@ -1610,7 +1734,7 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, gpa, ".{}", &diag, .{}), + fromSliceAlloc(Union, .{ .gpa = gpa, .source = ".{}", .diag = &diag }), ); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } @@ -1620,7 +1744,11 @@ test "std.zon unions" { const Union = union { x: void }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".x", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".x", + .diag = &diag, + })); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } @@ -1629,7 +1757,11 @@ test "std.zon unions" { const Union = union(enum) { x: void }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".y", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".y", + .diag = &diag, + })); try std.testing.expectFmt( \\1:2: error: unexpected field 'y' \\1:2: note: supported: 'x' @@ -1645,7 +1777,11 @@ test "std.zon unions" { const Union = union(enum) { x: f32 }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".x", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".x", + .diag = &diag, + })); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } } @@ -1660,16 +1796,32 @@ test "std.zon structs" { const Vec2 = struct { x: f32, y: f32 }; const Vec3 = struct { x: f32, y: f32, z: f32 }; - const zero = try fromSlice(Vec0, gpa, ".{}", null, .{}); + const zero = try fromSlice(Vec0, .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }); try std.testing.expectEqual(Vec0{}, zero); - const one = try fromSlice(Vec1, gpa, ".{.x = 1.2}", null, .{}); + const one = try fromSlice(Vec1, .{ + .gpa = gpa, + .source = ".{.x = 1.2}", + .diag = null, + }); try std.testing.expectEqual(Vec1{ .x = 1.2 }, one); - const two = try fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 3.4}", null, .{}); + const two = try fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x = 1.2, .y = 3.4}", + .diag = null, + }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 3.4 }, two); - const three = try fromSlice(Vec3, gpa, ".{.x = 1.2, .y = 3.4, .z = 5.6}", null, .{}); + const three = try fromSlice(Vec3, .{ + .gpa = gpa, + .source = ".{.x = 1.2, .y = 3.4, .z = 5.6}", + .diag = null, + }); try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three); } @@ -1677,13 +1829,11 @@ test "std.zon structs" { { const Foo = struct { bar: []const u8, baz: []const []const u8 }; - const parsed = try fromSliceAlloc( - Foo, - gpa, - ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", - null, - .{}, - ); + const parsed = try fromSliceAlloc(Foo, .{ + .gpa = gpa, + .source = ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed); } @@ -1695,7 +1845,7 @@ test "std.zon structs" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &diag, .{}), + fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .z=2.5}", .diag = &diag }), ); try std.testing.expectFmt( \\1:12: error: unexpected field 'z' @@ -1714,7 +1864,7 @@ test "std.zon structs" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, gpa, ".{.x=1.5, .x=2.5, .x=3.5}", &diag, .{}), + fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .x=2.5, .x=3.5}", .diag = &diag }), ); try std.testing.expectFmt( \\1:4: error: duplicate struct field name @@ -1726,8 +1876,11 @@ test "std.zon structs" { // Ignore unknown fields { const Vec2 = struct { x: f32, y: f32 = 2.0 }; - const parsed = try fromSlice(Vec2, gpa, ".{ .x = 1.0, .z = 3.0 }", null, .{ + const parsed = try fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{ .x = 1.0, .z = 3.0 }", .ignore_unknown_fields = true, + .diag = null, }); try std.testing.expectEqual(Vec2{ .x = 1.0, .y = 2.0 }, parsed); } @@ -1739,7 +1892,7 @@ test "std.zon structs" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &diag, .{}), + fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .z=2.5}", .diag = &diag }), ); try std.testing.expectFmt( \\1:4: error: unexpected field 'x' @@ -1755,7 +1908,7 @@ test "std.zon structs" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, gpa, ".{.x=1.5}", &diag, .{}), + fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5}", .diag = &diag }), ); try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{diag}); } @@ -1763,14 +1916,14 @@ test "std.zon structs" { // Default field { const Vec2 = struct { x: f32, y: f32 = 1.5 }; - const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{}); + const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2}", .diag = null }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } // Comptime field { const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; - const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{}); + const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2}", .diag = null }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } @@ -1779,7 +1932,11 @@ test "std.zon structs" { const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 1.5}", &diag, .{}); + const parsed = fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x = 1.2, .y = 1.5}", + .diag = &diag, + }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:18: error: cannot initialize comptime field @@ -1791,14 +1948,22 @@ test "std.zon structs" { // incorrect way that broke for enum values) { const Vec0 = struct { x: enum { x } }; - const parsed = try fromSlice(Vec0, gpa, ".{ .x = .x }", null, .{}); + const parsed = try fromSlice(Vec0, .{ + .gpa = gpa, + .source = ".{ .x = .x }", + .diag = null, + }); try std.testing.expectEqual(Vec0{ .x = .x }, parsed); } // Enum field and struct field with @ { const Vec0 = struct { @"x x": enum { @"x x" } }; - const parsed = try fromSlice(Vec0, gpa, ".{ .@\"x x\" = .@\"x x\" }", null, .{}); + const parsed = try fromSlice(Vec0, .{ + .gpa = gpa, + .source = ".{ .@\"x x\" = .@\"x x\" }", + .diag = null, + }); try std.testing.expectEqual(Vec0{ .@"x x" = .@"x x" }, parsed); } @@ -1808,7 +1973,11 @@ test "std.zon structs" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSlice(struct {}, gpa, "Empty{}", &diag, .{}); + const parsed = fromSlice(struct {}, .{ + .gpa = gpa, + .source = "Empty{}", + .diag = &diag, + }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON @@ -1821,7 +1990,11 @@ test "std.zon structs" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSlice([3]u8, gpa, "[3]u8{1, 2, 3}", &diag, .{}); + const parsed = fromSlice([3]u8, .{ + .gpa = gpa, + .source = "[3]u8{1, 2, 3}", + .diag = &diag, + }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON @@ -1834,7 +2007,11 @@ test "std.zon structs" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSliceAlloc([]u8, gpa, "[]u8{1, 2, 3}", &diag, .{}); + const parsed = fromSliceAlloc([]u8, .{ + .gpa = gpa, + .source = "[]u8{1, 2, 3}", + .diag = &diag, + }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON @@ -1847,13 +2024,11 @@ test "std.zon structs" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSlice( - struct { u8, u8, u8 }, - gpa, - "Tuple{1, 2, 3}", - &diag, - .{}, - ); + const parsed = fromSlice(struct { u8, u8, u8 }, .{ + .gpa = gpa, + .source = "Tuple{1, 2, 3}", + .diag = &diag, + }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON @@ -1866,7 +2041,11 @@ test "std.zon structs" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSlice(struct {}, gpa, ".{ .x = Tuple{1, 2, 3} }", &diag, .{}); + const parsed = fromSlice(struct {}, .{ + .gpa = gpa, + .source = ".{ .x = Tuple{1, 2, 3} }", + .diag = &diag, + }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:9: error: types are not available in ZON @@ -1887,23 +2066,43 @@ test "std.zon tuples" { const Tuple2 = struct { f32, bool }; const Tuple3 = struct { f32, bool, u8 }; - const zero = try fromSlice(Tuple0, gpa, ".{}", null, .{}); + const zero = try fromSlice(Tuple0, .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }); try std.testing.expectEqual(Tuple0{}, zero); - const one = try fromSlice(Tuple1, gpa, ".{1.2}", null, .{}); + const one = try fromSlice(Tuple1, .{ + .gpa = gpa, + .source = ".{1.2}", + .diag = null, + }); try std.testing.expectEqual(Tuple1{1.2}, one); - const two = try fromSlice(Tuple2, gpa, ".{1.2, true}", null, .{}); + const two = try fromSlice(Tuple2, .{ + .gpa = gpa, + .source = ".{1.2, true}", + .diag = null, + }); try std.testing.expectEqual(Tuple2{ 1.2, true }, two); - const three = try fromSlice(Tuple3, gpa, ".{1.2, false, 3}", null, .{}); + const three = try fromSlice(Tuple3, .{ + .gpa = gpa, + .source = ".{1.2, false, 3}", + .diag = null, + }); try std.testing.expectEqual(Tuple3{ 1.2, false, 3 }, three); } // Deep free { const Tuple = struct { []const u8, []const u8 }; - const parsed = try fromSliceAlloc(Tuple, gpa, ".{\"hello\", \"world\"}", null, .{}); + const parsed = try fromSliceAlloc(Tuple, .{ + .gpa = gpa, + .source = ".{\"hello\", \"world\"}", + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed); } @@ -1915,7 +2114,7 @@ test "std.zon tuples" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, gpa, ".{0.5, true, 123}", &diag, .{}), + fromSlice(Tuple, .{ .gpa = gpa, .source = ".{0.5, true, 123}", .diag = &diag }), ); try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{diag}); } @@ -1927,7 +2126,7 @@ test "std.zon tuples" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, gpa, ".{0.5}", &diag, .{}), + fromSlice(Tuple, .{ .gpa = gpa, .source = ".{0.5}", .diag = &diag }), ); try std.testing.expectFmt( "1:2: error: missing tuple field with index 1\n", @@ -1943,7 +2142,7 @@ test "std.zon tuples" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, gpa, ".{.foo = 10.0}", &diag, .{}), + fromSlice(Tuple, .{ .gpa = gpa, .source = ".{.foo = 10.0}", .diag = &diag }), ); try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{diag}); } @@ -1955,7 +2154,7 @@ test "std.zon tuples" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Struct, gpa, ".{10.0}", &diag, .{}), + fromSlice(Struct, .{ .gpa = gpa, .source = ".{10.0}", .diag = &diag }), ); try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{diag}); } @@ -1963,7 +2162,7 @@ test "std.zon tuples" { // Comptime field { const Vec2 = struct { f32, comptime f32 = 1.5 }; - const parsed = try fromSlice(Vec2, gpa, ".{ 1.2 }", null, .{}); + const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{ 1.2 }", .diag = null }); try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed); } @@ -1972,7 +2171,7 @@ test "std.zon tuples" { const Vec2 = struct { f32, comptime f32 = 1.5 }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSlice(Vec2, gpa, ".{ 1.2, 1.5}", &diag, .{}); + const parsed = fromSlice(Vec2, .{ .gpa = gpa, .source = ".{ 1.2, 1.5}", .diag = &diag }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:9: error: cannot initialize comptime field @@ -1989,49 +2188,97 @@ test "std.zon arrays and slices" { { // Arrays { - const zero = try fromSlice([0]u8, gpa, ".{}", null, .{}); + const zero = try fromSlice([0]u8, .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }); try std.testing.expectEqualSlices(u8, &@as([0]u8, .{}), &zero); - const one = try fromSlice([1]u8, gpa, ".{'a'}", null, .{}); + const one = try fromSlice([1]u8, .{ + .gpa = gpa, + .source = ".{'a'}", + .diag = null, + }); try std.testing.expectEqualSlices(u8, &@as([1]u8, .{'a'}), &one); - const two = try fromSlice([2]u8, gpa, ".{'a', 'b'}", null, .{}); + const two = try fromSlice([2]u8, .{ + .gpa = gpa, + .source = ".{'a', 'b'}", + .diag = null, + }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two); - const two_comma = try fromSlice([2]u8, gpa, ".{'a', 'b',}", null, .{}); + const two_comma = try fromSlice([2]u8, .{ + .gpa = gpa, + .source = ".{'a', 'b',}", + .diag = null, + }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two_comma); - const three = try fromSlice([3]u8, gpa, ".{'a', 'b', 'c'}", null, .{}); + const three = try fromSlice([3]u8, .{ + .gpa = gpa, + .source = ".{'a', 'b', 'c'}", + .diag = null, + }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, &three); - const sentinel = try fromSlice([3:'z']u8, gpa, ".{'a', 'b', 'c'}", null, .{}); + const sentinel = try fromSlice([3:'z']u8, .{ + .gpa = gpa, + .source = ".{'a', 'b', 'c'}", + .diag = null, + }); const expected_sentinel: [3:'z']u8 = .{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, &expected_sentinel, &sentinel); } // Slice literals { - const zero = try fromSliceAlloc([]const u8, gpa, ".{}", null, .{}); + const zero = try fromSliceAlloc([]const u8, .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }); defer free(gpa, zero); try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero); - const one = try fromSliceAlloc([]u8, gpa, ".{'a'}", null, .{}); + const one = try fromSliceAlloc([]u8, .{ + .gpa = gpa, + .source = ".{'a'}", + .diag = null, + }); defer free(gpa, one); try std.testing.expectEqualSlices(u8, &.{'a'}, one); - const two = try fromSliceAlloc([]const u8, gpa, ".{'a', 'b'}", null, .{}); + const two = try fromSliceAlloc([]const u8, .{ + .gpa = gpa, + .source = ".{'a', 'b'}", + .diag = null, + }); defer free(gpa, two); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two); - const two_comma = try fromSliceAlloc([]const u8, gpa, ".{'a', 'b',}", null, .{}); + const two_comma = try fromSliceAlloc([]const u8, .{ + .gpa = gpa, + .source = ".{'a', 'b',}", + .diag = null, + }); defer free(gpa, two_comma); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma); - const three = try fromSliceAlloc([]u8, gpa, ".{'a', 'b', 'c'}", null, .{}); + const three = try fromSliceAlloc([]u8, .{ + .gpa = gpa, + .source = ".{'a', 'b', 'c'}", + .diag = null, + }); defer free(gpa, three); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three); - const sentinel = try fromSliceAlloc([:'z']const u8, gpa, ".{'a', 'b', 'c'}", null, .{}); + const sentinel = try fromSliceAlloc([:'z']const u8, .{ + .gpa = gpa, + .source = ".{'a', 'b', 'c'}", + .diag = null, + }); defer free(gpa, sentinel); const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel); @@ -2042,7 +2289,11 @@ test "std.zon arrays and slices" { { // Arrays { - const parsed = try fromSliceAlloc([1][]const u8, gpa, ".{\"abc\"}", null, .{}); + const parsed = try fromSliceAlloc([1][]const u8, .{ + .gpa = gpa, + .source = ".{\"abc\"}", + .diag = null, + }); defer free(gpa, parsed); const expected: [1][]const u8 = .{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2050,7 +2301,11 @@ test "std.zon arrays and slices" { // Slice literals { - const parsed = try fromSliceAlloc([]const []const u8, gpa, ".{\"abc\"}", null, .{}); + const parsed = try fromSliceAlloc([]const []const u8, .{ + .gpa = gpa, + .source = ".{\"abc\"}", + .diag = null, + }); defer free(gpa, parsed); const expected: []const []const u8 = &.{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2061,7 +2316,11 @@ test "std.zon arrays and slices" { { // Arrays { - const sentinel = try fromSlice([1:2]u8, gpa, ".{1}", null, .{}); + const sentinel = try fromSlice([1:2]u8, .{ + .gpa = gpa, + .source = ".{1}", + .diag = null, + }); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); try std.testing.expectEqual(@as(u8, 2), sentinel[1]); @@ -2069,7 +2328,11 @@ test "std.zon arrays and slices" { // Slice literals { - const sentinel = try fromSliceAlloc([:2]align(4) u8, gpa, ".{1}", null, .{}); + const sentinel = try fromSliceAlloc([:2]align(4) u8, .{ + .gpa = gpa, + .source = ".{1}", + .diag = null, + }); defer free(gpa, sentinel); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -2083,7 +2346,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([0]u8, gpa, ".{'a', 'b', 'c'}", &diag, .{}), + fromSlice([0]u8, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", .diag = &diag }), ); try std.testing.expectFmt( "1:3: error: index 0 outside of array of length 0\n", @@ -2098,7 +2361,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([1]u8, gpa, ".{'a', 'b'}", &diag, .{}), + fromSlice([1]u8, .{ .gpa = gpa, .source = ".{'a', 'b'}", .diag = &diag }), ); try std.testing.expectFmt( "1:8: error: index 1 outside of array of length 1\n", @@ -2113,7 +2376,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([2]u8, gpa, ".{'a'}", &diag, .{}), + fromSlice([2]u8, .{ .gpa = gpa, .source = ".{'a'}", .diag = &diag }), ); try std.testing.expectFmt( "1:2: error: expected 2 array elements; found 1\n", @@ -2128,7 +2391,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([3]u8, gpa, ".{}", &diag, .{}), + fromSlice([3]u8, .{ .gpa = gpa, .source = ".{}", .diag = &diag }), ); try std.testing.expectFmt( "1:2: error: expected 3 array elements; found 0\n", @@ -2145,7 +2408,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}), + fromSlice([3]bool, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", .diag = &diag }), ); try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); } @@ -2156,7 +2419,11 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}), + fromSliceAlloc([]bool, .{ + .gpa = gpa, + .source = ".{'a', 'b', 'c'}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); } @@ -2170,7 +2437,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([3]u8, gpa, "'a'", &diag, .{}), + fromSlice([3]u8, .{ .gpa = gpa, .source = "'a'", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2181,7 +2448,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, gpa, "'a'", &diag, .{}), + fromSliceAlloc([]u8, .{ .gpa = gpa, .source = "'a'", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2193,7 +2460,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, gpa, " &.{'a', 'b', 'c'}", &diag, .{}), + fromSliceAlloc([]u8, .{ .gpa = gpa, .source = " &.{'a', 'b', 'c'}", .diag = &diag }), ); try std.testing.expectFmt( "1:3: error: pointers are not available in ZON\n", @@ -2208,21 +2475,33 @@ test "std.zon string literal" { // Basic string literal { - const parsed = try fromSliceAlloc([]const u8, gpa, "\"abc\"", null, .{}); + const parsed = try fromSliceAlloc([]const u8, .{ + .gpa = gpa, + .source = "\"abc\"", + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed); } // String literal with escape characters { - const parsed = try fromSliceAlloc([]const u8, gpa, "\"ab\\nc\"", null, .{}); + const parsed = try fromSliceAlloc([]const u8, .{ + .gpa = gpa, + .source = "\"ab\\nc\"", + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed); } // String literal with embedded null { - const parsed = try fromSliceAlloc([]const u8, gpa, "\"ab\\x00c\"", null, .{}); + const parsed = try fromSliceAlloc([]const u8, .{ + .gpa = gpa, + .source = "\"ab\\x00c\"", + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed); } @@ -2234,7 +2513,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, gpa, "\"abcd\"", &diag, .{}), + fromSliceAlloc([]u8, .{ .gpa = gpa, .source = "\"abcd\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2244,7 +2523,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, gpa, "\\\\abcd", &diag, .{}), + fromSliceAlloc([]u8, .{ .gpa = gpa, .source = "\\\\abcd", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2261,7 +2540,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([4:0]u8, gpa, "\"abcd\"", &diag, .{}), + fromSlice([4:0]u8, .{ .gpa = gpa, .source = "\"abcd\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2271,7 +2550,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([4:0]u8, gpa, "\\\\abcd", &diag, .{}), + fromSlice([4:0]u8, .{ .gpa = gpa, .source = "\\\\abcd", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2280,26 +2559,22 @@ test "std.zon string literal" { // Zero terminated slices { { - const parsed: [:0]const u8 = try fromSliceAlloc( - [:0]const u8, - gpa, - "\"abc\"", - null, - .{}, - ); + const parsed: [:0]const u8 = try fromSliceAlloc([:0]const u8, .{ + .gpa = gpa, + .source = "\"abc\"", + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); } { - const parsed: [:0]const u8 = try fromSliceAlloc( - [:0]const u8, - gpa, - "\\\\abc", - null, - .{}, - ); + const parsed: [:0]const u8 = try fromSliceAlloc([:0]const u8, .{ + .gpa = gpa, + .source = "\\\\abc", + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); @@ -2313,7 +2588,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, gpa, "\"foo\"", &diag, .{}), + fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .source = "\"foo\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2323,7 +2598,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, gpa, "\\\\foo", &diag, .{}), + fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .source = "\\\\foo", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2335,7 +2610,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, gpa, "true", &diag, .{}), + fromSliceAlloc([]const u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{diag}); } @@ -2346,7 +2621,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, gpa, ".{false}", &diag, .{}), + fromSliceAlloc([]const u8, .{ .gpa = gpa, .source = ".{false}", .diag = &diag }), ); try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{diag}); } @@ -2357,7 +2632,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, gpa, "\"\\a\"", &diag, .{}), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .source = "\"\\a\"", .diag = &diag }), ); try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{diag}); } @@ -2369,7 +2644,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, gpa, "\"a\"", &diag, .{}), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .source = "\"a\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2379,7 +2654,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, gpa, "\\\\a", &diag, .{}), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .source = "\\\\a", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2392,7 +2667,11 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]align(2) const u8, gpa, "\"abc\"", &diag, .{}), + fromSliceAlloc([]align(2) const u8, .{ + .gpa = gpa, + .source = "\"abc\"", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2402,7 +2681,11 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]align(2) const u8, gpa, "\\\\abc", &diag, .{}), + fromSliceAlloc([]align(2) const u8, .{ + .gpa = gpa, + .source = "\\\\abc", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2417,7 +2700,9 @@ test "std.zon string literal" { message2: String, message3: String, }; - const parsed = try fromSliceAlloc(S, gpa, + const parsed = try fromSliceAlloc(S, .{ + .gpa = gpa, + .source = \\.{ \\ .message = \\ \\hello, world! @@ -2434,7 +2719,9 @@ test "std.zon string literal" { \\ \\ \\ \\and this. \\} - , null, .{}); + , + .diag = null, + }); defer free(gpa, parsed); try std.testing.expectEqualStrings( "hello, world!\nthis is a multiline string!\n\n...", @@ -2457,12 +2744,24 @@ test "std.zon enum literals" { }; // Tags that exist - try std.testing.expectEqual(Enum.foo, try fromSlice(Enum, gpa, ".foo", null, .{})); - try std.testing.expectEqual(Enum.bar, try fromSlice(Enum, gpa, ".bar", null, .{})); - try std.testing.expectEqual(Enum.baz, try fromSlice(Enum, gpa, ".baz", null, .{})); + try std.testing.expectEqual(Enum.foo, try fromSlice(Enum, .{ + .gpa = gpa, + .source = ".foo", + .diag = null, + })); + try std.testing.expectEqual(Enum.bar, try fromSlice(Enum, .{ + .gpa = gpa, + .source = ".bar", + .diag = null, + })); + try std.testing.expectEqual(Enum.baz, try fromSlice(Enum, .{ + .gpa = gpa, + .source = ".baz", + .diag = null, + })); try std.testing.expectEqual( Enum.@"ab\nc", - try fromSlice(Enum, gpa, ".@\"ab\\nc\"", null, .{}), + try fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"ab\\nc\"", .diag = null }), ); // Bad tag @@ -2471,7 +2770,7 @@ test "std.zon enum literals" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, gpa, ".qux", &diag, .{}), + fromSlice(Enum, .{ .gpa = gpa, .source = ".qux", .diag = &diag }), ); try std.testing.expectFmt( \\1:2: error: unexpected enum literal 'qux' @@ -2489,7 +2788,7 @@ test "std.zon enum literals" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, gpa, ".@\"foobarbaz\"", &diag, .{}), + fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"foobarbaz\"", .diag = &diag }), ); try std.testing.expectFmt( \\1:2: error: unexpected enum literal 'foobarbaz' @@ -2507,7 +2806,7 @@ test "std.zon enum literals" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, gpa, "true", &diag, .{}), + fromSlice(Enum, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{diag}); } @@ -2518,7 +2817,7 @@ test "std.zon enum literals" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, gpa, ".@\"\\x00\"", &diag, .{}), + fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"\\x00\"", .diag = &diag }), ); try std.testing.expectFmt( "1:2: error: identifier cannot contain null bytes\n", @@ -2532,8 +2831,16 @@ test "std.zon parse bool" { const gpa = std.testing.allocator; // Correct bools - try std.testing.expectEqual(true, try fromSlice(bool, gpa, "true", null, .{})); - try std.testing.expectEqual(false, try fromSlice(bool, gpa, "false", null, .{})); + try std.testing.expectEqual(true, try fromSlice(bool, .{ + .gpa = gpa, + .source = "true", + .diag = null, + })); + try std.testing.expectEqual(false, try fromSlice(bool, .{ + .gpa = gpa, + .source = "false", + .diag = null, + })); // Errors { @@ -2541,7 +2848,7 @@ test "std.zon parse bool" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(bool, gpa, " foo", &diag, .{}), + fromSlice(bool, .{ .gpa = gpa, .source = " foo", .diag = &diag }), ); try std.testing.expectFmt( \\1:2: error: invalid expression @@ -2553,7 +2860,11 @@ test "std.zon parse bool" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(bool, gpa, "123", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(bool, .{ + .gpa = gpa, + .source = "123", + .diag = &diag, + })); try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{f}", .{diag}); } } @@ -2584,52 +2895,102 @@ test "std.zon parse int" { const gpa = std.testing.allocator; // Test various numbers and types - try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, gpa, "10", null, .{})); - try std.testing.expectEqual(@as(i16, 24), try fromSlice(i16, gpa, "24", null, .{})); - try std.testing.expectEqual(@as(i14, -4), try fromSlice(i14, gpa, "-4", null, .{})); - try std.testing.expectEqual(@as(i32, -123), try fromSlice(i32, gpa, "-123", null, .{})); + try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, .{ + .gpa = gpa, + .source = "10", + .diag = null, + })); + try std.testing.expectEqual(@as(i16, 24), try fromSlice(i16, .{ + .gpa = gpa, + .source = "24", + .diag = null, + })); + try std.testing.expectEqual(@as(i14, -4), try fromSlice(i14, .{ + .gpa = gpa, + .source = "-4", + .diag = null, + })); + try std.testing.expectEqual(@as(i32, -123), try fromSlice(i32, .{ + .gpa = gpa, + .source = "-123", + .diag = null, + })); // Test limits - try std.testing.expectEqual(@as(i8, 127), try fromSlice(i8, gpa, "127", null, .{})); - try std.testing.expectEqual(@as(i8, -128), try fromSlice(i8, gpa, "-128", null, .{})); + try std.testing.expectEqual(@as(i8, 127), try fromSlice(i8, .{ + .gpa = gpa, + .source = "127", + .diag = null, + })); + try std.testing.expectEqual(@as(i8, -128), try fromSlice(i8, .{ + .gpa = gpa, + .source = "-128", + .diag = null, + })); // Test characters - try std.testing.expectEqual(@as(u8, 'a'), try fromSlice(u8, gpa, "'a'", null, .{})); - try std.testing.expectEqual(@as(u8, 'z'), try fromSlice(u8, gpa, "'z'", null, .{})); + try std.testing.expectEqual(@as(u8, 'a'), try fromSlice(u8, .{ + .gpa = gpa, + .source = "'a'", + .diag = null, + })); + try std.testing.expectEqual(@as(u8, 'z'), try fromSlice(u8, .{ + .gpa = gpa, + .source = "'z'", + .diag = null, + })); // Test big integers try std.testing.expectEqual( @as(u65, 36893488147419103231), - try fromSlice(u65, gpa, "36893488147419103231", null, .{}), + try fromSlice(u65, .{ + .gpa = gpa, + .source = "36893488147419103231", + .diag = null, + }), ); try std.testing.expectEqual( @as(u65, 36893488147419103231), - try fromSlice(u65, gpa, "368934_881_474191032_31", null, .{}), + try fromSlice(u65, .{ + .gpa = gpa, + .source = "368934_881_474191032_31", + .diag = null, + }), ); try std.testing.expectEqual( @as(u128, 340282366920938463463374607431768211455), - try fromSlice(u128, gpa, "340282366920938463463374607431768211455", null, .{}), + try fromSlice(u128, .{ + .gpa = gpa, + .source = "340282366920938463463374607431768211455", + .diag = null, + }), ); // Test big integer limits try std.testing.expectEqual( @as(i66, 36893488147419103231), - try fromSlice(i66, gpa, "36893488147419103231", null, .{}), + try fromSlice(i66, .{ + .gpa = gpa, + .source = "36893488147419103231", + .diag = null, + }), ); try std.testing.expectEqual( @as(i66, -36893488147419103232), - try fromSlice(i66, gpa, "-36893488147419103232", null, .{}), + try fromSlice(i66, .{ + .gpa = gpa, + .source = "-36893488147419103232", + .diag = null, + }), ); { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice( - i66, - gpa, - "36893488147419103232", - &diag, - .{}, - )); + try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ + .gpa = gpa, + .source = "36893488147419103232", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: type 'i66' cannot represent value\n", "{f}", @@ -2639,13 +3000,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice( - i66, - gpa, - "-36893488147419103233", - &diag, - .{}, - )); + try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ + .gpa = gpa, + .source = "-36893488147419103233", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: type 'i66' cannot represent value\n", "{f}", @@ -2654,87 +3013,105 @@ test "std.zon parse int" { } // Test parsing whole number floats as integers - try std.testing.expectEqual(@as(i8, -1), try fromSlice(i8, gpa, "-1.0", null, .{})); - try std.testing.expectEqual(@as(i8, 123), try fromSlice(i8, gpa, "123.0", null, .{})); + try std.testing.expectEqual(@as(i8, -1), try fromSlice(i8, .{ + .gpa = gpa, + .source = "-1.0", + .diag = null, + })); + try std.testing.expectEqual(@as(i8, 123), try fromSlice(i8, .{ + .gpa = gpa, + .source = "123.0", + .diag = null, + })); // Test non-decimal integers - try std.testing.expectEqual(@as(i16, 0xff), try fromSlice(i16, gpa, "0xff", null, .{})); - try std.testing.expectEqual(@as(i16, -0xff), try fromSlice(i16, gpa, "-0xff", null, .{})); - try std.testing.expectEqual(@as(i16, 0o77), try fromSlice(i16, gpa, "0o77", null, .{})); - try std.testing.expectEqual(@as(i16, -0o77), try fromSlice(i16, gpa, "-0o77", null, .{})); - try std.testing.expectEqual(@as(i16, 0b11), try fromSlice(i16, gpa, "0b11", null, .{})); - try std.testing.expectEqual(@as(i16, -0b11), try fromSlice(i16, gpa, "-0b11", null, .{})); + try std.testing.expectEqual(@as(i16, 0xff), try fromSlice(i16, .{ + .gpa = gpa, + .source = "0xff", + .diag = null, + })); + try std.testing.expectEqual(@as(i16, -0xff), try fromSlice(i16, .{ + .gpa = gpa, + .source = "-0xff", + .diag = null, + })); + try std.testing.expectEqual(@as(i16, 0o77), try fromSlice(i16, .{ + .gpa = gpa, + .source = "0o77", + .diag = null, + })); + try std.testing.expectEqual(@as(i16, -0o77), try fromSlice(i16, .{ + .gpa = gpa, + .source = "-0o77", + .diag = null, + })); + try std.testing.expectEqual(@as(i16, 0b11), try fromSlice(i16, .{ + .gpa = gpa, + .source = "0b11", + .diag = null, + })); + try std.testing.expectEqual(@as(i16, -0b11), try fromSlice(i16, .{ + .gpa = gpa, + .source = "-0b11", + .diag = null, + })); // Test non-decimal big integers - try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice( - u65, - gpa, - "0x1ffffffffffffffff", - null, - .{}, - )); - try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice( - i66, - gpa, - "0x1ffffffffffffffff", - null, - .{}, - )); - try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice( - i66, - gpa, - "-0x1ffffffffffffffff", - null, - .{}, - )); - try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice( - u65, - gpa, - "0o3777777777777777777777", - null, - .{}, - )); - try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice( - i66, - gpa, - "0o3777777777777777777777", - null, - .{}, - )); - try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice( - i66, - gpa, - "-0o3777777777777777777777", - null, - .{}, - )); - try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice( - u65, - gpa, - "0b11111111111111111111111111111111111111111111111111111111111111111", - null, - .{}, - )); - try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice( - i66, - gpa, - "0b11111111111111111111111111111111111111111111111111111111111111111", - null, - .{}, - )); - try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice( - i66, - gpa, - "-0b11111111111111111111111111111111111111111111111111111111111111111", - null, - .{}, - )); + try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ + .gpa = gpa, + .source = "0x1ffffffffffffffff", + .diag = null, + })); + try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ + .gpa = gpa, + .source = "0x1ffffffffffffffff", + .diag = null, + })); + try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ + .gpa = gpa, + .source = "-0x1ffffffffffffffff", + .diag = null, + })); + try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ + .gpa = gpa, + .source = "0o3777777777777777777777", + .diag = null, + })); + try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ + .gpa = gpa, + .source = "0o3777777777777777777777", + .diag = null, + })); + try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ + .gpa = gpa, + .source = "-0o3777777777777777777777", + .diag = null, + })); + try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ + .gpa = gpa, + .source = "0b11111111111111111111111111111111111111111111111111111111111111111", + .diag = null, + })); + try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ + .gpa = gpa, + .source = "0b11111111111111111111111111111111111111111111111111111111111111111", + .diag = null, + })); + try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ + .gpa = gpa, + .source = "-0b11111111111111111111111111111111111111111111111111111111111111111", + .diag = null, + })); // Number with invalid character in the middle { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "32a32", + .diag = &diag, + })); try std.testing.expectFmt( "1:3: error: invalid digit 'a' for decimal base\n", "{f}", @@ -2746,7 +3123,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "true", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "true", + .diag = &diag, + })); try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{f}", .{diag}); } @@ -2754,7 +3135,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "256", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", @@ -2766,7 +3151,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + .gpa = gpa, + .source = "-129", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: type 'i8' cannot represent value\n", "{f}", @@ -2778,7 +3167,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "-1", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", @@ -2790,7 +3183,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "1.5", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", @@ -2802,7 +3199,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "-1.0", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", @@ -2814,7 +3215,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-0", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + .gpa = gpa, + .source = "-0", + .diag = &diag, + })); try std.testing.expectFmt( \\1:2: error: integer literal '-0' is ambiguous \\1:2: note: use '0' for an integer zero @@ -2827,7 +3232,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-0", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + .gpa = gpa, + .source = "-0", + .diag = &diag, + })); try std.testing.expectFmt( \\1:2: error: integer literal '-0' is ambiguous \\1:2: note: use '0' for an integer zero @@ -2838,15 +3247,27 @@ test "std.zon parse int" { // Negative float 0 is allowed try std.testing.expect( - std.math.isNegativeZero(try fromSlice(f32, gpa, "-0.0", null, .{})), + std.math.isNegativeZero(try fromSlice(f32, .{ + .gpa = gpa, + .source = "-0.0", + .diag = null, + })), ); - try std.testing.expect(std.math.isPositiveZero(try fromSlice(f32, gpa, "0.0", null, .{}))); + try std.testing.expect(std.math.isPositiveZero(try fromSlice(f32, .{ + .gpa = gpa, + .source = "0.0", + .diag = null, + }))); // Double negation is not allowed { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + .gpa = gpa, + .source = "--2", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", @@ -2859,7 +3280,7 @@ test "std.zon parse int" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(f32, gpa, "--2.0", &diag, .{}), + fromSlice(f32, .{ .gpa = gpa, .source = "--2.0", .diag = &diag }), ); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", @@ -2872,7 +3293,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0xg", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "0xg", + .diag = &diag, + })); try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{f}", .{diag}); } @@ -2880,7 +3305,11 @@ test "std.zon parse int" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0123", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + .gpa = gpa, + .source = "0123", + .diag = &diag, + })); try std.testing.expectFmt( \\1:1: error: number '0123' has leading zero \\1:1: note: use '0o' prefix for octal literals @@ -2895,7 +3324,11 @@ test "std.zon negative char" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + .gpa = gpa, + .source = "-'a'", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", @@ -2905,7 +3338,11 @@ test "std.zon negative char" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i16, .{ + .gpa = gpa, + .source = "-'a'", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", @@ -2918,81 +3355,149 @@ test "std.zon parse float" { const gpa = std.testing.allocator; // Test decimals - try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, gpa, "0.5", null, .{})); + try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, .{ + .gpa = gpa, + .source = "0.5", + .diag = null, + })); try std.testing.expectEqual( @as(f32, 123.456), - try fromSlice(f32, gpa, "123.456", null, .{}), + try fromSlice(f32, .{ .gpa = gpa, .source = "123.456", .diag = null }), ); try std.testing.expectEqual( @as(f64, -123.456), - try fromSlice(f64, gpa, "-123.456", null, .{}), + try fromSlice(f64, .{ .gpa = gpa, .source = "-123.456", .diag = null }), ); - try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, gpa, "42.5", null, .{})); + try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, .{ + .gpa = gpa, + .source = "42.5", + .diag = null, + })); // Test whole numbers with and without decimals - try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5.0", null, .{})); - try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5", null, .{})); - try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102.0", null, .{})); - try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102", null, .{})); + try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, .{ + .gpa = gpa, + .source = "5.0", + .diag = null, + })); + try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, .{ + .gpa = gpa, + .source = "5", + .diag = null, + })); + try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, .{ + .gpa = gpa, + .source = "-102.0", + .diag = null, + })); + try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, .{ + .gpa = gpa, + .source = "-102", + .diag = null, + })); // Test characters and negated characters - try std.testing.expectEqual(@as(f32, 'a'), try fromSlice(f32, gpa, "'a'", null, .{})); - try std.testing.expectEqual(@as(f32, 'z'), try fromSlice(f32, gpa, "'z'", null, .{})); + try std.testing.expectEqual(@as(f32, 'a'), try fromSlice(f32, .{ + .gpa = gpa, + .source = "'a'", + .diag = null, + })); + try std.testing.expectEqual(@as(f32, 'z'), try fromSlice(f32, .{ + .gpa = gpa, + .source = "'z'", + .diag = null, + })); // Test big integers try std.testing.expectEqual( @as(f32, 36893488147419103231.0), - try fromSlice(f32, gpa, "36893488147419103231", null, .{}), + try fromSlice(f32, .{ + .gpa = gpa, + .source = "36893488147419103231", + .diag = null, + }), ); try std.testing.expectEqual( @as(f32, -36893488147419103231.0), - try fromSlice(f32, gpa, "-36893488147419103231", null, .{}), + try fromSlice(f32, .{ + .gpa = gpa, + .source = "-36893488147419103231", + .diag = null, + }), ); - try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice( - f128, - gpa, - "0x1ffffffffffffffff", - null, - .{}, - )); - try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice( - f32, - gpa, - "0x1ffffffffffffffff", - null, - .{}, - )); + try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(f128, .{ + .gpa = gpa, + .source = "0x1ffffffffffffffff", + .diag = null, + })); + try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(f32, .{ + .gpa = gpa, + .source = "0x1ffffffffffffffff", + .diag = null, + })); // Exponents, underscores try std.testing.expectEqual( @as(f32, 123.0E+77), - try fromSlice(f32, gpa, "12_3.0E+77", null, .{}), + try fromSlice(f32, .{ + .gpa = gpa, + .source = "12_3.0E+77", + .diag = null, + }), ); // Hexadecimal try std.testing.expectEqual( @as(f32, 0x103.70p-5), - try fromSlice(f32, gpa, "0x103.70p-5", null, .{}), + try fromSlice(f32, .{ + .gpa = gpa, + .source = "0x103.70p-5", + .diag = null, + }), ); try std.testing.expectEqual( @as(f32, -0x103.70), - try fromSlice(f32, gpa, "-0x103.70", null, .{}), + try fromSlice(f32, .{ + .gpa = gpa, + .source = "-0x103.70", + .diag = null, + }), ); try std.testing.expectEqual( @as(f32, 0x1234_5678.9ABC_CDEFp-10), - try fromSlice(f32, gpa, "0x1234_5678.9ABC_CDEFp-10", null, .{}), + try fromSlice(f32, .{ + .gpa = gpa, + .source = "0x1234_5678.9ABC_CDEFp-10", + .diag = null, + }), ); // inf, nan - try std.testing.expect(std.math.isPositiveInf(try fromSlice(f32, gpa, "inf", null, .{}))); - try std.testing.expect(std.math.isNegativeInf(try fromSlice(f32, gpa, "-inf", null, .{}))); - try std.testing.expect(std.math.isNan(try fromSlice(f32, gpa, "nan", null, .{}))); + try std.testing.expect(std.math.isPositiveInf(try fromSlice(f32, .{ + .gpa = gpa, + .source = "inf", + .diag = null, + }))); + try std.testing.expect(std.math.isNegativeInf(try fromSlice(f32, .{ + .gpa = gpa, + .source = "-inf", + .diag = null, + }))); + try std.testing.expect(std.math.isNan(try fromSlice(f32, .{ + .gpa = gpa, + .source = "nan", + .diag = null, + }))); // Negative nan not allowed { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + .gpa = gpa, + .source = "-nan", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", @@ -3004,7 +3509,11 @@ test "std.zon parse float" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + .gpa = gpa, + .source = "nan", + .diag = &diag, + })); try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } @@ -3012,7 +3521,11 @@ test "std.zon parse float" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + .gpa = gpa, + .source = "nan", + .diag = &diag, + })); try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } @@ -3020,7 +3533,11 @@ test "std.zon parse float" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "inf", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + .gpa = gpa, + .source = "inf", + .diag = &diag, + })); try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } @@ -3028,7 +3545,11 @@ test "std.zon parse float" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-inf", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + .gpa = gpa, + .source = "-inf", + .diag = &diag, + })); try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); } @@ -3036,7 +3557,11 @@ test "std.zon parse float" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "foo", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + .gpa = gpa, + .source = "foo", + .diag = &diag, + })); try std.testing.expectFmt( \\1:1: error: invalid expression \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' @@ -3048,7 +3573,11 @@ test "std.zon parse float" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + .gpa = gpa, + .source = "-foo", + .diag = &diag, + })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", @@ -3062,7 +3591,7 @@ test "std.zon parse float" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(f32, gpa, "\"foo\"", &diag, .{}), + fromSlice(f32, .{ .gpa = gpa, .source = "\"foo\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{diag}); } @@ -3076,13 +3605,17 @@ test "std.zon free on error" { y: []const u8, z: bool, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + .gpa = std.testing.allocator, + .source = \\.{ \\ .x = "hello", \\ .y = "world", \\ .z = "fail", \\} - , null, .{})); + , + .diag = null, + })); } // Test freeing partially allocated tuples @@ -3092,13 +3625,17 @@ test "std.zon free on error" { []const u8, bool, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + .gpa = std.testing.allocator, + .source = \\.{ \\ "hello", \\ "world", \\ "fail", \\} - , null, .{})); + , + .diag = null, + })); } // Test freeing structs with missing fields @@ -3107,62 +3644,66 @@ test "std.zon free on error" { x: []const u8, y: bool, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + .gpa = std.testing.allocator, + .source = \\.{ \\ .x = "hello", \\} - , null, .{})); + , + .diag = null, + })); } // Test freeing partially allocated arrays { - try std.testing.expectError(error.ParseZon, fromSliceAlloc( - [3][]const u8, - std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc([3][]const u8, .{ + .gpa = std.testing.allocator, + .source = \\.{ \\ "hello", \\ false, \\ false, \\} - , - null, - .{}, - )); + , + .diag = null, + })); } // Test freeing partially allocated slices { - try std.testing.expectError(error.ParseZon, fromSliceAlloc( - [][]const u8, - std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc([][]const u8, .{ + .gpa = std.testing.allocator, + .source = \\.{ \\ "hello", \\ "world", \\ false, \\} - , - null, - .{}, - )); + , + .diag = null, + })); } // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged // unions. try std.testing.expectEqual( @as(f32, 1.5), - (try fromSlice(union { x: f32 }, std.testing.allocator, ".{ .x = 1.5 }", null, .{})).x, + (try fromSlice(union { x: f32 }, .{ + .gpa = std.testing.allocator, + .source = ".{ .x = 1.5 }", + .diag = null, + })).x, ); // We can also parse types that can't be freed if it's impossible for an error to occur after // the allocation, as is the case here. { - const result = try fromSliceAlloc( - union { x: []const u8 }, - std.testing.allocator, - ".{ .x = \"foo\" }", - null, - .{}, - ); + const result = try fromSliceAlloc(union { x: []const u8 }, .{ + .gpa = std.testing.allocator, + .source = ".{ .x = \"foo\" }", + .diag = null, + }); defer free(std.testing.allocator, result.x); try std.testing.expectEqualStrings("foo", result.x); } @@ -3175,13 +3716,12 @@ test "std.zon free on error" { union { x: []const u8 }, bool, }; - const result = try fromSliceAlloc( - S, - std.testing.allocator, - ".{ .{ .x = \"foo\" }, true }", - null, - .{ .free_on_error = false }, - ); + const result = try fromSliceAlloc(S, .{ + .gpa = std.testing.allocator, + .source = ".{ .{ .x = \"foo\" }, true }", + .free_on_error = false, + .diag = null, + }); defer free(std.testing.allocator, result[0].x); try std.testing.expectEqualStrings("foo", result[0].x); try std.testing.expect(result[1]); @@ -3193,15 +3733,12 @@ test "std.zon free on error" { a: union { x: []const u8 }, b: bool, }; - const result = try fromSliceAlloc( - S, - std.testing.allocator, - ".{ .a = .{ .x = \"foo\" }, .b = true }", - null, - .{ - .free_on_error = false, - }, - ); + const result = try fromSliceAlloc(S, .{ + .gpa = std.testing.allocator, + .source = ".{ .a = .{ .x = \"foo\" }, .b = true }", + .free_on_error = false, + .diag = null, + }); defer free(std.testing.allocator, result.a.x); try std.testing.expectEqualStrings("foo", result.a.x); try std.testing.expect(result.b); @@ -3210,15 +3747,12 @@ test "std.zon free on error" { // Again but for arrays. { const S = [2]union { x: []const u8 }; - const result = try fromSliceAlloc( - S, - std.testing.allocator, - ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", - null, - .{ - .free_on_error = false, - }, - ); + const result = try fromSliceAlloc(S, .{ + .gpa = std.testing.allocator, + .source = ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", + .free_on_error = false, + .diag = null, + }); defer free(std.testing.allocator, result[0].x); defer free(std.testing.allocator, result[1].x); try std.testing.expectEqualStrings("foo", result[0].x); @@ -3228,15 +3762,12 @@ test "std.zon free on error" { // Again but for slices. { const S = []union { x: []const u8 }; - const result = try fromSliceAlloc( - S, - std.testing.allocator, - ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", - null, - .{ - .free_on_error = false, - }, - ); + const result = try fromSliceAlloc(S, .{ + .gpa = std.testing.allocator, + .source = ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", + .free_on_error = false, + .diag = null, + }); defer std.testing.allocator.free(result); defer free(std.testing.allocator, result[0].x); defer free(std.testing.allocator, result[1].x); @@ -3254,37 +3785,53 @@ test "std.zon vector" { // Passing cases try std.testing.expectEqual( @Vector(0, bool){}, - try fromSlice(@Vector(0, bool), gpa, ".{}", null, .{}), + try fromSlice(@Vector(0, bool), .{ .gpa = gpa, .source = ".{}", .diag = null }), ); try std.testing.expectEqual( @Vector(3, bool){ true, false, true }, - try fromSlice(@Vector(3, bool), gpa, ".{true, false, true}", null, .{}), + try fromSlice(@Vector(3, bool), .{ + .gpa = gpa, + .source = ".{true, false, true}", + .diag = null, + }), ); try std.testing.expectEqual( @Vector(0, f32){}, - try fromSlice(@Vector(0, f32), gpa, ".{}", null, .{}), + try fromSlice(@Vector(0, f32), .{ .gpa = gpa, .source = ".{}", .diag = null }), ); try std.testing.expectEqual( @Vector(3, f32){ 1.5, 2.5, 3.5 }, - try fromSlice(@Vector(3, f32), gpa, ".{1.5, 2.5, 3.5}", null, .{}), + try fromSlice(@Vector(3, f32), .{ + .gpa = gpa, + .source = ".{1.5, 2.5, 3.5}", + .diag = null, + }), ); try std.testing.expectEqual( @Vector(0, u8){}, - try fromSlice(@Vector(0, u8), gpa, ".{}", null, .{}), + try fromSlice(@Vector(0, u8), .{ .gpa = gpa, .source = ".{}", .diag = null }), ); try std.testing.expectEqual( @Vector(3, u8){ 2, 4, 6 }, - try fromSlice(@Vector(3, u8), gpa, ".{2, 4, 6}", null, .{}), + try fromSlice(@Vector(3, u8), .{ .gpa = gpa, .source = ".{2, 4, 6}", .diag = null }), ); { try std.testing.expectEqual( @Vector(0, *const u8){}, - try fromSliceAlloc(@Vector(0, *const u8), gpa, ".{}", null, .{}), + try fromSliceAlloc(@Vector(0, *const u8), .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }), ); - const pointers = try fromSliceAlloc(@Vector(3, *const u8), gpa, ".{2, 4, 6}", null, .{}); + const pointers = try fromSliceAlloc(@Vector(3, *const u8), .{ + .gpa = gpa, + .source = ".{2, 4, 6}", + .diag = null, + }); defer free(gpa, pointers); try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers); } @@ -3292,9 +3839,17 @@ test "std.zon vector" { { try std.testing.expectEqual( @Vector(0, ?*const u8){}, - try fromSliceAlloc(@Vector(0, ?*const u8), gpa, ".{}", null, .{}), + try fromSliceAlloc(@Vector(0, ?*const u8), .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }), ); - const pointers = try fromSliceAlloc(@Vector(3, ?*const u8), gpa, ".{2, null, 6}", null, .{}); + const pointers = try fromSliceAlloc(@Vector(3, ?*const u8), .{ + .gpa = gpa, + .source = ".{2, null, 6}", + .diag = null, + }); defer free(gpa, pointers); try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers); } @@ -3305,7 +3860,11 @@ test "std.zon vector" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(2, f32), gpa, ".{0.5}", &diag, .{}), + fromSlice(@Vector(2, f32), .{ + .gpa = gpa, + .source = ".{0.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:2: error: expected 2 array elements; found 1\n", @@ -3320,7 +3879,11 @@ test "std.zon vector" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(2, f32), gpa, ".{0.5, 1.5, 2.5}", &diag, .{}), + fromSlice(@Vector(2, f32), .{ + .gpa = gpa, + .source = ".{0.5, 1.5, 2.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:13: error: index 2 outside of array of length 2\n", @@ -3335,7 +3898,11 @@ test "std.zon vector" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(3, f32), gpa, ".{0.5, true, 2.5}", &diag, .{}), + fromSlice(@Vector(3, f32), .{ + .gpa = gpa, + .source = ".{0.5, true, 2.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:8: error: expected type 'f32'\n", @@ -3350,7 +3917,7 @@ test "std.zon vector" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(3, u8), gpa, "true", &diag, .{}), + fromSlice(@Vector(3, u8), .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{diag}); } @@ -3361,7 +3928,11 @@ test "std.zon vector" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}), + fromSliceAlloc(@Vector(3, *u8), .{ + .gpa = gpa, + .source = ".{1, true, 3}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{diag}); } @@ -3372,77 +3943,133 @@ test "std.zon add pointers" { // Primitive with varying levels of pointers { - const result = try fromSliceAlloc(*u32, gpa, "10", null, .{}); + const result = try fromSliceAlloc(*u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*); } { - const result = try fromSliceAlloc(**u32, gpa, "10", null, .{}); + const result = try fromSliceAlloc(**u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*.*); } { - const result = try fromSliceAlloc(***u32, gpa, "10", null, .{}); + const result = try fromSliceAlloc(***u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*.*.*); } // Primitive optional with varying levels of pointers { - const some = try fromSliceAlloc(?*u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(?*u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.?.*); - const none = try fromSliceAlloc(?*u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(?*u32, .{ + .gpa = gpa, + .source = "null", + .diag = null, + }); defer free(gpa, none); try std.testing.expectEqual(null, none); } { - const some = try fromSliceAlloc(*?u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(*?u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.?); - const none = try fromSliceAlloc(*?u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(*?u32, .{ + .gpa = gpa, + .source = "null", + .diag = null, + }); defer free(gpa, none); try std.testing.expectEqual(null, none.*); } { - const some = try fromSliceAlloc(?**u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(?**u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.?.*.*); - const none = try fromSliceAlloc(?**u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(?**u32, .{ + .gpa = gpa, + .source = "null", + .diag = null, + }); defer free(gpa, none); try std.testing.expectEqual(null, none); } { - const some = try fromSliceAlloc(*?*u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(*?*u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.?.*); - const none = try fromSliceAlloc(*?*u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(*?*u32, .{ + .gpa = gpa, + .source = "null", + .diag = null, + }); defer free(gpa, none); try std.testing.expectEqual(null, none.*); } { - const some = try fromSliceAlloc(**?u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(**?u32, .{ + .gpa = gpa, + .source = "10", + .diag = null, + }); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.*.?); - const none = try fromSliceAlloc(**?u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(**?u32, .{ + .gpa = gpa, + .source = "null", + .diag = null, + }); defer free(gpa, none); try std.testing.expectEqual(null, none.*.*); } // Pointer to an array { - const result = try fromSliceAlloc(*[3]u8, gpa, ".{ 1, 2, 3 }", null, .{}); + const result = try fromSliceAlloc(*[3]u8, .{ + .gpa = gpa, + .source = ".{ 1, 2, 3 }", + .diag = null, + }); defer free(gpa, result); try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*); } @@ -3465,7 +4092,9 @@ test "std.zon add pointers" { .f2 = &null, }; - const found = try fromSliceAlloc(?*Outer, gpa, + const found = try fromSliceAlloc(?*Outer, .{ + .gpa = gpa, + .source = \\.{ \\ .f1 = .{ \\ .f1 = null, @@ -3473,7 +4102,9 @@ test "std.zon add pointers" { \\ }, \\ .f2 = null, \\} - , null, .{}); + , + .diag = null, + }); defer free(gpa, found); try std.testing.expectEqualDeep(expected, found.?.*); @@ -3485,7 +4116,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{diag}); } @@ -3495,7 +4126,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const f32, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const f32, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{diag}); } @@ -3505,7 +4136,11 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const @Vector(3, u8), .{ + .gpa = gpa, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{diag}); } @@ -3515,7 +4150,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const bool, gpa, "10", &diag, .{}), + fromSliceAlloc(*const ?*const bool, .{ .gpa = gpa, .source = "10", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{diag}); } @@ -3525,7 +4160,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const struct { a: i32 }, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{diag}); } @@ -3535,7 +4170,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const struct { i32 }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const struct { i32 }, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{diag}); } @@ -3545,7 +4180,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const union { x: void }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const union { x: void }, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{diag}); } @@ -3555,7 +4190,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const [3]u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const [3]u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3565,7 +4200,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(?[3]u8, gpa, "true", &diag, .{}), + fromSliceAlloc(?[3]u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3575,7 +4210,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const []u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const []u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3585,7 +4220,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(?[]u8, gpa, "true", &diag, .{}), + fromSliceAlloc(?[]u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3595,7 +4230,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const []const u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const []const u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{diag}); } @@ -3605,7 +4240,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const enum { foo }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const enum { foo }, .{ .gpa = gpa, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{diag}); } @@ -3622,7 +4257,11 @@ test "std.zon stop on node" { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const result = try fromSlice(Vec2, gpa, ".{ .x = 1.5, .y = 2.5 }", &diag, .{}); + const result = try fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{ .x = 1.5, .y = 2.5 }", + .diag = &diag, + }); try std.testing.expectEqual(result.y, 2.5); try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.5 }, result.x.get(diag.zoir)); } @@ -3630,7 +4269,11 @@ test "std.zon stop on node" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const result = try fromSlice(Zoir.Node.Index, gpa, "1.23", &diag, .{}); + const result = try fromSlice(Zoir.Node.Index, .{ + .gpa = gpa, + .source = "1.23", + .diag = &diag, + }); try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(diag.zoir)); } } @@ -3640,7 +4283,11 @@ test "std.zon no alloc" { try std.testing.expectEqual( [3]u8{ 1, 2, 3 }, - try fromSlice([3]u8, gpa, ".{ 1, 2, 3 }", null, .{}), + try fromSlice([3]u8, .{ + .gpa = gpa, + .source = ".{ 1, 2, 3 }", + .diag = null, + }), ); const Nested = struct { u8, u8, struct { u8, u8 } }; @@ -3653,7 +4300,7 @@ test "std.zon no alloc" { try std.testing.expectEqual( Nested{ 1, 2, .{ 3, 4 } }, - try fromZoir(Nested, ast, zoir, .root, null, .{}), + try fromZoir(Nested, .{ .ast = ast, .zoir = zoir, .diag = null }), ); } @@ -3665,19 +4312,35 @@ test "std.zon errors without diagnostics" { bar, baz, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Enum, gpa, ".nothing", null, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Enum, .{ + .gpa = gpa, + .source = ".nothing", + .diag = null, + })); const Struct = struct { name: []const u8, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, gpa, ".{ .name = \"Alice\", .age = 25 }", null, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + .gpa = gpa, + .source = ".{ .name = \"Alice\", .age = 25 }", + .diag = null, + })); const Union = union(enum) { x, y: u32, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".a", null, .{})); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".{ .b = 8 }", null, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".a", + .diag = null, + })); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + .gpa = gpa, + .source = ".{ .b = 8 }", + .diag = null, + })); } test "std.zon aligned pointers" { @@ -3691,13 +4354,11 @@ test "std.zon aligned pointers" { const expected: Foo = .{ .inner = &n, }; - const found = try fromSliceAlloc( - Foo, - gpa, - ".{ .inner = 10 }", - null, - .{}, - ); + const found = try fromSliceAlloc(Foo, .{ + .gpa = gpa, + .source = ".{ .inner = 10 }", + .diag = null, + }); defer free(gpa, found); try std.testing.expectEqualDeep(expected.inner, found.inner); } @@ -3743,94 +4404,78 @@ test "std.zon update" { }; defer free(gpa, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{}", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{}", + .diag = null, + }); try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{ .bar = false }", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{ .bar = false }", + .diag = null, + }); expected.bar = false; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{ .baz = .{ .a = .{ 2, 4 } } }", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{ .baz = .{ .a = .{ 2, 4 } } }", + .diag = null, + }); expected.baz.a[0] = 2; expected.baz.a[1] = 4; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{ .foo = 11, .baz = .{ .b = .d } }", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{ .foo = 11, .baz = .{ .b = .d } }", + .diag = null, + }); expected.foo = 11; expected.baz.b = .d; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{ .optional = 10 }", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{ .optional = 10 }", + .diag = null, + }); expected.optional = 10; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{ .optional = null }", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{ .optional = null }", + .diag = null, + }); expected.optional = null; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", + .diag = null, + }); expected.ptr.x = 10; expected.ptr.y = 20; expected.ptr.z = 30; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc( - MyStruct, - gpa, - &found, - ".{ .str = \"foo\" }", - null, - .{}, - ); + try updateFromSliceAlloc(MyStruct, .{ + .gpa = gpa, + .value = &found, + .source = ".{ .str = \"foo\" }", + .diag = null, + }); gpa.free(expected.str); expected.str = gpa.dupe(u8, "foo") catch @panic("oom"); try std.testing.expectEqualDeep(expected, found); @@ -3854,19 +4499,40 @@ test "std.zon variants" { defer zoir.deinit(gpa); var curr = start; - try updateFromSlice(Struct, gpa, &curr, ".{ .a = 100 }", null, .{}); + try updateFromSlice(Struct, .{ + .gpa = gpa, + .value = &curr, + .source = ".{ .a = 100 }", + .diag = null, + }); try std.testing.expectEqual(end, curr); curr = start; - try updateFromSliceAlloc(Struct, gpa, &curr, ".{ .a = 100 }", null, .{}); + try updateFromSliceAlloc(Struct, .{ + .gpa = gpa, + .value = &curr, + .source = ".{ .a = 100 }", + .diag = null, + }); try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoir(Struct, &curr, ast, zoir, .root, null, .{}); + try updateFromZoir(Struct, .{ + .ast = ast, + .value = &curr, + .zoir = zoir, + .diag = null, + }); try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoirAlloc(Struct, gpa, &curr, ast, zoir, .root, null, .{}); + try updateFromZoirAlloc(Struct, .{ + .gpa = gpa, + .value = &curr, + .ast = ast, + .zoir = zoir, + .diag = null, + }); try std.testing.expectEqual(end, curr); } @@ -3879,18 +4545,35 @@ test "std.zon variants" { try std.testing.expectEqual( end, - try fromSlice(Struct, gpa, ".{ .a = 100, .b = 20 }", null, .{}), + try fromSlice(Struct, .{ + .gpa = gpa, + .source = ".{ .a = 100, .b = 20 }", + .diag = null, + }), ); try std.testing.expectEqual( end, - try fromSliceAlloc(Struct, gpa, ".{ .a = 100, .b = 20 }", null, .{}), + try fromSliceAlloc(Struct, .{ + .gpa = gpa, + .source = ".{ .a = 100, .b = 20 }", + .diag = null, + }), ); try std.testing.expectEqual( - try fromZoir(Struct, ast, zoir, .root, null, .{}), + try fromZoir(Struct, .{ + .ast = ast, + .zoir = zoir, + .diag = null, + }), end, ); try std.testing.expectEqual( - try fromZoirAlloc(Struct, gpa, ast, zoir, .root, null, .{}), + try fromZoirAlloc(Struct, .{ + .gpa = gpa, + .ast = ast, + .zoir = zoir, + .diag = null, + }), end, ); } -- 2.54.0 From ed70c643492c7c49057a72ff3e1981cb1ca81e57 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 20 Jul 2026 17:14:44 -0700 Subject: [PATCH 07/26] Use arena for diagnostics and results --- lib/std/zig/ErrorBundle.zig | 4 +- lib/std/zig/Zoir.zig | 12 +- lib/std/zig/ZonGen.zig | 8 +- lib/std/zon/parse.zig | 1910 ++++++++++++++++------------------- 4 files changed, 908 insertions(+), 1026 deletions(-) diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index d6fde276f1e9fb328d262b90a91f0112c45afb77..a16adbc708990c692a23c1d8bb0009ce95b6099a 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -631,7 +631,7 @@ pub const Wip = struct { const err_loc = std.zig.findLineColumn(source, err_span.main); try eb.addRootErrorMessage(.{ - .msg = try eb.addString(err.msg.get(zoir)), + .msg = try eb.addString(err.msg.get(&zoir)), .src_loc = try eb.addSourceLocation(.{ .src_path = try eb.addString(src_path), .span_start = err_span.start, @@ -661,7 +661,7 @@ pub const Wip = struct { // This line can cause `wip.extra.items` to be resized. const note_index = @backingInt(try eb.addErrorMessage(.{ - .msg = try eb.addString(note.msg.get(zoir)), + .msg = try eb.addString(note.msg.get(&zoir)), .src_loc = try eb.addSourceLocation(.{ .src_path = try eb.addString(src_path), .span_start = note_span.start, diff --git a/lib/std/zig/Zoir.zig b/lib/std/zig/Zoir.zig index d29315aa5cb91ab12167ee39a3022f87505cda11..e7c2376d865d652c9eb2e38b28341e224443679d 100644 --- a/lib/std/zig/Zoir.zig +++ b/lib/std/zig/Zoir.zig @@ -42,7 +42,7 @@ pub const Header = extern struct { } }; -pub fn hasCompileErrors(zoir: Zoir) bool { +pub fn hasCompileErrors(zoir: *const Zoir) bool { if (zoir.compile_errors.len > 0) { assert(zoir.nodes.len == 0); assert(zoir.extra.len == 0); @@ -54,7 +54,7 @@ pub fn hasCompileErrors(zoir: Zoir) bool { } } -pub fn deinit(zoir: Zoir, gpa: Allocator) void { +pub fn deinit(zoir: *const Zoir, gpa: Allocator) void { var nodes = zoir.nodes; nodes.deinit(gpa); @@ -105,7 +105,7 @@ pub const Node = union(enum) { root = 0, _, - pub fn get(idx: Index, zoir: Zoir) Node { + pub fn get(idx: Index, zoir: *const Zoir) Node { const repr = zoir.nodes.get(@backingInt(idx)); return switch (repr.tag) { .true => .true, @@ -151,7 +151,7 @@ pub const Node = union(enum) { }; } - pub fn getAstNode(idx: Index, zoir: Zoir) std.zig.Ast.Node.Index { + pub fn getAstNode(idx: Index, zoir: *const Zoir) std.zig.Ast.Node.Index { return zoir.nodes.items(.ast_node)[@backingInt(idx)]; } @@ -227,7 +227,7 @@ pub const Node = union(enum) { pub const NullTerminatedString = enum(u32) { _, - pub fn get(nts: NullTerminatedString, zoir: Zoir) [:0]const u8 { + pub fn get(nts: NullTerminatedString, zoir: *const Zoir) [:0]const u8 { const idx = std.mem.findScalar(u8, zoir.string_bytes[@backingInt(nts)..], 0).?; return zoir.string_bytes[@backingInt(nts)..][0..idx :0]; } @@ -244,7 +244,7 @@ pub const CompileError = extern struct { first_note: u32, note_count: u32, - pub fn getNotes(err: CompileError, zoir: Zoir) []const Note { + pub fn getNotes(err: CompileError, zoir: *const Zoir) []const Note { return zoir.error_notes[err.first_note..][0..err.note_count]; } diff --git a/lib/std/zig/ZonGen.zig b/lib/std/zig/ZonGen.zig index 97ab7c5ab707b537246eae1d2ea714bbc1ae21b7..33723d62f6cd301c05fec0c4d97c72393e565174 100644 --- a/lib/std/zig/ZonGen.zig +++ b/lib/std/zig/ZonGen.zig @@ -490,7 +490,7 @@ fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) error{ OutOfMemory, } /// Estimates the size of a string node without parsing it. -pub fn strLitSizeHint(tree: Ast, node: Ast.Node.Index) usize { +pub fn strLitSizeHint(tree: *const Ast, node: Ast.Node.Index) usize { switch (tree.nodeTag(node)) { // Parsed string literals are typically around the size of the raw strings. .string_literal => { @@ -515,7 +515,7 @@ pub fn strLitSizeHint(tree: Ast, node: Ast.Node.Index) usize { /// Parses the given node as a string literal. pub fn parseStrLit( - tree: Ast, + tree: *const Ast, node: Ast.Node.Index, writer: *Writer, ) Writer.Error!std.zig.string_literal.Result { @@ -559,12 +559,12 @@ fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) error{ OutOfMemory, Bad const gpa = zg.gpa; const string_bytes = &zg.string_bytes; const str_index: u32 = @intCast(zg.string_bytes.items.len); - const size_hint = strLitSizeHint(zg.tree, str_node); + const size_hint = strLitSizeHint(&zg.tree, str_node); try string_bytes.ensureUnusedCapacity(gpa, size_hint); const result = r: { var aw: Writer.Allocating = .fromArrayList(gpa, &zg.string_bytes); defer zg.string_bytes = aw.toArrayList(); - break :r parseStrLit(zg.tree, str_node, &aw.writer) catch |err| switch (err) { + break :r parseStrLit(&zg.tree, str_node, &aw.writer) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; }; diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index c8eb480ea4b5698673ceed6ee2440829653ba1d7..149188195e8b1dfc2e5636a6af2be62ce40ed609 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -1,13 +1,11 @@ //! The simplest way to parse ZON at runtime is to use `fromSlice`/`fromSliceAlloc`. //! -//! Note that if you need to parse ZON at compile time, you may use `@import`. -//! //! Parsing from individual Zoir nodes is also available: //! * `fromZoir`/`fromZoirAlloc` //! -//! To update an existing values, see the `updateFrom*` variants. -//! -//! For lower level control over parsing, see `std.zig.Zoir`. +//! To update an existing values, see the `updateFrom*` variants. To get human readable errors, use +//! the `diag` option. For lower level control over parsing, see `std.zig.Zoir`. For importing ZON +//! at compile time, use `@import`. const std = @import("std"); const Allocator = std.mem.Allocator; @@ -20,235 +18,122 @@ const StrLitErr = std.zig.string_literal.Error; const NumberLiteralError = std.zig.number_literal.Error; const assert = std.debug.assert; const ArrayList = std.ArrayList; +const ArenaAllocator = std.heap.ArenaAllocator; /// Rename when adding or removing support for a type. const valid_types = {}; -pub const Error = union(enum) { - zoir: Zoir.CompileError, - type_check: Error.TypeCheckFailure, - - pub const Note = union(enum) { - zoir: Zoir.CompileError.Note, - type_check: TypeCheckFailure.Note, - - pub const Iterator = struct { - index: usize = 0, - err: Error, - diag: *const Diagnostics, - - pub fn next(self: *@This()) ?Note { - switch (self.err) { - .zoir => |err| { - if (self.index >= err.note_count) return null; - const note = err.getNotes(self.diag.zoir)[self.index]; - self.index += 1; - return .{ .zoir = note }; - }, - .type_check => |err| { - if (self.index >= err.getNoteCount()) return null; - const note = err.getNote(self.index); - self.index += 1; - return .{ .type_check = note }; - }, - } - } - }; - - fn formatMessage(self: []const u8, w: *std.Io.Writer) std.Io.Writer.Error!void { - // Just writes the string for now, but we're keeping this behind a formatter so we have - // the option to extend it in the future to print more advanced messages (like `Error` - // does) without breaking the API. - try w.writeAll(self); - } - - pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Alt([]const u8, Note.formatMessage) { - return .{ .data = switch (self) { - .zoir => |note| note.msg.get(diag.zoir), - .type_check => |note| note.msg, - } }; - } - - pub fn getLocation(self: Note, diag: *const Diagnostics) Ast.Location { - switch (self) { - .zoir => |note| return zoirErrorLocation(diag.ast, note.token, note.node_or_offset), - .type_check => |note| return diag.ast.tokenLocation(note.offset, note.token), - } - } - }; - - pub const Iterator = struct { - index: usize = 0, - diag: *const Diagnostics, - - pub fn next(self: *@This()) ?Error { - if (self.index < self.diag.zoir.compile_errors.len) { - const result: Error = .{ .zoir = self.diag.zoir.compile_errors[self.index] }; - self.index += 1; - return result; - } - - if (self.diag.type_check) |err| { - if (self.index == self.diag.zoir.compile_errors.len) { - const result: Error = .{ .type_check = err }; - self.index += 1; - return result; - } - } - - return null; - } - }; - - const TypeCheckFailure = struct { - const Note = struct { - token: Ast.TokenIndex, - offset: u32, - msg: []const u8, - owned: bool, - - fn deinit(self: @This(), gpa: Allocator) void { - if (self.owned) gpa.free(self.msg); - } - }; - - message: []const u8, - owned: bool, - token: Ast.TokenIndex, - offset: u32, - note: ?@This().Note, - - fn deinit(self: @This(), gpa: Allocator) void { - if (self.note) |note| note.deinit(gpa); - if (self.owned) gpa.free(self.message); - } - - fn getNoteCount(self: @This()) usize { - return @intFromBool(self.note != null); - } - - fn getNote(self: @This(), index: usize) @This().Note { - assert(index == 0); - return self.note.?; - } - }; - - const FormatMessage = struct { - err: Error, - diag: *const Diagnostics, +pub const Error = struct { + msg: []const u8, + loc: Ast.Location, + token: Ast.OptionalTokenIndex, + /// If `token == .none`, this is an `Ast.Node.Index`. + /// Otherwise, this is a byte offset into `token`. + node_or_offset: u32, + notes: []Note, + + const InitOptions = struct { + msg: []const u8, + token: Ast.OptionalTokenIndex, + node_or_offset: u32, + notes: []Note, }; - fn formatMessage(self: FormatMessage, w: *std.Io.Writer) std.Io.Writer.Error!void { - switch (self.err) { - .zoir => |err| try w.writeAll(err.msg.get(self.diag.zoir)), - .type_check => |tc| try w.writeAll(tc.message), - } - } - - pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Alt(FormatMessage, formatMessage) { - return .{ .data = .{ - .err = self, - .diag = diag, - } }; - } - - pub fn getLocation(self: @This(), diag: *const Diagnostics) Ast.Location { - return switch (self) { - .zoir => |err| return zoirErrorLocation( - diag.ast, - err.token, - err.node_or_offset, - ), - .type_check => |err| return diag.ast.tokenLocation(err.offset, err.token), + fn init(ast: *const Ast, options: InitOptions) @This() { + return .{ + .msg = options.msg, + .loc = astLoc(ast, options.token, options.node_or_offset), + .token = options.token, + .node_or_offset = options.node_or_offset, + .notes = options.notes, }; } - pub fn iterateNotes(self: @This(), diag: *const Diagnostics) Note.Iterator { - return .{ .err = self, .diag = diag }; - } - - fn zoirErrorLocation(ast: Ast, maybe_token: Ast.OptionalTokenIndex, node_or_offset: u32) Ast.Location { + fn astLoc( + ast: *const Ast, + maybe_token: Ast.OptionalTokenIndex, + node_or_offset: u32, + ) Ast.Location { if (maybe_token.unwrap()) |token| { - var location = ast.tokenLocation(0, token); - location.column += node_or_offset; - return location; + var loc = ast.tokenLocation(0, token); + loc.column += node_or_offset; + return loc; } else { const ast_node: Ast.Node.Index = @fromBackingInt(@intCast(node_or_offset)); const token = ast.nodeMainToken(ast_node); return ast.tokenLocation(0, token); } } + + pub const Note = struct { + msg: []const u8, + loc: Ast.Location, + token: Ast.OptionalTokenIndex, + /// If `token == .none`, this is an `Ast.Node.Index`. + /// Otherwise, this is a byte offset into `token`. + node_or_offset: u32, + + const InitOptions = struct { + msg: []const u8, + token: Ast.OptionalTokenIndex, + node_or_offset: u32, + }; + + fn init(ast: *const Ast, options: @This().InitOptions) @This() { + return .{ + .msg = options.msg, + .loc = astLoc(ast, options.token, options.node_or_offset), + .token = options.token, + .node_or_offset = options.node_or_offset, + }; + } + }; }; -/// Information about the success or failure of a parse. +/// Human readable information about the success or failure of a parse. pub const Diagnostics = struct { - ast: Ast = .{ - .source = "", - .tokens = .empty, - .nodes = .empty, - .extra_data = &.{}, - .mode = .zon, - .errors = &.{}, - }, - zoir: Zoir = .{ - .nodes = .empty, - .extra = &.{}, - .limbs = &.{}, - .string_bytes = &.{}, - .compile_errors = &.{}, - .error_notes = &.{}, - }, - type_check: ?Error.TypeCheckFailure = null, + arena: Allocator, + compile_errors: std.ArrayList(Error), - fn assertEmpty(self: Diagnostics) void { - assert(self.ast.tokens.len == 0); - assert(self.zoir.nodes.len == 0); - assert(self.type_check == null); - } - - pub fn deinit(self: *Diagnostics, gpa: Allocator) void { - self.ast.deinit(gpa); - self.zoir.deinit(gpa); - if (self.type_check) |tc| tc.deinit(gpa); - self.* = undefined; - } - - pub fn iterateErrors(self: *const Diagnostics) Error.Iterator { - return .{ .diag = self }; + pub fn init(arena: Allocator) @This() { + return .{ + .arena = arena, + .compile_errors = .empty, + }; } pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - var errors = self.iterateErrors(); - while (errors.next()) |err| { - const loc = err.getLocation(self); - const msg = err.fmtMessage(self); - try w.print("{d}:{d}: error: {f}\n", .{ loc.line + 1, loc.column + 1, msg }); + for (self.compile_errors.items) |e| { + try w.print("{d}:{d}: error: {s}\n", .{ + e.loc.line + 1, + e.loc.column + 1, + e.msg, + }); - var notes = err.iterateNotes(self); - while (notes.next()) |note| { - const note_loc = note.getLocation(self); - const note_msg = note.fmtMessage(self); - try w.print("{d}:{d}: note: {f}\n", .{ - note_loc.line + 1, - note_loc.column + 1, - note_msg, + for (e.notes) |note| { + try w.print("{d}:{d}: note: {s}\n", .{ + note.loc.line + 1, + note.loc.column + 1, + note.msg, }); } } } + + pub fn clear(self: *@This()) void { + self.compile_errors.clearRetainingCapacity(); + } }; pub const FromSliceOptions = struct { + /// Used for scratch allocations. gpa: Allocator, + /// The ZON source. source: [:0]const u8, /// Write human readable error messages here. diag: ?*Diagnostics, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, - /// If true, the parser cleans up partially parsed values on error. This requires some extra - /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because - /// you're using arena allocation.) - free_on_error: bool = true, }; /// Parses the given slice as ZON. @@ -257,7 +142,7 @@ pub const FromSliceOptions = struct { /// invalid or can not be deserialized into type `T`. /// /// When the parser returns `error.ParseZon`, it will also store a human readable explanation in -/// `diag` if non null. If diag is not null, it must be initialized to `.{}`. +/// `diag` if non null. /// /// Asserts at compile time that the result type doesn't contain pointers. As such, the result /// doesn't need to be freed. @@ -265,25 +150,13 @@ pub const FromSliceOptions = struct { /// An allocator is still required for temporary allocations made during parsing. pub fn fromSlice(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { comptime assert(!requiresAllocator(T)); - return fromSliceAlloc(T, options); -} - -pub fn UpdateFromSliceOptions(T: type) type { - return struct { - gpa: Allocator, - /// The value to update. - value: *T, - /// The ZON source. - source: [:0]const u8, - /// Write human readable error messages here. - diag: ?*Diagnostics, - /// If true, unknown fields do not error. - ignore_unknown_fields: bool = false, - /// If true, the old values of overriden fields are freed. You may want to turn this off if you - /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. - /// In this case, you can use arena allocation. - free_on_write: bool = true, - }; + return fromSliceAlloc(T, .{ + .gpa = options.gpa, + .arena = .failing, + .source = options.source, + .diag = options.diag, + .ignore_unknown_fields = options.ignore_unknown_fields, + }); } /// Similar to `fromSlice`, but updates an existing value `current`. Fields not specified by ZON are @@ -291,141 +164,139 @@ pub fn UpdateFromSliceOptions(T: type) type { /// failure, values that were updated prior to the failure are left updated. pub fn updateFromSlice( T: type, - options: UpdateFromSliceOptions(T), + value: *T, + options: FromSliceOptions, ) error{ OutOfMemory, ParseZon }!void { comptime assert(!requiresAllocator(T)); - try updateFromSliceAlloc(T, options); + try updateFromSliceAlloc(T, value, .{ + .gpa = options.gpa, + .arena = .failing, + .source = options.source, + .diag = options.diag, + .ignore_unknown_fields = options.ignore_unknown_fields, + }); } -pub const FromSliceAllocOptions = FromSliceOptions; +pub const FromSliceAllocOptions = struct { + /// Used for scratch allocations. + gpa: Allocator, + /// Owns the result. + arena: Allocator, + /// The ZON source. + source: [:0]const u8, + /// Write human readable error messages here. + diag: ?*Diagnostics, + /// If true, unknown fields do not error. + ignore_unknown_fields: bool = false, +}; /// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see /// `free`. -pub fn fromSliceAlloc(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { - var result: T = undefined; - try parseSliceAlloc(T, &result, options.gpa, options.source, options.diag, .{ - .ignore_unknown_fields = options.ignore_unknown_fields, - .ignore_missing_fields = false, - .free_on_error = options.free_on_error, - .free_on_write = false, +pub fn fromSliceAlloc(T: type, options: FromSliceAllocOptions) error{ OutOfMemory, ParseZon }!T { + var value: T = undefined; + try parseSliceAlloc(T, &value, .{ + .gpa = options.gpa, + .arena = options.arena, + .source = options.source, + .diag = options.diag, + .parser_options = .{ + .ignore_unknown_fields = options.ignore_unknown_fields, + .ignore_missing_fields = false, + }, }); - return result; + return value; } -pub const UpdateFromSliceAllocOptions = UpdateFromSliceOptions; - /// Like updateFromSlice`, but the result may contain pointers. pub fn updateFromSliceAlloc( T: type, - options: UpdateFromSliceAllocOptions(T), + value: *T, + options: FromSliceAllocOptions, ) error{ OutOfMemory, ParseZon }!void { - try parseSliceAlloc( - T, - options.value, - options.gpa, - options.source, - options.diag, - .{ + try parseSliceAlloc(T, value, .{ + .gpa = options.gpa, + .arena = options.arena, + .source = options.source, + .diag = options.diag, + .parser_options = .{ .ignore_unknown_fields = options.ignore_unknown_fields, .ignore_missing_fields = true, - .free_on_error = false, - .free_on_write = options.free_on_write, }, - ); + }); } +const ParseSliceAllocOptions = struct { + gpa: Allocator, + arena: Allocator, + source: [:0]const u8, + diag: ?*Diagnostics, + parser_options: Parser.Options, +}; + fn parseSliceAlloc( T: type, - out: *T, - gpa: Allocator, - source: [:0]const u8, - diag: ?*Diagnostics, - options: Parser.Options, + value: *T, + options: ParseSliceAllocOptions, ) error{ OutOfMemory, ParseZon }!void { - if (diag) |s| s.assertEmpty(); + var ast = try std.zig.Ast.parse(options.gpa, options.source, .{ .mode = .zon }); + defer ast.deinit(options.gpa); - var ast = try std.zig.Ast.parse(gpa, source, .{ .mode = .zon }); - defer if (diag == null) ast.deinit(gpa); - if (diag) |s| s.ast = ast; + var zoir = try ZonGen.generate(options.gpa, ast, .{ .parse_str_lits = false }); + defer zoir.deinit(options.gpa); - // If there's no diagnostics, Zoir exists for the lifetime of this function. If there is a - // diagnostics, ownership is transferred to diagnostics. - var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); - defer if (diag == null) zoir.deinit(gpa); - - if (diag) |s| s.* = .{}; - try parseZoirAlloc(T, out, gpa, ast, zoir, .root, diag, options); + try parseZoirAlloc(T, value, .{ + .arena = options.arena, + .ast = &ast, + .zoir = &zoir, + .node = .root, + .diag = options.diag, + .parser_options = options.parser_options, + }); } pub const FromZoirOptions = struct { /// The AST for this this ZON value. - ast: Ast, + ast: *const Ast, /// The zig object intermediate representation of this ZON value. - zoir: Zoir, + zoir: *const Zoir, /// The node to start parsing at. node: Zoir.Node.Index = .root, /// Write human readable error messages here. diag: ?*Diagnostics, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, - /// If true, the parser cleans up partially parsed values on error. This requires some extra - /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because - /// you're using arena allocation.) - free_on_error: bool = true, }; /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. pub fn fromZoir(T: type, options: FromZoirOptions) error{ParseZon}!T { comptime assert(!requiresAllocator(T)); return fromZoirAlloc(T, .{ - .gpa = .failing, + .arena = .failing, .ast = options.ast, .zoir = options.zoir, .node = options.node, .diag = options.diag, .ignore_unknown_fields = options.ignore_unknown_fields, - .free_on_error = options.free_on_error, }) catch |err| switch (err) { error.OutOfMemory => unreachable, // Checked by comptime assertion above else => |e| return e, }; } -pub fn UpdateFromZoirOptions(T: type) type { - return struct { - value: *T, - /// The AST for this ZON value. - ast: Ast, - /// The zig object intermediate representation of this ZON value. - zoir: Zoir, - /// The node to start parsing at. - node: Zoir.Node.Index = .root, - /// Write human readable error messages here. - diag: ?*Diagnostics, - /// If true, unknown fields do not error. - ignore_unknown_fields: bool = false, - /// If true, the old values of overriden fields are freed. You may want to turn this off if you - /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. - /// In this case, you can use arena allocation. - free_on_write: bool = true, - }; -} - /// Like `updateSlice`, but operates on `Zoir` instead of ZON source. pub fn updateFromZoir( T: type, - options: UpdateFromZoirOptions(T), + value: *T, + options: FromZoirOptions, ) error{ParseZon}!void { comptime assert(!requiresAllocator(T)); - updateFromZoirAlloc(T, .{ - .gpa = .failing, - .value = options.value, + updateFromZoirAlloc(T, value, .{ + .arena = .failing, .ast = options.ast, .zoir = options.zoir, .node = options.node, .diag = options.diag, .ignore_unknown_fields = options.ignore_unknown_fields, - .free_on_write = options.free_on_write, }) catch |err| switch (err) { error.OutOfMemory => unreachable, // Checked by comptime assertion above else => |e| return e, @@ -433,172 +304,103 @@ pub fn updateFromZoir( } pub const FromZoirAllocOptions = struct { - gpa: Allocator, + /// Owns the result. + arena: Allocator, /// The AST for this ZON value. - ast: Ast, + ast: *const Ast, /// The zig object intermediate representation of this ZON value. - zoir: Zoir, + zoir: *const Zoir, /// The node to start parsing at. node: Zoir.Node.Index = .root, /// Write human readable error messages here. diag: ?*Diagnostics, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, - /// If true, the parser cleans up partially parsed values on error. This requires some extra - /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because - /// you're using arena allocation.) - free_on_error: bool = true, }; /// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source. pub fn fromZoirAlloc(T: type, options: FromZoirAllocOptions) error{ OutOfMemory, ParseZon }!T { - var out: T = undefined; - try parseZoirAlloc( - T, - &out, - options.gpa, - options.ast, - options.zoir, - options.node, - options.diag, - .{ + var value: T = undefined; + try parseZoirAlloc(T, &value, .{ + .arena = options.arena, + .ast = options.ast, + .zoir = options.zoir, + .node = options.node, + .diag = options.diag, + .parser_options = .{ .ignore_unknown_fields = options.ignore_unknown_fields, .ignore_missing_fields = false, - .free_on_error = options.free_on_error, - .free_on_write = false, }, - ); - return out; -} - -pub fn UpdateFromZoirAllocOptions(T: type) type { - return struct { - gpa: Allocator, - value: *T, - ast: Ast, - zoir: Zoir, - node: Zoir.Node.Index = .root, - /// Write human readable error messages here. - diag: ?*Diagnostics, - /// If true, unknown fields do not error. - ignore_unknown_fields: bool = false, - /// If true, the old values of overriden fields are freed. You may want to turn this off if you - /// are updating a value that shouldn't be freed e.g. because it contains Zig string literals. - /// In this case, you can use arena allocation. - free_on_write: bool = true, - }; + }); + return value; } /// Like updateSliceAlloc`, but operates on `Zoir` instead of ZON source. pub fn updateFromZoirAlloc( T: type, - options: UpdateFromZoirAllocOptions(T), + value: *T, + options: FromZoirAllocOptions, ) error{ OutOfMemory, ParseZon }!void { - return parseZoirAlloc( - T, - options.value, - options.gpa, - options.ast, - options.zoir, - options.node, - options.diag, - .{ + return parseZoirAlloc(T, value, .{ + .arena = options.arena, + .ast = options.ast, + .zoir = options.zoir, + .node = options.node, + .diag = options.diag, + .parser_options = .{ .ignore_unknown_fields = options.ignore_unknown_fields, .ignore_missing_fields = true, - .free_on_error = false, - .free_on_write = options.free_on_write, }, - ); + }); } -fn parseZoirAlloc( - T: type, - out: *T, - gpa: Allocator, - ast: Ast, - zoir: Zoir, +const ParseZoirAllocOptions = struct { + arena: Allocator, + ast: *const Ast, + zoir: *const Zoir, node: Zoir.Node.Index, diag: ?*Diagnostics, - options: Parser.Options, + parser_options: Parser.Options, +}; + +fn parseZoirAlloc( + T: type, + value: *T, + options: ParseZoirAllocOptions, ) error{ OutOfMemory, ParseZon }!void { comptime assert(canParseType(T)); - if (diag) |s| { - s.assertEmpty(); - s.ast = ast; - s.zoir = zoir; - } - - if (zoir.hasCompileErrors()) { + if (options.zoir.hasCompileErrors()) { + if (options.diag) |s| { + for (options.zoir.compile_errors) |e| { + var notes: std.ArrayList(Error.Note) = .empty; + for (e.getNotes(options.zoir)) |note| { + try notes.append(s.arena, .init(options.ast, .{ + .msg = try s.arena.dupe(u8, note.msg.get(options.zoir)), + .token = note.token, + .node_or_offset = note.node_or_offset, + })); + } + try s.compile_errors.append(s.arena, .init(options.ast, .{ + .msg = try s.arena.dupe(u8, e.msg.get(options.zoir)), + .token = e.token, + .node_or_offset = e.node_or_offset, + .notes = try notes.toOwnedSlice(s.arena), + })); + } + } return error.ParseZon; } var parser: Parser = .{ - .gpa = gpa, - .ast = ast, - .zoir = zoir, - .options = options, - .diag = diag, + .arena = options.arena, + .ast = options.ast, + .zoir = options.zoir, + .options = options.parser_options, + .diag = options.diag, }; - try parser.parseExprInto(node, out); -} - -/// Frees ZON values. -/// -/// Provided for convenience, you may also free these values on your own using the same allocator -/// passed into the parser. -/// -/// Asserts at comptime that sufficient information is available via the type system to free this -/// value. Untagged unions, for example, will fail this assert. -pub fn free(gpa: Allocator, value: anytype) void { - const Value = @TypeOf(value); - - _ = valid_types; - switch (@typeInfo(Value)) { - .bool, .int, .float, .@"enum" => {}, - .pointer => |pointer| { - switch (pointer.size) { - .one => { - free(gpa, value.*); - gpa.destroy(value); - }, - .slice => { - for (value) |item| { - free(gpa, item); - } - gpa.free(value); - }, - .many, .c => comptime unreachable, - } - }, - .array => { - freeArray(gpa, @TypeOf(value), &value); - }, - .vector => |vector| { - const array: [vector.len]vector.child = value; - freeArray(gpa, @TypeOf(array), &array); - }, - .@"struct" => |@"struct"| inline for (@"struct".field_names) |field_name| { - free(gpa, @field(value, field_name)); - }, - .@"union" => |@"union"| if (@"union".tag_type == null) { - if (comptime requiresAllocator(Value)) unreachable; - } else switch (value) { - inline else => |_, tag| { - free(gpa, @field(value, @tagName(tag))); - }, - }, - .optional => if (value) |some| { - free(gpa, some); - }, - .void => {}, - else => comptime unreachable, - } -} - -fn freeArray(gpa: Allocator, comptime A: type, array: *const A) void { - for (array) |elem| free(gpa, elem); + try parser.parseExprInto(options.node, value); } fn requiresAllocator(T: type) bool { @@ -623,17 +425,15 @@ fn requiresAllocator(T: type) bool { } const Parser = struct { - gpa: Allocator, - ast: Ast, - zoir: Zoir, + arena: Allocator, + ast: *const Ast, + zoir: *const Zoir, diag: ?*Diagnostics, options: Parser.Options, pointer_depth: usize = 0, pub const Options = struct { ignore_unknown_fields: bool, - free_on_error: bool, - free_on_write: bool, ignore_missing_fields: bool, }; @@ -654,37 +454,36 @@ const Parser = struct { out: anytype, ) ParseExprInnerError!void { if (@TypeOf(out.*) == Zoir.Node.Index) { - self.write(out, node); + out.* = node; return; } switch (@typeInfo(@TypeOf(out.*))) { .optional => |optional| if (node.get(self.zoir) == .null) { - self.write(out, null); + out.* = null; } else { var new: optional.child = undefined; try self.parseExprInnerInto(node, &new); - self.write(out, new); + out.* = new; }, - .bool => self.write(out, try self.parseBool(node)), - .int => self.write(out, try self.parseInt(@TypeOf(out.*), node)), - .float => self.write(out, try self.parseFloat(@TypeOf(out.*), node)), - .@"enum" => self.write(out, try self.parseEnumLiteral(@TypeOf(out.*), node)), + .bool => out.* = try self.parseBool(node), + .int => out.* = try self.parseInt(@TypeOf(out.*), node), + .float => out.* = try self.parseFloat(@TypeOf(out.*), node), + .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), .pointer => |pointer| switch (pointer.size) { .one => { - const slice = try self.gpa.alignedAlloc( + const slice = try self.arena.alignedAlloc( pointer.child, if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, 1, ); - errdefer self.gpa.free(slice); const new = &slice[0]; { self.pointer_depth += 1; defer self.pointer_depth -= 1; try self.parseExprInnerInto(node, new); } - self.write(out, new); + out.* = new; }, .slice => { const new = b: { @@ -693,7 +492,7 @@ const Parser = struct { const new = try self.parseSlicePointer(@TypeOf(out.*), node); break :b new; }; - self.write(out, new); + out.* = new; }, else => comptime unreachable, }, @@ -867,7 +666,7 @@ const Parser = struct { var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node); if (pointer.sentinel() != null) size_hint += 1; - var aw: std.Io.Writer.Allocating = .init(self.gpa); + var aw: std.Io.Writer.Allocating = .init(self.arena); try aw.ensureUnusedCapacity(size_hint); defer aw.deinit(); const result = ZonGen.parseStrLit(self.ast, ast_node, &aw.writer) catch return error.OutOfMemory; @@ -906,21 +705,15 @@ const Parser = struct { } // Allocate the slice - const slice = try self.gpa.allocWithOptions( + const slice = try self.arena.allocWithOptions( pointer.child, nodes.len, .fromByteUnitsOptional(pointer.attrs.@"align"), pointer.sentinel(), ); - errdefer self.gpa.free(slice); // Parse the elements and return the slice for (slice, 0..) |*elem, i| { - errdefer if (self.options.free_on_error) { - for (slice[0..i]) |item| { - free(self.gpa, item); - } - }; try self.parseExprInto(nodes.at(@intCast(i)), elem); } @@ -953,13 +746,6 @@ const Parser = struct { // Parse the elements and return the array inline for (out, 0..) |*elem, i| { - // If we fail to parse this field, free all fields before it - errdefer if (self.options.free_on_error) { - for (out[0..i]) |item| { - free(self.gpa, item); - } - }; - try self.parseExprInto(nodes.at(@intCast(i)), elem); } @@ -972,7 +758,7 @@ const Parser = struct { const vector = @typeInfo(@TypeOf(out.*)).vector; var array: [vector.len]vector.child = undefined; try self.parseArrayInto(node, &array); - self.write(out, array); + out.* = array; } fn parseStructInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { @@ -998,22 +784,6 @@ const Parser = struct { // Parse the struct var field_found: [info.field_names.len]bool = @splat(false); - - // If we fail partway through, free all already initialized fields - var initialized: usize = 0; - errdefer if (self.options.free_on_error and info.field_names.len > 0) { - for (fields.names[0..initialized]) |name_runtime| { - switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) { - inline 0...(info.field_names.len - 1) => |name_index| { - const name = info.field_names[name_index]; - free(self.gpa, @field(out, name)); - }, - else => unreachable, // Can't be out of bounds - } - } - }; - - // Fill in the fields we found for (0..fields.names.len) |i| { const name = fields.names[i].get(self.zoir); const field_index = field_indices.get(name) orelse { @@ -1040,8 +810,6 @@ const Parser = struct { }, else => unreachable, // Can't be out of bounds } - - initialized += 1; } // Fill in any missing default fields @@ -1089,14 +857,6 @@ const Parser = struct { return self.failNodeFmt(node, "missing tuple field with index {}", .{i}); } } else { - // If we fail to parse this field, free all fields before it - errdefer if (self.options.free_on_error) { - inline for (0..i) |j| { - if (j >= i) break; - free(self.gpa, out[j]); - } - }; - if (info.field_attrs[i].@"comptime") { return self.failComptimeField(node, i); } else { @@ -1144,7 +904,7 @@ const Parser = struct { return self.failNode(node, "expected union"); // Instantiate the union - self.write(out, @unionInit(@TypeOf(out.*), @"union".field_names[i], {})); + out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], {}); }, else => unreachable, // Can't be out of bounds } @@ -1166,7 +926,7 @@ const Parser = struct { if (@"union".field_types[i] == void) { return self.failNode(field_val, "expected type 'void'"); } else { - self.write(out, @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined)); + out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined); try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i])); } }, @@ -1185,7 +945,7 @@ const Parser = struct { args: anytype, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - return self.failTokenFmtNote(token, offset, fmt, args, null); + return self.failTokenFmtNote(token, offset, fmt, args, &.{}); } fn failTokenFmtNote( @@ -1194,20 +954,16 @@ const Parser = struct { offset: u32, comptime fmt: []const u8, args: anytype, - note: ?Error.TypeCheckFailure.Note, + notes: []Error.Note, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); comptime assert(args.len > 0); - if (self.diag) |s| s.type_check = .{ - .token = token, - .offset = offset, - .message = std.fmt.allocPrint(self.gpa, fmt, args) catch |err| { - if (note) |n| n.deinit(self.gpa); - return err; - }, - .owned = true, - .note = note, - }; + if (self.diag) |s| try s.compile_errors.append(s.arena, .init(self.ast, .{ + .token = .fromToken(token), + .node_or_offset = offset, + .msg = try std.fmt.allocPrint(s.arena, fmt, args), + .notes = notes, + })); return error.ParseZon; } @@ -1224,27 +980,26 @@ const Parser = struct { fn failToken( self: @This(), - failure: Error.TypeCheckFailure, - ) error{ParseZon} { + failure: Error, + ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - if (self.diag) |s| s.type_check = failure; + if (self.diag) |s| try s.compile_errors.append(s.arena, failure); return error.ParseZon; } fn failNode( self: @This(), node: Zoir.Node.Index, - message: []const u8, - ) error{ParseZon} { + msg: []const u8, + ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); const token = self.ast.nodeMainToken(node.getAstNode(self.zoir)); - return self.failToken(.{ - .token = token, - .offset = 0, - .message = message, - .owned = false, - .note = null, - }); + return self.failToken(.init(self.ast, .{ + .token = .fromToken(token), + .node_or_offset = 0, + .msg = msg, + .notes = &.{}, + })); } fn failCannotRepresent( @@ -1265,8 +1020,7 @@ const Parser = struct { name: []const u8, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - if (self.diag == null) return error.ParseZon; - const gpa = self.gpa; + const diag = self.diag orelse return error.ParseZon; const token = if (field) |f| b: { var buf: [2]Ast.Node.Index = undefined; const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?; @@ -1275,38 +1029,35 @@ const Parser = struct { } else self.ast.nodeMainToken(node.getAstNode(self.zoir)); switch (@typeInfo(T)) { inline .@"struct", .@"union", .@"enum" => |info| { - const note: Error.TypeCheckFailure.Note = if (info.field_names.len == 0) b: { - break :b .{ - .token = token, - .offset = 0, + var notes: std.ArrayList(Error.Note) = .empty; + if (info.field_names.len == 0) { + try notes.append(diag.arena, .init(self.ast, .{ + .token = .fromToken(token), + .node_or_offset = 0, .msg = "none expected", - .owned = false, - }; - } else b: { - const msg = "supported: "; - var buf: std.ArrayList(u8) = try .initCapacity(gpa, 64); - defer buf.deinit(gpa); - try buf.appendSlice(gpa, msg); + })); + } else { + var aw: std.Io.Writer.Allocating = .init(diag.arena); + aw.writer.writeAll("supported: ") catch return error.OutOfMemory; inline for (info.field_names, 0..) |field_name, i| { - if (i != 0) try buf.appendSlice(gpa, ", "); - try buf.print(gpa, "'{f}'", .{std.zig.fmtIdFlags(field_name, .{ + if (i != 0) aw.writer.writeAll(", ") catch return error.OutOfMemory; + aw.writer.print("'{f}'", .{std.zig.fmtIdFlags(field_name, .{ .allow_primitive = true, .allow_underscore = true, - })}); + })}) catch return error.OutOfMemory; } - break :b .{ - .token = token, - .offset = 0, - .msg = try buf.toOwnedSlice(gpa), - .owned = true, - }; - }; + try notes.append(diag.arena, .init(self.ast, .{ + .token = .fromToken(token), + .node_or_offset = 0, + .msg = try aw.toOwnedSlice(), + })); + } return self.failTokenFmtNote( token, 0, "unexpected {s} '{s}'", .{ item_kind, name }, - note, + try notes.toOwnedSlice(diag.arena), ); }, else => comptime unreachable, @@ -1333,23 +1084,12 @@ const Parser = struct { const value_node = array_init.ast.elements[field]; break :b self.ast.firstToken(value_node); }; - return self.failToken(.{ - .token = token, - .offset = 0, - .message = "cannot initialize comptime field", - .owned = false, - .note = null, - }); - } - - /// Write to out, freeing the old value first if necessary. - fn write(self: *@This(), out: anytype, new: @TypeOf(out.*)) void { - if (@typeInfo(@TypeOf(out)) == .pointer) { - if (self.options.free_on_write and self.pointer_depth == 0) { - free(self.gpa, out.*); - } - } - out.* = new; + return self.failToken(.init(self.ast, .{ + .token = .fromToken(token), + .node_or_offset = 0, + .msg = "cannot initialize comptime field", + .notes = &.{}, + })); } }; @@ -1511,17 +1251,26 @@ test "std.zon requiresAllocator" { test "std.zon ast errors" { const gpa = std.testing.allocator; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(struct {}, .{ .gpa = gpa, .source = ".{.x = 1 .y = 2}", .diag = &diag }), + fromSlice(struct {}, .{ + .gpa = gpa, + .source = ".{.x = 1 .y = 2}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{diag}); } test "std.zon comments" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); try std.testing.expectEqual(@as(u8, 10), fromSlice(u8, .{ .gpa = gpa, @@ -1534,8 +1283,7 @@ test "std.zon comments" { })); { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = @@ -1555,10 +1303,13 @@ test "std.zon comments" { test "std.zon failure/oom formatting" { const gpa = std.testing.allocator; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.OutOfMemory, fromSliceAlloc([]const u8, .{ .gpa = .failing, + .arena = arena, .source = "\"foo\"", .diag = &diag, })); @@ -1566,10 +1317,11 @@ test "std.zon failure/oom formatting" { } test "std.zon fromSliceAlloc syntax error" { + const gpa = std.testing.allocator; try std.testing.expectError( error.ParseZon, fromSlice(u8, .{ - .gpa = std.testing.allocator, + .gpa = gpa, .source = ".{", .diag = null, }), @@ -1578,6 +1330,9 @@ test "std.zon fromSliceAlloc syntax error" { test "std.zon optional" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Basic usage { @@ -1599,22 +1354,26 @@ test "std.zon optional" { { const none = try fromSliceAlloc(?[]const u8, .{ .gpa = gpa, + .arena = arena, .source = "null", .diag = null, }); try std.testing.expect(none == null); const some = try fromSliceAlloc(?[]const u8, .{ .gpa = gpa, + .arena = arena, .source = "\"foo\"", .diag = null, }); - defer free(gpa, some); try std.testing.expectEqualStrings("foo", some.?); } } test "std.zon unions" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Unions { @@ -1666,6 +1425,7 @@ test "std.zon unions" { const noalloc = try fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".{.baz = false}", .diag = null, }); @@ -1673,21 +1433,25 @@ test "std.zon unions" { const alloc = try fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".{.bar = \"qux\"}", .diag = null, }); - defer free(gpa, alloc); try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc); } // Unknown field { const Union = union { x: f32, y: f32 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, .{ .gpa = gpa, .source = ".{.z=2.5}", .diag = &diag }), + fromSliceAlloc(Union, .{ + .gpa = gpa, + .arena = arena, + .source = ".{.z=2.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( \\1:4: error: unexpected field 'z' @@ -1702,11 +1466,15 @@ test "std.zon unions" { // Explicit void field { const Union = union(enum) { x: void }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, .{ .gpa = gpa, .source = ".{.x=1}", .diag = &diag }), + fromSliceAlloc(Union, .{ + .gpa = gpa, + .arena = arena, + .source = ".{.x=1}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{diag}); } @@ -1714,12 +1482,12 @@ test "std.zon unions" { // Extra field { const Union = union { x: f32, y: bool }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.5, .y = true}", .diag = &diag, }), @@ -1730,11 +1498,15 @@ test "std.zon unions" { // No fields { const Union = union { x: f32, y: bool }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, .{ .gpa = gpa, .source = ".{}", .diag = &diag }), + fromSliceAlloc(Union, .{ + .gpa = gpa, + .arena = arena, + .source = ".{}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } @@ -1742,10 +1514,10 @@ test "std.zon unions" { // Enum literals cannot coerce into untagged unions { const Union = union { x: void }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".x", .diag = &diag, })); @@ -1755,10 +1527,10 @@ test "std.zon unions" { // Unknown field for enum literal coercion { const Union = union(enum) { x: void }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".y", .diag = &diag, })); @@ -1775,10 +1547,10 @@ test "std.zon unions" { // Non void field for enum literal coercion { const Union = union(enum) { x: f32 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".x", .diag = &diag, })); @@ -1788,6 +1560,9 @@ test "std.zon unions" { test "std.zon structs" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Structs (various sizes tested since they're parsed differently) { @@ -1831,21 +1606,24 @@ test "std.zon structs" { const parsed = try fromSliceAlloc(Foo, .{ .gpa = gpa, + .arena = arena, .source = ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed); } // Unknown field { const Vec2 = struct { x: f32, y: f32 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .z=2.5}", .diag = &diag }), + fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x=1.5, .z=2.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( \\1:12: error: unexpected field 'z' @@ -1860,11 +1638,14 @@ test "std.zon structs" { // Duplicate field { const Vec2 = struct { x: f32, y: f32 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .x=2.5, .x=3.5}", .diag = &diag }), + fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x=1.5, .x=2.5, .x=3.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( \\1:4: error: duplicate struct field name @@ -1888,11 +1669,14 @@ test "std.zon structs" { // Unknown field when struct has no fields (regression test) { const Vec2 = struct {}; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .z=2.5}", .diag = &diag }), + fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x=1.5, .z=2.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( \\1:4: error: unexpected field 'x' @@ -1904,11 +1688,14 @@ test "std.zon structs" { // Missing field { const Vec2 = struct { x: f32, y: f32 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5}", .diag = &diag }), + fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x=1.5}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{diag}); } @@ -1916,22 +1703,29 @@ test "std.zon structs" { // Default field { const Vec2 = struct { x: f32, y: f32 = 1.5 }; - const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2}", .diag = null }); + const parsed = try fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x = 1.2}", + .diag = null, + }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } // Comptime field { const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; - const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2}", .diag = null }); + const parsed = try fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{.x = 1.2}", + .diag = null, + }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } // Comptime field assignment { const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); const parsed = fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2, .y = 1.5}", @@ -1971,8 +1765,7 @@ test "std.zon structs" { { // Structs { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); const parsed = fromSlice(struct {}, .{ .gpa = gpa, .source = "Empty{}", @@ -1988,8 +1781,7 @@ test "std.zon structs" { // Arrays { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); const parsed = fromSlice([3]u8, .{ .gpa = gpa, .source = "[3]u8{1, 2, 3}", @@ -2005,10 +1797,10 @@ test "std.zon structs" { // Slices { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); const parsed = fromSliceAlloc([]u8, .{ .gpa = gpa, + .arena = arena, .source = "[]u8{1, 2, 3}", .diag = &diag, }); @@ -2022,8 +1814,7 @@ test "std.zon structs" { // Tuples { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); const parsed = fromSlice(struct { u8, u8, u8 }, .{ .gpa = gpa, .source = "Tuple{1, 2, 3}", @@ -2039,8 +1830,7 @@ test "std.zon structs" { // Nested { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); const parsed = fromSlice(struct {}, .{ .gpa = gpa, .source = ".{ .x = Tuple{1, 2, 3} }", @@ -2058,6 +1848,9 @@ test "std.zon structs" { test "std.zon tuples" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Structs (various sizes tested since they're parsed differently) { @@ -2100,21 +1893,24 @@ test "std.zon tuples" { const Tuple = struct { []const u8, []const u8 }; const parsed = try fromSliceAlloc(Tuple, .{ .gpa = gpa, + .arena = arena, .source = ".{\"hello\", \"world\"}", .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed); } // Extra field { const Tuple = struct { f32, bool }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, .{ .gpa = gpa, .source = ".{0.5, true, 123}", .diag = &diag }), + fromSlice(Tuple, .{ + .gpa = gpa, + .source = ".{0.5, true, 123}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{diag}); } @@ -2122,11 +1918,14 @@ test "std.zon tuples" { // Extra field { const Tuple = struct { f32, bool }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, .{ .gpa = gpa, .source = ".{0.5}", .diag = &diag }), + fromSlice(Tuple, .{ + .gpa = gpa, + .source = ".{0.5}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:2: error: missing tuple field with index 1\n", @@ -2138,11 +1937,14 @@ test "std.zon tuples" { // Tuple with unexpected field names { const Tuple = struct { f32 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, .{ .gpa = gpa, .source = ".{.foo = 10.0}", .diag = &diag }), + fromSlice(Tuple, .{ + .gpa = gpa, + .source = ".{.foo = 10.0}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{diag}); } @@ -2150,11 +1952,14 @@ test "std.zon tuples" { // Struct with missing field names { const Struct = struct { foo: f32 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Struct, .{ .gpa = gpa, .source = ".{10.0}", .diag = &diag }), + fromSlice(Struct, .{ + .gpa = gpa, + .source = ".{10.0}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{diag}); } @@ -2162,16 +1967,23 @@ test "std.zon tuples" { // Comptime field { const Vec2 = struct { f32, comptime f32 = 1.5 }; - const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{ 1.2 }", .diag = null }); + const parsed = try fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{ 1.2 }", + .diag = null, + }); try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed); } // Comptime field assignment { const Vec2 = struct { f32, comptime f32 = 1.5 }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); - const parsed = fromSlice(Vec2, .{ .gpa = gpa, .source = ".{ 1.2, 1.5}", .diag = &diag }); + var diag: Diagnostics = .init(arena); + const parsed = fromSlice(Vec2, .{ + .gpa = gpa, + .source = ".{ 1.2, 1.5}", + .diag = &diag, + }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:9: error: cannot initialize comptime field @@ -2183,6 +1995,9 @@ test "std.zon tuples" { // Test sizes 0 to 3 since small sizes get parsed differently test "std.zon arrays and slices" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Literals { @@ -2236,50 +2051,50 @@ test "std.zon arrays and slices" { { const zero = try fromSliceAlloc([]const u8, .{ .gpa = gpa, + .arena = arena, .source = ".{}", .diag = null, }); - defer free(gpa, zero); try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero); const one = try fromSliceAlloc([]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a'}", .diag = null, }); - defer free(gpa, one); try std.testing.expectEqualSlices(u8, &.{'a'}, one); const two = try fromSliceAlloc([]const u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b'}", .diag = null, }); - defer free(gpa, two); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two); const two_comma = try fromSliceAlloc([]const u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b',}", .diag = null, }); - defer free(gpa, two_comma); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma); const three = try fromSliceAlloc([]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b', 'c'}", .diag = null, }); - defer free(gpa, three); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three); const sentinel = try fromSliceAlloc([:'z']const u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b', 'c'}", .diag = null, }); - defer free(gpa, sentinel); const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel); } @@ -2291,10 +2106,10 @@ test "std.zon arrays and slices" { { const parsed = try fromSliceAlloc([1][]const u8, .{ .gpa = gpa, + .arena = arena, .source = ".{\"abc\"}", .diag = null, }); - defer free(gpa, parsed); const expected: [1][]const u8 = .{"abc"}; try std.testing.expectEqualDeep(expected, parsed); } @@ -2303,10 +2118,10 @@ test "std.zon arrays and slices" { { const parsed = try fromSliceAlloc([]const []const u8, .{ .gpa = gpa, + .arena = arena, .source = ".{\"abc\"}", .diag = null, }); - defer free(gpa, parsed); const expected: []const []const u8 = &.{"abc"}; try std.testing.expectEqualDeep(expected, parsed); } @@ -2330,10 +2145,10 @@ test "std.zon arrays and slices" { { const sentinel = try fromSliceAlloc([:2]align(4) u8, .{ .gpa = gpa, + .arena = arena, .source = ".{1}", .diag = null, }); - defer free(gpa, sentinel); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); try std.testing.expectEqual(@as(u8, 2), sentinel[1]); @@ -2342,11 +2157,14 @@ test "std.zon arrays and slices" { // Expect 0 find 3 { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([0]u8, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", .diag = &diag }), + fromSlice([0]u8, .{ + .gpa = gpa, + .source = ".{'a', 'b', 'c'}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:3: error: index 0 outside of array of length 0\n", @@ -2357,11 +2175,14 @@ test "std.zon arrays and slices" { // Expect 1 find 2 { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([1]u8, .{ .gpa = gpa, .source = ".{'a', 'b'}", .diag = &diag }), + fromSlice([1]u8, .{ + .gpa = gpa, + .source = ".{'a', 'b'}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:8: error: index 1 outside of array of length 1\n", @@ -2372,11 +2193,14 @@ test "std.zon arrays and slices" { // Expect 2 find 1 { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([2]u8, .{ .gpa = gpa, .source = ".{'a'}", .diag = &diag }), + fromSlice([2]u8, .{ + .gpa = gpa, + .source = ".{'a'}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:2: error: expected 2 array elements; found 1\n", @@ -2387,11 +2211,14 @@ test "std.zon arrays and slices" { // Expect 3 find 0 { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([3]u8, .{ .gpa = gpa, .source = ".{}", .diag = &diag }), + fromSlice([3]u8, .{ + .gpa = gpa, + .source = ".{}", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:2: error: expected 3 array elements; found 0\n", @@ -2404,23 +2231,26 @@ test "std.zon arrays and slices" { { // Array { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([3]bool, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", .diag = &diag }), + fromSlice([3]bool, .{ + .gpa = gpa, + .source = ".{'a', 'b', 'c'}", + .diag = &diag, + }), ); try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); } // Slice { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc([]bool, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b', 'c'}", .diag = &diag, }), @@ -2433,22 +2263,24 @@ test "std.zon arrays and slices" { { // Array { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([3]u8, .{ .gpa = gpa, .source = "'a'", .diag = &diag }), + fromSlice([3]u8, .{ + .gpa = gpa, + .source = "'a'", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } // Slice { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .source = "'a'", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2456,11 +2288,10 @@ test "std.zon arrays and slices" { // Address of is not allowed (indirection for slices in ZON is implicit) { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .source = " &.{'a', 'b', 'c'}", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .diag = &diag }), ); try std.testing.expectFmt( "1:3: error: pointers are not available in ZON\n", @@ -2472,15 +2303,18 @@ test "std.zon arrays and slices" { test "std.zon string literal" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Basic string literal { const parsed = try fromSliceAlloc([]const u8, .{ .gpa = gpa, + .arena = arena, .source = "\"abc\"", .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed); } @@ -2488,10 +2322,10 @@ test "std.zon string literal" { { const parsed = try fromSliceAlloc([]const u8, .{ .gpa = gpa, + .arena = arena, .source = "\"ab\\nc\"", .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed); } @@ -2499,31 +2333,29 @@ test "std.zon string literal" { { const parsed = try fromSliceAlloc([]const u8, .{ .gpa = gpa, + .arena = arena, .source = "\"ab\\x00c\"", .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed); } // Passing string literal to a mutable slice { { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .source = "\"abcd\"", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .source = "\\\\abcd", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2536,21 +2368,27 @@ test "std.zon string literal" { defer ast.deinit(gpa); var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(gpa); - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([4:0]u8, .{ .gpa = gpa, .source = "\"abcd\"", .diag = &diag }), + fromSlice([4:0]u8, .{ + .gpa = gpa, + .source = "\"abcd\"", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([4:0]u8, .{ .gpa = gpa, .source = "\\\\abcd", .diag = &diag }), + fromSlice([4:0]u8, .{ + .gpa = gpa, + .source = "\\\\abcd", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2561,10 +2399,10 @@ test "std.zon string literal" { { const parsed: [:0]const u8 = try fromSliceAlloc([:0]const u8, .{ .gpa = gpa, + .arena = arena, .source = "\"abc\"", .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); } @@ -2572,10 +2410,10 @@ test "std.zon string literal" { { const parsed: [:0]const u8 = try fromSliceAlloc([:0]const u8, .{ .gpa = gpa, + .arena = arena, .source = "\\\\abc", .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); } @@ -2584,21 +2422,19 @@ test "std.zon string literal" { // Other value terminated slices { { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .source = "\"foo\"", .diag = &diag }), + fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .source = "\\\\foo", .diag = &diag }), + fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2606,33 +2442,30 @@ test "std.zon string literal" { // Expecting string literal, getting something else { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{diag}); } // Expecting string literal, getting an incompatible tuple { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, .{ .gpa = gpa, .source = ".{false}", .diag = &diag }), + fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .diag = &diag }), ); try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{diag}); } // Invalid string literal { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .source = "\"\\a\"", .diag = &diag }), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .diag = &diag }), ); try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{diag}); } @@ -2640,21 +2473,19 @@ test "std.zon string literal" { // Slice wrong child type { { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .source = "\"a\"", .diag = &diag }), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .source = "\\\\a", .diag = &diag }), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .diag = &diag }), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2663,12 +2494,12 @@ test "std.zon string literal" { // Bad alignment { { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc([]align(2) const u8, .{ .gpa = gpa, + .arena = arena, .source = "\"abc\"", .diag = &diag, }), @@ -2677,12 +2508,12 @@ test "std.zon string literal" { } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc([]align(2) const u8, .{ .gpa = gpa, + .arena = arena, .source = "\\\\abc", .diag = &diag, }), @@ -2702,6 +2533,7 @@ test "std.zon string literal" { }; const parsed = try fromSliceAlloc(S, .{ .gpa = gpa, + .arena = arena, .source = \\.{ \\ .message = @@ -2722,7 +2554,6 @@ test "std.zon string literal" { , .diag = null, }); - defer free(gpa, parsed); try std.testing.expectEqualStrings( "hello, world!\nthis is a multiline string!\n\n...", parsed.message, @@ -2735,6 +2566,9 @@ test "std.zon string literal" { test "std.zon enum literals" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); const Enum = enum { foo, @@ -2761,16 +2595,23 @@ test "std.zon enum literals" { })); try std.testing.expectEqual( Enum.@"ab\nc", - try fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"ab\\nc\"", .diag = null }), + try fromSlice(Enum, .{ + .gpa = gpa, + .source = ".@\"ab\\nc\"", + .diag = null, + }), ); // Bad tag { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ .gpa = gpa, .source = ".qux", .diag = &diag }), + fromSlice(Enum, .{ + .gpa = gpa, + .source = ".qux", + .diag = &diag, + }), ); try std.testing.expectFmt( \\1:2: error: unexpected enum literal 'qux' @@ -2784,11 +2625,14 @@ test "std.zon enum literals" { // Bad tag that's too long for parser { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"foobarbaz\"", .diag = &diag }), + fromSlice(Enum, .{ + .gpa = gpa, + .source = ".@\"foobarbaz\"", + .diag = &diag, + }), ); try std.testing.expectFmt( \\1:2: error: unexpected enum literal 'foobarbaz' @@ -2802,22 +2646,28 @@ test "std.zon enum literals" { // Bad type { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSlice(Enum, .{ + .gpa = gpa, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{diag}); } // Test embedded nulls in an identifier { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"\\x00\"", .diag = &diag }), + fromSlice(Enum, .{ + .gpa = gpa, + .source = ".@\"\\x00\"", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:2: error: identifier cannot contain null bytes\n", @@ -2829,6 +2679,9 @@ test "std.zon enum literals" { test "std.zon parse bool" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Correct bools try std.testing.expectEqual(true, try fromSlice(bool, .{ @@ -2844,11 +2697,14 @@ test "std.zon parse bool" { // Errors { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(bool, .{ .gpa = gpa, .source = " foo", .diag = &diag }), + fromSlice(bool, .{ + .gpa = gpa, + .source = " foo", + .diag = &diag, + }), ); try std.testing.expectFmt( \\1:2: error: invalid expression @@ -2858,8 +2714,7 @@ test "std.zon parse bool" { , "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(bool, .{ .gpa = gpa, .source = "123", @@ -2893,6 +2748,9 @@ test "std.zon intFromFloatExact" { test "std.zon parse int" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Test various numbers and types try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, .{ @@ -2984,8 +2842,7 @@ test "std.zon parse int" { }), ); { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ .gpa = gpa, .source = "36893488147419103232", @@ -2998,8 +2855,7 @@ test "std.zon parse int" { ); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ .gpa = gpa, .source = "-36893488147419103233", @@ -3105,8 +2961,7 @@ test "std.zon parse int" { // Number with invalid character in the middle { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "32a32", @@ -3121,8 +2976,7 @@ test "std.zon parse int" { // Failing to parse as int { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "true", @@ -3133,8 +2987,7 @@ test "std.zon parse int" { // Failing because an int is out of range { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "256", @@ -3149,8 +3002,7 @@ test "std.zon parse int" { // Failing because a negative int is out of range { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "-129", @@ -3165,8 +3017,7 @@ test "std.zon parse int" { // Failing because an unsigned int is negative { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "-1", @@ -3181,8 +3032,7 @@ test "std.zon parse int" { // Failing because a float is non-whole { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "1.5", @@ -3197,8 +3047,7 @@ test "std.zon parse int" { // Failing because a float is negative { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "-1.0", @@ -3213,8 +3062,7 @@ test "std.zon parse int" { // Negative integer zero { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "-0", @@ -3230,8 +3078,7 @@ test "std.zon parse int" { // Negative integer zero casted to float { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-0", @@ -3261,8 +3108,7 @@ test "std.zon parse int" { // Double negation is not allowed { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "--2", @@ -3276,11 +3122,14 @@ test "std.zon parse int" { } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(f32, .{ .gpa = gpa, .source = "--2.0", .diag = &diag }), + fromSlice(f32, .{ + .gpa = gpa, + .source = "--2.0", + .diag = &diag, + }), ); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", @@ -3291,8 +3140,7 @@ test "std.zon parse int" { // Invalid int literal { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "0xg", @@ -3303,8 +3151,7 @@ test "std.zon parse int" { // Notes on invalid int literal { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "0123", @@ -3320,10 +3167,12 @@ test "std.zon parse int" { test "std.zon negative char" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-'a'", @@ -3336,8 +3185,7 @@ test "std.zon negative char" { ); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i16, .{ .gpa = gpa, .source = "-'a'", @@ -3353,6 +3201,9 @@ test "std.zon negative char" { test "std.zon parse float" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Test decimals try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, .{ @@ -3362,11 +3213,19 @@ test "std.zon parse float" { })); try std.testing.expectEqual( @as(f32, 123.456), - try fromSlice(f32, .{ .gpa = gpa, .source = "123.456", .diag = null }), + try fromSlice(f32, .{ + .gpa = gpa, + .source = "123.456", + .diag = null, + }), ); try std.testing.expectEqual( @as(f64, -123.456), - try fromSlice(f64, .{ .gpa = gpa, .source = "-123.456", .diag = null }), + try fromSlice(f64, .{ + .gpa = gpa, + .source = "-123.456", + .diag = null, + }), ); try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, .{ .gpa = gpa, @@ -3491,8 +3350,7 @@ test "std.zon parse float" { // Negative nan not allowed { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-nan", @@ -3507,8 +3365,7 @@ test "std.zon parse float" { // nan as int not allowed { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "nan", @@ -3519,8 +3376,7 @@ test "std.zon parse float" { // nan as int not allowed { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "nan", @@ -3531,8 +3387,7 @@ test "std.zon parse float" { // inf as int not allowed { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "inf", @@ -3543,8 +3398,7 @@ test "std.zon parse float" { // -inf as int not allowed { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "-inf", @@ -3555,8 +3409,7 @@ test "std.zon parse float" { // Bad identifier as float { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "foo", @@ -3571,8 +3424,7 @@ test "std.zon parse float" { } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-foo", @@ -3587,17 +3439,25 @@ test "std.zon parse float" { // Non float as float { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(f32, .{ .gpa = gpa, .source = "\"foo\"", .diag = &diag }), + fromSlice(f32, .{ + .gpa = gpa, + .source = "\"foo\"", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{diag}); } } test "std.zon free on error" { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + // Test freeing partially allocated structs { const Struct = struct { @@ -3606,7 +3466,8 @@ test "std.zon free on error" { z: bool, }; try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ - .gpa = std.testing.allocator, + .gpa = gpa, + .arena = arena, .source = \\.{ \\ .x = "hello", @@ -3626,7 +3487,8 @@ test "std.zon free on error" { bool, }; try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ - .gpa = std.testing.allocator, + .gpa = gpa, + .arena = arena, .source = \\.{ \\ "hello", @@ -3639,58 +3501,55 @@ test "std.zon free on error" { } // Test freeing structs with missing fields - { - const Struct = struct { - x: []const u8, - y: bool, - }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ - .gpa = std.testing.allocator, - .source = - \\.{ - \\ .x = "hello", - \\} - , - .diag = null, - })); - } + const Struct = struct { + x: []const u8, + y: bool, + }; + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + .gpa = gpa, + .arena = arena, + .source = + \\.{ + \\ .x = "hello", + \\} + , + .diag = null, + })); // Test freeing partially allocated arrays - { - try std.testing.expectError(error.ParseZon, fromSliceAlloc([3][]const u8, .{ - .gpa = std.testing.allocator, - .source = - \\.{ - \\ "hello", - \\ false, - \\ false, - \\} - , - .diag = null, - })); - } + try std.testing.expectError(error.ParseZon, fromSliceAlloc([3][]const u8, .{ + .gpa = gpa, + .arena = arena, + .source = + \\.{ + \\ "hello", + \\ false, + \\ false, + \\} + , + .diag = null, + })); // Test freeing partially allocated slices - { - try std.testing.expectError(error.ParseZon, fromSliceAlloc([][]const u8, .{ - .gpa = std.testing.allocator, - .source = - \\.{ - \\ "hello", - \\ "world", - \\ false, - \\} - , - .diag = null, - })); - } + try std.testing.expectError(error.ParseZon, fromSliceAlloc([][]const u8, .{ + .gpa = gpa, + .arena = arena, + .source = + \\.{ + \\ "hello", + \\ "world", + \\ false, + \\} + , + .diag = null, + })); // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged // unions. try std.testing.expectEqual( @as(f32, 1.5), (try fromSlice(union { x: f32 }, .{ - .gpa = std.testing.allocator, + .gpa = gpa, .source = ".{ .x = 1.5 }", .diag = null, })).x, @@ -3700,80 +3559,13 @@ test "std.zon free on error" { // the allocation, as is the case here. { const result = try fromSliceAlloc(union { x: []const u8 }, .{ - .gpa = std.testing.allocator, + .gpa = gpa, + .arena = arena, .source = ".{ .x = \"foo\" }", .diag = null, }); - defer free(std.testing.allocator, result.x); try std.testing.expectEqualStrings("foo", result.x); } - - // However, if it's possible we could get an error requiring we free the value, but the value - // cannot be freed (e.g. untagged unions) then we need to turn off `free_on_error` for it to - // compile. - { - const S = struct { - union { x: []const u8 }, - bool, - }; - const result = try fromSliceAlloc(S, .{ - .gpa = std.testing.allocator, - .source = ".{ .{ .x = \"foo\" }, true }", - .free_on_error = false, - .diag = null, - }); - defer free(std.testing.allocator, result[0].x); - try std.testing.expectEqualStrings("foo", result[0].x); - try std.testing.expect(result[1]); - } - - // Again but for structs. - { - const S = struct { - a: union { x: []const u8 }, - b: bool, - }; - const result = try fromSliceAlloc(S, .{ - .gpa = std.testing.allocator, - .source = ".{ .a = .{ .x = \"foo\" }, .b = true }", - .free_on_error = false, - .diag = null, - }); - defer free(std.testing.allocator, result.a.x); - try std.testing.expectEqualStrings("foo", result.a.x); - try std.testing.expect(result.b); - } - - // Again but for arrays. - { - const S = [2]union { x: []const u8 }; - const result = try fromSliceAlloc(S, .{ - .gpa = std.testing.allocator, - .source = ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", - .free_on_error = false, - .diag = null, - }); - defer free(std.testing.allocator, result[0].x); - defer free(std.testing.allocator, result[1].x); - try std.testing.expectEqualStrings("foo", result[0].x); - try std.testing.expectEqualStrings("bar", result[1].x); - } - - // Again but for slices. - { - const S = []union { x: []const u8 }; - const result = try fromSliceAlloc(S, .{ - .gpa = std.testing.allocator, - .source = ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", - .free_on_error = false, - .diag = null, - }); - defer std.testing.allocator.free(result); - defer free(std.testing.allocator, result[0].x); - defer free(std.testing.allocator, result[1].x); - try std.testing.expectEqualStrings("foo", result[0].x); - try std.testing.expectEqualStrings("bar", result[1].x); - } } test "std.zon vector" { @@ -3781,11 +3573,18 @@ test "std.zon vector" { if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .s390x) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25957 const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Passing cases try std.testing.expectEqual( @Vector(0, bool){}, - try fromSlice(@Vector(0, bool), .{ .gpa = gpa, .source = ".{}", .diag = null }), + try fromSlice(@Vector(0, bool), .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }), ); try std.testing.expectEqual( @Vector(3, bool){ true, false, true }, @@ -3798,7 +3597,11 @@ test "std.zon vector" { try std.testing.expectEqual( @Vector(0, f32){}, - try fromSlice(@Vector(0, f32), .{ .gpa = gpa, .source = ".{}", .diag = null }), + try fromSlice(@Vector(0, f32), .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }), ); try std.testing.expectEqual( @Vector(3, f32){ 1.5, 2.5, 3.5 }, @@ -3811,11 +3614,19 @@ test "std.zon vector" { try std.testing.expectEqual( @Vector(0, u8){}, - try fromSlice(@Vector(0, u8), .{ .gpa = gpa, .source = ".{}", .diag = null }), + try fromSlice(@Vector(0, u8), .{ + .gpa = gpa, + .source = ".{}", + .diag = null, + }), ); try std.testing.expectEqual( @Vector(3, u8){ 2, 4, 6 }, - try fromSlice(@Vector(3, u8), .{ .gpa = gpa, .source = ".{2, 4, 6}", .diag = null }), + try fromSlice(@Vector(3, u8), .{ + .gpa = gpa, + .source = ".{2, 4, 6}", + .diag = null, + }), ); { @@ -3823,16 +3634,17 @@ test "std.zon vector" { @Vector(0, *const u8){}, try fromSliceAlloc(@Vector(0, *const u8), .{ .gpa = gpa, + .arena = arena, .source = ".{}", .diag = null, }), ); const pointers = try fromSliceAlloc(@Vector(3, *const u8), .{ .gpa = gpa, + .arena = arena, .source = ".{2, 4, 6}", .diag = null, }); - defer free(gpa, pointers); try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers); } @@ -3841,23 +3653,23 @@ test "std.zon vector" { @Vector(0, ?*const u8){}, try fromSliceAlloc(@Vector(0, ?*const u8), .{ .gpa = gpa, + .arena = arena, .source = ".{}", .diag = null, }), ); const pointers = try fromSliceAlloc(@Vector(3, ?*const u8), .{ .gpa = gpa, + .arena = arena, .source = ".{2, null, 6}", .diag = null, }); - defer free(gpa, pointers); try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers); } // Too few fields { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(@Vector(2, f32), .{ @@ -3875,8 +3687,7 @@ test "std.zon vector" { // Too many fields { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(@Vector(2, f32), .{ @@ -3894,8 +3705,7 @@ test "std.zon vector" { // Wrong type fields { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(@Vector(3, f32), .{ @@ -3913,23 +3723,26 @@ test "std.zon vector" { // Wrong type { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(3, u8), .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSlice(@Vector(3, u8), .{ + .gpa = gpa, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{diag}); } // Elements should get freed on error { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(@Vector(3, *u8), .{ .gpa = gpa, + .arena = arena, .source = ".{1, true, 3}", .diag = &diag, }), @@ -3940,35 +3753,38 @@ test "std.zon vector" { test "std.zon add pointers" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); // Primitive with varying levels of pointers { const result = try fromSliceAlloc(*u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*); } { const result = try fromSliceAlloc(**u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*.*); } { const result = try fromSliceAlloc(***u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*.*.*); } @@ -3976,90 +3792,90 @@ test "std.zon add pointers" { { const some = try fromSliceAlloc(?*u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.?.*); const none = try fromSliceAlloc(?*u32, .{ .gpa = gpa, + .arena = arena, .source = "null", .diag = null, }); - defer free(gpa, none); try std.testing.expectEqual(null, none); } { const some = try fromSliceAlloc(*?u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.?); const none = try fromSliceAlloc(*?u32, .{ .gpa = gpa, + .arena = arena, .source = "null", .diag = null, }); - defer free(gpa, none); try std.testing.expectEqual(null, none.*); } { const some = try fromSliceAlloc(?**u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.?.*.*); const none = try fromSliceAlloc(?**u32, .{ .gpa = gpa, + .arena = arena, .source = "null", .diag = null, }); - defer free(gpa, none); try std.testing.expectEqual(null, none); } { const some = try fromSliceAlloc(*?*u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.?.*); const none = try fromSliceAlloc(*?*u32, .{ .gpa = gpa, + .arena = arena, .source = "null", .diag = null, }); - defer free(gpa, none); try std.testing.expectEqual(null, none.*); } { const some = try fromSliceAlloc(**?u32, .{ .gpa = gpa, + .arena = arena, .source = "10", .diag = null, }); - defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.*.?); const none = try fromSliceAlloc(**?u32, .{ .gpa = gpa, + .arena = arena, .source = "null", .diag = null, }); - defer free(gpa, none); try std.testing.expectEqual(null, none.*.*); } @@ -4067,10 +3883,10 @@ test "std.zon add pointers" { { const result = try fromSliceAlloc(*[3]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{ 1, 2, 3 }", .diag = null, }); - defer free(gpa, result); try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*); } @@ -4094,6 +3910,7 @@ test "std.zon add pointers" { const found = try fromSliceAlloc(?*Outer, .{ .gpa = gpa, + .arena = arena, .source = \\.{ \\ .f1 = .{ @@ -4105,39 +3922,46 @@ test "std.zon add pointers" { , .diag = null, }); - defer free(gpa, found); try std.testing.expectEqualDeep(expected, found.?.*); } // Test that optional types are flattened correctly in errors { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const u8, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const f32, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const f32, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const @Vector(3, u8), .{ .gpa = gpa, + .arena = arena, .source = "true", .diag = &diag, }), @@ -4146,101 +3970,141 @@ test "std.zon add pointers" { } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const bool, .{ .gpa = gpa, .source = "10", .diag = &diag }), + fromSliceAlloc(*const ?*const bool, .{ + .gpa = gpa, + .arena = arena, + .source = "10", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const struct { a: i32 }, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const struct { a: i32 }, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const struct { i32 }, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const struct { i32 }, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const union { x: void }, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const union { x: void }, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const [3]u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const [3]u8, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(?[3]u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(?[3]u8, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const []u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const []u8, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(?[]u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(?[]u8, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const []const u8, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const []const u8, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{diag}); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); + var diag: Diagnostics = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const enum { foo }, .{ .gpa = gpa, .source = "true", .diag = &diag }), + fromSliceAlloc(*const ?*const enum { foo }, .{ + .gpa = gpa, + .arena = arena, + .source = "true", + .diag = &diag, + }), ); try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{diag}); } @@ -4255,26 +4119,35 @@ test "std.zon stop on node" { y: f32, }; - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); - const result = try fromSlice(Vec2, .{ - .gpa = gpa, - .source = ".{ .x = 1.5, .y = 2.5 }", - .diag = &diag, + var ast = try std.zig.Ast.parse(gpa, ".{ .x = 1.5, .y = 2.5 }", .{ .mode = .zon }); + defer ast.deinit(gpa); + + var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); + defer zoir.deinit(gpa); + + const result = try fromZoir(Vec2, .{ + .ast = &ast, + .zoir = &zoir, + .diag = null, }); + try std.testing.expectEqual(result.y, 2.5); - try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.5 }, result.x.get(diag.zoir)); + try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.5 }, result.x.get(&zoir)); } { - var diag: Diagnostics = .{}; - defer diag.deinit(gpa); - const result = try fromSlice(Zoir.Node.Index, .{ - .gpa = gpa, - .source = "1.23", - .diag = &diag, + var ast = try std.zig.Ast.parse(gpa, "1.23", .{ .mode = .zon }); + defer ast.deinit(gpa); + + var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); + defer zoir.deinit(gpa); + + const result = try fromZoir(Zoir.Node.Index, .{ + .ast = &ast, + .zoir = &zoir, + .diag = null, }); - try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(diag.zoir)); + try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(&zoir)); } } @@ -4300,12 +4173,19 @@ test "std.zon no alloc" { try std.testing.expectEqual( Nested{ 1, 2, .{ 3, 4 } }, - try fromZoir(Nested, .{ .ast = ast, .zoir = zoir, .diag = null }), + try fromZoir(Nested, .{ + .ast = &ast, + .zoir = &zoir, + .diag = null, + }), ); } test "std.zon errors without diagnostics" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); const Enum = enum { foo, @@ -4314,6 +4194,7 @@ test "std.zon errors without diagnostics" { }; try std.testing.expectError(error.ParseZon, fromSliceAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".nothing", .diag = null, })); @@ -4323,6 +4204,7 @@ test "std.zon errors without diagnostics" { }; try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ .gpa = gpa, + .arena = arena, .source = ".{ .name = \"Alice\", .age = 25 }", .diag = null, })); @@ -4333,11 +4215,13 @@ test "std.zon errors without diagnostics" { }; try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".a", .diag = null, })); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, + .arena = arena, .source = ".{ .b = 8 }", .diag = null, })); @@ -4345,6 +4229,9 @@ test "std.zon errors without diagnostics" { test "std.zon aligned pointers" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); const n: u8 align(8) = 10; const Foo = struct { @@ -4356,15 +4243,18 @@ test "std.zon aligned pointers" { }; const found = try fromSliceAlloc(Foo, .{ .gpa = gpa, + .arena = arena, .source = ".{ .inner = 10 }", .diag = null, }); - defer free(gpa, found); try std.testing.expectEqualDeep(expected.inner, found.inner); } test "std.zon update" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); const Vector = struct { x: f32, y: f32, z: f32 }; const MyStruct = struct { @@ -4379,51 +4269,43 @@ test "std.zon update" { str: []const u8, }; + var v1: Vector = .{ .x = 1, .y = 2, .z = 3 }; var expected: MyStruct = .{ .foo = 10, .bar = true, .baz = .{ .a = .{ 1, 2 }, .b = .c }, - .ptr = b: { - const v = gpa.create(Vector) catch @panic("oom"); - v.* = .{ .x = 1, .y = 2, .z = 3 }; - break :b v; - }, - .str = gpa.dupe(u8, "hello, world") catch @panic("oom"), + .ptr = &v1, + .str = "hello, world", }; - defer free(gpa, expected); + var v2: Vector = .{ .x = 1, .y = 2, .z = 3 }; var found: MyStruct = .{ .foo = 10, .bar = true, .baz = .{ .a = .{ 1, 2 }, .b = .c }, - .ptr = b: { - const v = gpa.create(Vector) catch @panic("oom"); - v.* = .{ .x = 1, .y = 2, .z = 3 }; - break :b v; - }, - .str = gpa.dupe(u8, "hello, world") catch @panic("oom"), + .ptr = &v2, + .str = "hello, world", }; - defer free(gpa, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{}", .diag = null, }); try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{ .bar = false }", .diag = null, }); expected.bar = false; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{ .baz = .{ .a = .{ 2, 4 } } }", .diag = null, }); @@ -4431,9 +4313,9 @@ test "std.zon update" { expected.baz.a[1] = 4; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{ .foo = 11, .baz = .{ .b = .d } }", .diag = null, }); @@ -4441,27 +4323,27 @@ test "std.zon update" { expected.baz.b = .d; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{ .optional = 10 }", .diag = null, }); expected.optional = 10; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{ .optional = null }", .diag = null, }); expected.optional = null; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", .diag = null, }); @@ -4470,14 +4352,13 @@ test "std.zon update" { expected.ptr.z = 30; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, .{ + try updateFromSliceAlloc(MyStruct, &found, .{ .gpa = gpa, - .value = &found, + .arena = arena, .source = ".{ .str = \"foo\" }", .diag = null, }); - gpa.free(expected.str); - expected.str = gpa.dupe(u8, "foo") catch @panic("oom"); + expected.str = "foo"; try std.testing.expectEqualDeep(expected, found); } @@ -4486,6 +4367,9 @@ test "std.zon variants" { // the same code. const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); const Struct = struct { a: u32, b: u32 }; const start: Struct = .{ .a = 10, .b = 20 }; @@ -4499,38 +4383,35 @@ test "std.zon variants" { defer zoir.deinit(gpa); var curr = start; - try updateFromSlice(Struct, .{ + try updateFromSlice(Struct, &curr, .{ .gpa = gpa, - .value = &curr, .source = ".{ .a = 100 }", .diag = null, }); try std.testing.expectEqual(end, curr); curr = start; - try updateFromSliceAlloc(Struct, .{ + try updateFromSliceAlloc(Struct, &curr, .{ .gpa = gpa, - .value = &curr, + .arena = arena, .source = ".{ .a = 100 }", .diag = null, }); try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoir(Struct, .{ - .ast = ast, - .value = &curr, - .zoir = zoir, + try updateFromZoir(Struct, &curr, .{ + .ast = &ast, + .zoir = &zoir, .diag = null, }); try std.testing.expectEqual(end, curr); curr = start; - try updateFromZoirAlloc(Struct, .{ - .gpa = gpa, - .value = &curr, - .ast = ast, - .zoir = zoir, + try updateFromZoirAlloc(Struct, &curr, .{ + .arena = arena, + .ast = &ast, + .zoir = &zoir, .diag = null, }); try std.testing.expectEqual(end, curr); @@ -4555,23 +4436,24 @@ test "std.zon variants" { end, try fromSliceAlloc(Struct, .{ .gpa = gpa, + .arena = arena, .source = ".{ .a = 100, .b = 20 }", .diag = null, }), ); try std.testing.expectEqual( try fromZoir(Struct, .{ - .ast = ast, - .zoir = zoir, + .ast = &ast, + .zoir = &zoir, .diag = null, }), end, ); try std.testing.expectEqual( try fromZoirAlloc(Struct, .{ - .gpa = gpa, - .ast = ast, - .zoir = zoir, + .arena = arena, + .ast = &ast, + .zoir = &zoir, .diag = null, }), end, -- 2.54.0 From a5bfa7aa453f2a061791501339044db452b93df5 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sun, 19 Jul 2026 19:22:07 -0700 Subject: [PATCH 08/26] Support updating pointers, unions, and optionals --- lib/std/zon/parse.zig | 331 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 271 insertions(+), 60 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 149188195e8b1dfc2e5636a6af2be62ce40ed609..7208d70bee2ed598c4445fe06392bb1d24a5c0e2 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -199,10 +199,8 @@ pub fn fromSliceAlloc(T: type, options: FromSliceAllocOptions) error{ OutOfMemor .arena = options.arena, .source = options.source, .diag = options.diag, - .parser_options = .{ - .ignore_unknown_fields = options.ignore_unknown_fields, - .ignore_missing_fields = false, - }, + .ignore_unknown_fields = options.ignore_unknown_fields, + .initialized = false, }); return value; } @@ -218,10 +216,8 @@ pub fn updateFromSliceAlloc( .arena = options.arena, .source = options.source, .diag = options.diag, - .parser_options = .{ - .ignore_unknown_fields = options.ignore_unknown_fields, - .ignore_missing_fields = true, - }, + .ignore_unknown_fields = options.ignore_unknown_fields, + .initialized = true, }); } @@ -230,7 +226,8 @@ const ParseSliceAllocOptions = struct { arena: Allocator, source: [:0]const u8, diag: ?*Diagnostics, - parser_options: Parser.Options, + ignore_unknown_fields: bool, + initialized: bool, }; fn parseSliceAlloc( @@ -250,7 +247,8 @@ fn parseSliceAlloc( .zoir = &zoir, .node = .root, .diag = options.diag, - .parser_options = options.parser_options, + .ignore_unknown_fields = options.ignore_unknown_fields, + .initialized = options.initialized, }); } @@ -327,10 +325,8 @@ pub fn fromZoirAlloc(T: type, options: FromZoirAllocOptions) error{ OutOfMemory, .zoir = options.zoir, .node = options.node, .diag = options.diag, - .parser_options = .{ - .ignore_unknown_fields = options.ignore_unknown_fields, - .ignore_missing_fields = false, - }, + .ignore_unknown_fields = options.ignore_unknown_fields, + .initialized = false, }); return value; } @@ -347,10 +343,8 @@ pub fn updateFromZoirAlloc( .zoir = options.zoir, .node = options.node, .diag = options.diag, - .parser_options = .{ - .ignore_unknown_fields = options.ignore_unknown_fields, - .ignore_missing_fields = true, - }, + .ignore_unknown_fields = options.ignore_unknown_fields, + .initialized = true, }); } @@ -360,7 +354,8 @@ const ParseZoirAllocOptions = struct { zoir: *const Zoir, node: Zoir.Node.Index, diag: ?*Diagnostics, - parser_options: Parser.Options, + ignore_unknown_fields: bool, + initialized: bool, }; fn parseZoirAlloc( @@ -396,11 +391,11 @@ fn parseZoirAlloc( .arena = options.arena, .ast = options.ast, .zoir = options.zoir, - .options = options.parser_options, + .ignore_unknown_fields = options.ignore_unknown_fields, .diag = options.diag, }; - try parser.parseExprInto(options.node, value); + try parser.parseExprInto(options.node, value, options.initialized); } fn requiresAllocator(T: type) bool { @@ -429,18 +424,17 @@ const Parser = struct { ast: *const Ast, zoir: *const Zoir, diag: ?*Diagnostics, - options: Parser.Options, - pointer_depth: usize = 0, - - pub const Options = struct { - ignore_unknown_fields: bool, - ignore_missing_fields: bool, - }; + ignore_unknown_fields: bool, const ParseExprError = error{ ParseZon, OutOfMemory }; - fn parseExprInto(self: *@This(), node: Zoir.Node.Index, out: anytype) ParseExprError!void { - return self.parseExprInnerInto(node, out) catch |err| switch (err) { + fn parseExprInto( + self: *@This(), + node: Zoir.Node.Index, + out: anytype, + initialized: bool, + ) ParseExprError!void { + return self.parseExprInnerInto(node, out, initialized) catch |err| switch (err) { error.WrongType => return self.failExpectedType(@TypeOf(out.*), node), else => |e| return e, }; @@ -452,6 +446,7 @@ const Parser = struct { self: *@This(), node: Zoir.Node.Index, out: anytype, + initialized: bool, ) ParseExprInnerError!void { if (@TypeOf(out.*) == Zoir.Node.Index) { out.* = node; @@ -462,9 +457,9 @@ const Parser = struct { .optional => |optional| if (node.get(self.zoir) == .null) { out.* = null; } else { - var new: optional.child = undefined; - try self.parseExprInnerInto(node, &new); - out.* = new; + const child_initialized = initialized and out.* != null; + if (!child_initialized) out.* = @as(optional.child, undefined); + try self.parseExprInnerInto(node, &out.*.?, child_initialized); }, .bool => out.* = try self.parseBool(node), .int => out.* = try self.parseInt(@TypeOf(out.*), node), @@ -472,23 +467,31 @@ const Parser = struct { .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), .pointer => |pointer| switch (pointer.size) { .one => { - const slice = try self.arena.alignedAlloc( - pointer.child, - if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, - 1, + // Get a pointer to update + const new = b: { + // If the existing value is initialized and non const, use it + if (initialized and !pointer.attrs.@"const") break :b out.*; + // Otherwise, allocate new memory + const new = &(try self.arena.alignedAlloc( + pointer.child, + if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, + 1, + ))[0]; + // If the original value was initialized, dupe it into the new memory + if (initialized) new.* = out.*.*; + break :b new; + }; + try self.parseExprInnerInto( + node, + new, + initialized, ); - const new = &slice[0]; - { - self.pointer_depth += 1; - defer self.pointer_depth -= 1; - try self.parseExprInnerInto(node, new); - } out.* = new; }, .slice => { + // Slices are replaced wholesale since ZON doesn't have syntax for specifying + // just a part of a slice. const new = b: { - self.pointer_depth += 1; - defer self.pointer_depth -= 1; const new = try self.parseSlicePointer(@TypeOf(out.*), node); break :b new; }; @@ -501,8 +504,8 @@ const Parser = struct { .@"struct" => |@"struct"| if (@"struct".is_tuple) try self.parseTupleInto(node, out) else - try self.parseStructInto(node, out), - .@"union" => try self.parseUnionInto(node, out), + try self.parseStructInto(node, out, initialized), + .@"union" => try self.parseUnionInto(node, out, initialized), else => comptime unreachable, } @@ -714,7 +717,7 @@ const Parser = struct { // Parse the elements and return the slice for (slice, 0..) |*elem, i| { - try self.parseExprInto(nodes.at(@intCast(i)), elem); + try self.parseExprInto(nodes.at(@intCast(i)), elem, false); } return slice; @@ -746,7 +749,7 @@ const Parser = struct { // Parse the elements and return the array inline for (out, 0..) |*elem, i| { - try self.parseExprInto(nodes.at(@intCast(i)), elem); + try self.parseExprInto(nodes.at(@intCast(i)), elem, false); } if (array.sentinel()) |s| { @@ -761,7 +764,12 @@ const Parser = struct { out.* = array; } - fn parseStructInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { + fn parseStructInto( + self: *@This(), + node: Zoir.Node.Index, + out: anytype, + initialized: bool, + ) !void { const repr = node.get(self.zoir); const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { .struct_literal => |nodes| nodes, @@ -787,7 +795,7 @@ const Parser = struct { for (0..fields.names.len) |i| { const name = fields.names[i].get(self.zoir); const field_index = field_indices.get(name) orelse { - if (self.options.ignore_unknown_fields) continue; + if (self.ignore_unknown_fields) continue; return self.failUnexpected(@TypeOf(out.*), "field", node, i, name); }; if (field_index == comptime_field) { @@ -802,10 +810,10 @@ const Parser = struct { switch (field_index) { inline 0...(info.field_names.len - 1) => |j| { if (info.field_attrs[j].@"comptime") unreachable; - try self.parseExprInto( fields.vals.at(@intCast(i)), &@field(out, info.field_names[j]), + initialized, ); }, else => unreachable, // Can't be out of bounds @@ -813,7 +821,7 @@ const Parser = struct { } // Fill in any missing default fields - if (!self.options.ignore_missing_fields or self.pointer_depth > 0) { + if (!initialized) { inline for (field_found, 0..) |found, i| { if (!found) { const field_attrs = info.field_attrs[i]; @@ -860,20 +868,28 @@ const Parser = struct { if (info.field_attrs[i].@"comptime") { return self.failComptimeField(node, i); } else { - try self.parseExprInto(nodes.at(i), &out[i]); + try self.parseExprInto(nodes.at(i), &out[i], false); } } } } - fn parseUnionInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { + fn parseUnionInto( + self: *@This(), + node: Zoir.Node.Index, + out: anytype, + initialized: bool, + ) !void { const @"union" = @typeInfo(@TypeOf(out.*)).@"union"; if (@"union".field_names.len == 0) comptime unreachable; // Gather info on the fields const field_indices = b: { - comptime var kvs_list: [@"union".field_names.len]struct { []const u8, usize } = undefined; + comptime var kvs_list: [@"union".field_names.len]struct { + []const u8, + usize, + } = undefined; inline for (@"union".field_names, 0..) |field_name, i| { kvs_list[i] = .{ field_name, i }; } @@ -893,7 +909,13 @@ const Parser = struct { const field_index = b: { const field_name_str = field_name.get(self.zoir); break :b field_indices.get(field_name_str) orelse - return self.failUnexpected(@TypeOf(out.*), "field", node, null, field_name_str); + return self.failUnexpected( + @TypeOf(out.*), + "field", + node, + null, + field_name_str, + ); }; // Initialize the union from the given field. @@ -926,8 +948,25 @@ const Parser = struct { if (@"union".field_types[i] == void) { return self.failNode(field_val, "expected type 'void'"); } else { - out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined); - try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i])); + const field_initialized = b: { + if (!initialized) break :b false; + if (@"union".tag_type == null) break :b false; + const tag = @field(@"union".tag_type.?, @"union".field_names[i]); + if (out.* != tag) break :b false; + break :b true; + }; + if (!field_initialized) { + out.* = @unionInit( + @TypeOf(out.*), + @"union".field_names[i], + undefined, + ); + } + try self.parseExprInto( + field_val, + &@field(out.*, @"union".field_names[i]), + field_initialized, + ); } }, else => unreachable, // Can't be out of bounds @@ -4250,7 +4289,7 @@ test "std.zon aligned pointers" { try std.testing.expectEqualDeep(expected.inner, found.inner); } -test "std.zon update" { +test "std.zon update basic" { const gpa = std.testing.allocator; var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); @@ -4362,6 +4401,178 @@ test "std.zon update" { try std.testing.expectEqualDeep(expected, found); } +test "std.zon update optionals" { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const MyStruct = struct { + foo: ?struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, + }; + + // Updating an optional that starts out null should fill in any default fields we left off + { + var expected: MyStruct = .{ .foo = null }; + var found = expected; + try updateFromSlice(MyStruct, &found, .{ + .gpa = gpa, + .source = ".{ .foo = .{ .qux = 3 } }", + .diag = null, + }); + expected.foo = .{ .qux = 3 }; + try std.testing.expectEqual(expected, found); + } + + // Updating an optional that starts out null should error if we leave off required fields. + { + var found: MyStruct = .{ .foo = null }; + var diag: Diagnostics = .init(arena); + try std.testing.expectError(error.ParseZon, updateFromSlice(MyStruct, &found, .{ + .gpa = gpa, + .source = ".{ .foo = .{} }", + .diag = &diag, + })); + try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{diag}); + } + + // Updating an optional that starts out non-null should preserve any values we leave off. It's + // also okay to leave off required fields since they were already set. + { + var expected: MyStruct = .{ + .foo = .{ .bar = 10, .baz = 20, .qux = 30 }, + }; + var found = expected; + try updateFromSlice(MyStruct, &found, .{ + .gpa = gpa, + .source = ".{ .foo = .{ .baz = 200 } }", + .diag = null, + }); + expected.foo.?.baz = 200; + try std.testing.expectEqual(expected, found); + } +} + +test "std.zon update optional pointers" { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const MyStruct = struct { + foo: ?*const struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, + }; + + // Just a smoke test since its the combination of behavior we've already tested separately + { + var expected: MyStruct = .{ .foo = null }; + var found = expected; + try updateFromSliceAlloc(MyStruct, &found, .{ + .gpa = gpa, + .arena = arena, + .source = ".{ .foo = .{ .qux = 3 } }", + .diag = null, + }); + expected.foo = &.{ .qux = 3 }; + try std.testing.expectEqualDeep(expected, found); + } +} + +test "std.zon update pointers" { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + // Updating a pointer should leave fields we don't specify unchanged + { + const MyStruct = struct { + foo: *struct { bar: u32 = 10, baz: u32 = 20, qux: u32 }, + }; + var ptr: @typeInfo(@FieldType(MyStruct, "foo")).pointer.child = .{ .qux = 3 }; + var expected: MyStruct = .{ .foo = &ptr }; + var found = expected; + try updateFromSliceAlloc(MyStruct, &found, .{ + .gpa = gpa, + .arena = arena, + .source = ".{ .foo = .{ .bar = 100 } }", + .diag = null, + }); + expected.foo.bar = 100; + try std.testing.expectEqualDeep(expected, found); + } + + // Same thing, but for const pointers + { + const MyStruct = struct { + foo: *const struct { bar: u32 = 10, baz: u32 = 20, qux: u32 }, + }; + var expected: MyStruct = .{ .foo = &.{ .qux = 3 } }; + var found = expected; + try updateFromSliceAlloc(MyStruct, &found, .{ + .gpa = gpa, + .arena = arena, + .source = ".{ .foo = .{ .bar = 100 } }", + .diag = null, + }); + expected.foo = &.{ .bar = 100, .baz = 20, .qux = 3 }; + try std.testing.expectEqualDeep(expected, found); + } +} + +test "std.zon update unions" { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const MyUnion = union(enum) { + none: void, + foo: struct { bar: u32 = 10, baz: u32 = 20, qux: u32 }, + }; + + // Updating a union to a new field should fill in any default sub-fields we left off + { + var expected: MyUnion = .none; + var found = expected; + try updateFromSlice(MyUnion, &found, .{ + .gpa = gpa, + .source = ".{ .foo = .{ .qux = 3 } }", + .diag = null, + }); + expected = .{ .foo = .{ .qux = 3 } }; + try std.testing.expectEqual(expected, found); + } + + // Updating a union to a new field should error if we leave off required sub-fields + { + var found: MyUnion = .none; + var diag: Diagnostics = .init(arena); + try std.testing.expectError(error.ParseZon, updateFromSlice(MyUnion, &found, .{ + .gpa = gpa, + .source = ".{ .foo = .{} }", + .diag = &diag, + })); + try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{diag}); + } + + // Updating a union should preseve any sub-fields that we left off. It's also okay to leave off + // required fields since they were already set. + { + var expected: MyUnion = .{ + .foo = .{ .bar = 10, .baz = 20, .qux = 30 }, + }; + var found = expected; + try updateFromSlice(MyUnion, &found, .{ + .gpa = gpa, + .source = ".{ .foo = .{ .baz = 200 } }", + .diag = null, + }); + expected.foo.baz = 200; + try std.testing.expectEqual(expected, found); + } +} + test "std.zon variants" { // Just a smoke test, we don't need to test each variant thoroughly because they all call into // the same code. -- 2.54.0 From 6d21ffce6ee79bba8238db9f8012579f3cec7e57 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 20 Jul 2026 00:14:55 -0700 Subject: [PATCH 09/26] Use clearer conventions for diagnostics --- lib/std/zon/parse.zig | 1033 +++++++++++++++++++++-------------------- 1 file changed, 517 insertions(+), 516 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 7208d70bee2ed598c4445fe06392bb1d24a5c0e2..bd9281267a160c5636225aa06988036f76823325 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -4,7 +4,7 @@ //! * `fromZoir`/`fromZoirAlloc` //! //! To update an existing values, see the `updateFrom*` variants. To get human readable errors, use -//! the `diag` option. For lower level control over parsing, see `std.zig.Zoir`. For importing ZON +//! the `errors` option. For lower level control over parsing, see `std.zig.Zoir`. For importing ZON //! at compile time, use `@import`. const std = @import("std"); @@ -23,6 +23,7 @@ const ArenaAllocator = std.heap.ArenaAllocator; /// Rename when adding or removing support for a type. const valid_types = {}; +/// An error encountered while parsing ZON. pub const Error = struct { msg: []const u8, loc: Ast.Location, @@ -90,20 +91,20 @@ pub const Error = struct { }; }; -/// Human readable information about the success or failure of a parse. -pub const Diagnostics = struct { +/// Errors encountered while parsing ZON. +pub const Errors = struct { arena: Allocator, - compile_errors: std.ArrayList(Error), + list: std.ArrayList(Error), pub fn init(arena: Allocator) @This() { return .{ .arena = arena, - .compile_errors = .empty, + .list = .empty, }; } pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - for (self.compile_errors.items) |e| { + for (self.list.items) |e| { try w.print("{d}:{d}: error: {s}\n", .{ e.loc.line + 1, e.loc.column + 1, @@ -121,17 +122,17 @@ pub const Diagnostics = struct { } pub fn clear(self: *@This()) void { - self.compile_errors.clearRetainingCapacity(); + self.errors.clearRetainingCapacity(); } }; pub const FromSliceOptions = struct { /// Used for scratch allocations. gpa: Allocator, - /// The ZON source. + /// The ZON source to parse. source: [:0]const u8, - /// Write human readable error messages here. - diag: ?*Diagnostics, + /// If non-null, any errors encountered are appended here. + errors: ?*Errors, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; @@ -142,7 +143,7 @@ pub const FromSliceOptions = struct { /// invalid or can not be deserialized into type `T`. /// /// When the parser returns `error.ParseZon`, it will also store a human readable explanation in -/// `diag` if non null. +/// `errors` if non null. /// /// Asserts at compile time that the result type doesn't contain pointers. As such, the result /// doesn't need to be freed. @@ -154,7 +155,7 @@ pub fn fromSlice(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZo .gpa = options.gpa, .arena = .failing, .source = options.source, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, }); } @@ -172,7 +173,7 @@ pub fn updateFromSlice( .gpa = options.gpa, .arena = .failing, .source = options.source, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, }); } @@ -182,10 +183,10 @@ pub const FromSliceAllocOptions = struct { gpa: Allocator, /// Owns the result. arena: Allocator, - /// The ZON source. + /// The ZON source to parse. source: [:0]const u8, - /// Write human readable error messages here. - diag: ?*Diagnostics, + /// If non-null, any errors encountered are appended here. + errors: ?*Errors, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; @@ -198,7 +199,7 @@ pub fn fromSliceAlloc(T: type, options: FromSliceAllocOptions) error{ OutOfMemor .gpa = options.gpa, .arena = options.arena, .source = options.source, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, .initialized = false, }); @@ -215,7 +216,7 @@ pub fn updateFromSliceAlloc( .gpa = options.gpa, .arena = options.arena, .source = options.source, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, .initialized = true, }); @@ -225,7 +226,7 @@ const ParseSliceAllocOptions = struct { gpa: Allocator, arena: Allocator, source: [:0]const u8, - diag: ?*Diagnostics, + errors: ?*Errors, ignore_unknown_fields: bool, initialized: bool, }; @@ -246,7 +247,7 @@ fn parseSliceAlloc( .ast = &ast, .zoir = &zoir, .node = .root, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, .initialized = options.initialized, }); @@ -259,8 +260,8 @@ pub const FromZoirOptions = struct { zoir: *const Zoir, /// The node to start parsing at. node: Zoir.Node.Index = .root, - /// Write human readable error messages here. - diag: ?*Diagnostics, + /// If non-null, any errors encountered are appended here. + errors: ?*Errors, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; @@ -273,7 +274,7 @@ pub fn fromZoir(T: type, options: FromZoirOptions) error{ParseZon}!T { .ast = options.ast, .zoir = options.zoir, .node = options.node, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, }) catch |err| switch (err) { error.OutOfMemory => unreachable, // Checked by comptime assertion above @@ -293,7 +294,7 @@ pub fn updateFromZoir( .ast = options.ast, .zoir = options.zoir, .node = options.node, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, }) catch |err| switch (err) { error.OutOfMemory => unreachable, // Checked by comptime assertion above @@ -310,8 +311,8 @@ pub const FromZoirAllocOptions = struct { zoir: *const Zoir, /// The node to start parsing at. node: Zoir.Node.Index = .root, - /// Write human readable error messages here. - diag: ?*Diagnostics, + /// If non-null, any errors encountered are appended here. + errors: ?*Errors, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; @@ -324,7 +325,7 @@ pub fn fromZoirAlloc(T: type, options: FromZoirAllocOptions) error{ OutOfMemory, .ast = options.ast, .zoir = options.zoir, .node = options.node, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, .initialized = false, }); @@ -342,7 +343,7 @@ pub fn updateFromZoirAlloc( .ast = options.ast, .zoir = options.zoir, .node = options.node, - .diag = options.diag, + .errors = options.errors, .ignore_unknown_fields = options.ignore_unknown_fields, .initialized = true, }); @@ -353,7 +354,7 @@ const ParseZoirAllocOptions = struct { ast: *const Ast, zoir: *const Zoir, node: Zoir.Node.Index, - diag: ?*Diagnostics, + errors: ?*Errors, ignore_unknown_fields: bool, initialized: bool, }; @@ -366,21 +367,21 @@ fn parseZoirAlloc( comptime assert(canParseType(T)); if (options.zoir.hasCompileErrors()) { - if (options.diag) |s| { + if (options.errors) |errors| { for (options.zoir.compile_errors) |e| { var notes: std.ArrayList(Error.Note) = .empty; for (e.getNotes(options.zoir)) |note| { - try notes.append(s.arena, .init(options.ast, .{ - .msg = try s.arena.dupe(u8, note.msg.get(options.zoir)), + try notes.append(errors.arena, .init(options.ast, .{ + .msg = try errors.arena.dupe(u8, note.msg.get(options.zoir)), .token = note.token, .node_or_offset = note.node_or_offset, })); } - try s.compile_errors.append(s.arena, .init(options.ast, .{ - .msg = try s.arena.dupe(u8, e.msg.get(options.zoir)), + try errors.list.append(errors.arena, .init(options.ast, .{ + .msg = try errors.arena.dupe(u8, e.msg.get(options.zoir)), .token = e.token, .node_or_offset = e.node_or_offset, - .notes = try notes.toOwnedSlice(s.arena), + .notes = try notes.toOwnedSlice(errors.arena), })); } } @@ -392,7 +393,7 @@ fn parseZoirAlloc( .ast = options.ast, .zoir = options.zoir, .ignore_unknown_fields = options.ignore_unknown_fields, - .diag = options.diag, + .errors = options.errors, }; try parser.parseExprInto(options.node, value, options.initialized); @@ -423,7 +424,7 @@ const Parser = struct { arena: Allocator, ast: *const Ast, zoir: *const Zoir, - diag: ?*Diagnostics, + errors: ?*Errors, ignore_unknown_fields: bool, const ParseExprError = error{ ParseZon, OutOfMemory }; @@ -997,10 +998,10 @@ const Parser = struct { ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); comptime assert(args.len > 0); - if (self.diag) |s| try s.compile_errors.append(s.arena, .init(self.ast, .{ + if (self.errors) |errors| try errors.list.append(errors.arena, .init(self.ast, .{ .token = .fromToken(token), .node_or_offset = offset, - .msg = try std.fmt.allocPrint(s.arena, fmt, args), + .msg = try std.fmt.allocPrint(errors.arena, fmt, args), .notes = notes, })); return error.ParseZon; @@ -1022,7 +1023,7 @@ const Parser = struct { failure: Error, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - if (self.diag) |s| try s.compile_errors.append(s.arena, failure); + if (self.errors) |errors| try errors.list.append(errors.arena, failure); return error.ParseZon; } @@ -1059,7 +1060,7 @@ const Parser = struct { name: []const u8, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - const diag = self.diag orelse return error.ParseZon; + const errors = self.errors orelse return error.ParseZon; const token = if (field) |f| b: { var buf: [2]Ast.Node.Index = undefined; const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?; @@ -1070,13 +1071,13 @@ const Parser = struct { inline .@"struct", .@"union", .@"enum" => |info| { var notes: std.ArrayList(Error.Note) = .empty; if (info.field_names.len == 0) { - try notes.append(diag.arena, .init(self.ast, .{ + try notes.append(errors.arena, .init(self.ast, .{ .token = .fromToken(token), .node_or_offset = 0, .msg = "none expected", })); } else { - var aw: std.Io.Writer.Allocating = .init(diag.arena); + var aw: std.Io.Writer.Allocating = .init(errors.arena); aw.writer.writeAll("supported: ") catch return error.OutOfMemory; inline for (info.field_names, 0..) |field_name, i| { if (i != 0) aw.writer.writeAll(", ") catch return error.OutOfMemory; @@ -1085,7 +1086,7 @@ const Parser = struct { .allow_underscore = true, })}) catch return error.OutOfMemory; } - try notes.append(diag.arena, .init(self.ast, .{ + try notes.append(errors.arena, .init(self.ast, .{ .token = .fromToken(token), .node_or_offset = 0, .msg = try aw.toOwnedSlice(), @@ -1096,7 +1097,7 @@ const Parser = struct { 0, "unexpected {s} '{s}'", .{ item_kind, name }, - try notes.toOwnedSlice(diag.arena), + try notes.toOwnedSlice(errors.arena), ); }, else => comptime unreachable, @@ -1112,7 +1113,7 @@ const Parser = struct { field: usize, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - if (self.diag == null) return error.ParseZon; + if (self.errors == null) return error.ParseZon; const ast_node = node.getAstNode(self.zoir); var buf: [2]Ast.Node.Index = undefined; const token = if (self.ast.fullStructInit(&buf, ast_node)) |struct_init| b: { @@ -1293,16 +1294,16 @@ test "std.zon ast errors" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(struct {}, .{ .gpa = gpa, .source = ".{.x = 1 .y = 2}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{diag}); + try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{errors}); } test "std.zon comments" { @@ -1318,11 +1319,11 @@ test "std.zon comments" { \\10 // comment \\// comment , - .diag = null, + .errors = null, })); { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = @@ -1330,12 +1331,12 @@ test "std.zon comments" { \\10 // comment \\// comment , - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: expected expression, found 'a document comment'\n", "{f}", - .{diag}, + .{errors}, ); } } @@ -1345,14 +1346,14 @@ test "std.zon failure/oom formatting" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.OutOfMemory, fromSliceAlloc([]const u8, .{ .gpa = .failing, .arena = arena, .source = "\"foo\"", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("", "{f}", .{diag}); + try std.testing.expectFmt("", "{f}", .{errors}); } test "std.zon fromSliceAlloc syntax error" { @@ -1362,7 +1363,7 @@ test "std.zon fromSliceAlloc syntax error" { fromSlice(u8, .{ .gpa = gpa, .source = ".{", - .diag = null, + .errors = null, }), ); } @@ -1378,13 +1379,13 @@ test "std.zon optional" { const none = try fromSlice(?u32, .{ .gpa = gpa, .source = "null", - .diag = null, + .errors = null, }); try std.testing.expect(none == null); const some = try fromSlice(?u32, .{ .gpa = gpa, .source = "1", - .diag = null, + .errors = null, }); try std.testing.expect(some.? == 1); } @@ -1395,14 +1396,14 @@ test "std.zon optional" { .gpa = gpa, .arena = arena, .source = "null", - .diag = null, + .errors = null, }); try std.testing.expect(none == null); const some = try fromSliceAlloc(?[]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings("foo", some.?); } @@ -1422,38 +1423,38 @@ test "std.zon unions" { const tagged_x = try fromSlice(Tagged, .{ .gpa = gpa, .source = ".{.x = 1.5}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x); const tagged_y = try fromSlice(Tagged, .{ .gpa = gpa, .source = ".{.@\"y y\" = true}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Tagged{ .@"y y" = true }, tagged_y); const tagged_z_shorthand = try fromSlice(Tagged, .{ .gpa = gpa, .source = ".z", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand); const tagged_zz_shorthand = try fromSlice(Tagged, .{ .gpa = gpa, .source = ".@\"z z\"", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand); const untagged_x = try fromSlice(Untagged, .{ .gpa = gpa, .source = ".{.x = 1.5}", - .diag = null, + .errors = null, }); try std.testing.expect(untagged_x.x == 1.5); const untagged_y = try fromSlice(Untagged, .{ .gpa = gpa, .source = ".{.@\"y y\" = true}", - .diag = null, + .errors = null, }); try std.testing.expect(untagged_y.@"y y"); } @@ -1466,7 +1467,7 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.baz = false}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Union{ .baz = false }, noalloc); @@ -1474,7 +1475,7 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.bar = \"qux\"}", - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc); } @@ -1482,14 +1483,14 @@ test "std.zon unions" { // Unknown field { const Union = union { x: f32, y: f32 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.z=2.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( @@ -1498,80 +1499,80 @@ test "std.zon unions" { \\ , "{f}", - .{diag}, + .{errors}, ); } // Explicit void field { const Union = union(enum) { x: void }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.x=1}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{errors}); } // Extra field { const Union = union { x: f32, y: bool }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.x = 1.5, .y = true}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); } // No fields { const Union = union { x: f32, y: bool }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".{}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); } // Enum literals cannot coerce into untagged unions { const Union = union { x: void }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".x", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); } // Unknown field for enum literal coercion { const Union = union(enum) { x: void }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".y", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( \\1:2: error: unexpected field 'y' @@ -1579,21 +1580,21 @@ test "std.zon unions" { \\ , "{f}", - .{diag}, + .{errors}, ); } // Non void field for enum literal coercion { const Union = union(enum) { x: f32 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".x", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); + try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); } } @@ -1613,28 +1614,28 @@ test "std.zon structs" { const zero = try fromSlice(Vec0, .{ .gpa = gpa, .source = ".{}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec0{}, zero); const one = try fromSlice(Vec1, .{ .gpa = gpa, .source = ".{.x = 1.2}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec1{ .x = 1.2 }, one); const two = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2, .y = 3.4}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 3.4 }, two); const three = try fromSlice(Vec3, .{ .gpa = gpa, .source = ".{.x = 1.2, .y = 3.4, .z = 5.6}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three); } @@ -1647,7 +1648,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed); } @@ -1655,13 +1656,13 @@ test "std.zon structs" { // Unknown field { const Vec2 = struct { x: f32, y: f32 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .z=2.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( @@ -1670,27 +1671,27 @@ test "std.zon structs" { \\ , "{f}", - .{diag}, + .{errors}, ); } // Duplicate field { const Vec2 = struct { x: f32, y: f32 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .x=2.5, .x=3.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( \\1:4: error: duplicate struct field name \\1:12: note: duplicate name here \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Ignore unknown fields @@ -1700,7 +1701,7 @@ test "std.zon structs" { .gpa = gpa, .source = ".{ .x = 1.0, .z = 3.0 }", .ignore_unknown_fields = true, - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec2{ .x = 1.0, .y = 2.0 }, parsed); } @@ -1708,35 +1709,35 @@ test "std.zon structs" { // Unknown field when struct has no fields (regression test) { const Vec2 = struct {}; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5, .z=2.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( \\1:4: error: unexpected field 'x' \\1:4: note: none expected \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Missing field { const Vec2 = struct { x: f32, y: f32 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x=1.5}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{diag}); + try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{errors}); } // Default field @@ -1745,7 +1746,7 @@ test "std.zon structs" { const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } @@ -1756,7 +1757,7 @@ test "std.zon structs" { const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } @@ -1764,17 +1765,17 @@ test "std.zon structs" { // Comptime field assignment { const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); const parsed = fromSlice(Vec2, .{ .gpa = gpa, .source = ".{.x = 1.2, .y = 1.5}", - .diag = &diag, + .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:18: error: cannot initialize comptime field \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Enum field (regression test, we were previously getting the field name in an @@ -1784,7 +1785,7 @@ test "std.zon structs" { const parsed = try fromSlice(Vec0, .{ .gpa = gpa, .source = ".{ .x = .x }", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec0{ .x = .x }, parsed); } @@ -1795,7 +1796,7 @@ test "std.zon structs" { const parsed = try fromSlice(Vec0, .{ .gpa = gpa, .source = ".{ .@\"x x\" = .@\"x x\" }", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec0{ .@"x x" = .@"x x" }, parsed); } @@ -1804,83 +1805,83 @@ test "std.zon structs" { { // Structs { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); const parsed = fromSlice(struct {}, .{ .gpa = gpa, .source = "Empty{}", - .diag = &diag, + .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Arrays { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); const parsed = fromSlice([3]u8, .{ .gpa = gpa, .source = "[3]u8{1, 2, 3}", - .diag = &diag, + .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Slices { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); const parsed = fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "[]u8{1, 2, 3}", - .diag = &diag, + .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Tuples { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); const parsed = fromSlice(struct { u8, u8, u8 }, .{ .gpa = gpa, .source = "Tuple{1, 2, 3}", - .diag = &diag, + .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON \\1:1: note: replace the type with '.' \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Nested { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); const parsed = fromSlice(struct {}, .{ .gpa = gpa, .source = ".{ .x = Tuple{1, 2, 3} }", - .diag = &diag, + .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:9: error: types are not available in ZON \\1:9: note: replace the type with '.' \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } } } @@ -1901,28 +1902,28 @@ test "std.zon tuples" { const zero = try fromSlice(Tuple0, .{ .gpa = gpa, .source = ".{}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Tuple0{}, zero); const one = try fromSlice(Tuple1, .{ .gpa = gpa, .source = ".{1.2}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Tuple1{1.2}, one); const two = try fromSlice(Tuple2, .{ .gpa = gpa, .source = ".{1.2, true}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Tuple2{ 1.2, true }, two); const three = try fromSlice(Tuple3, .{ .gpa = gpa, .source = ".{1.2, false, 3}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Tuple3{ 1.2, false, 3 }, three); } @@ -1934,7 +1935,7 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{\"hello\", \"world\"}", - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed); } @@ -1942,65 +1943,65 @@ test "std.zon tuples" { // Extra field { const Tuple = struct { f32, bool }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Tuple, .{ .gpa = gpa, .source = ".{0.5, true, 123}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{diag}); + try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{errors}); } // Extra field { const Tuple = struct { f32, bool }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Tuple, .{ .gpa = gpa, .source = ".{0.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:2: error: missing tuple field with index 1\n", "{f}", - .{diag}, + .{errors}, ); } // Tuple with unexpected field names { const Tuple = struct { f32 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Tuple, .{ .gpa = gpa, .source = ".{.foo = 10.0}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{diag}); + try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{errors}); } // Struct with missing field names { const Struct = struct { foo: f32 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Struct, .{ .gpa = gpa, .source = ".{10.0}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{diag}); + try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{errors}); } // Comptime field @@ -2009,7 +2010,7 @@ test "std.zon tuples" { const parsed = try fromSlice(Vec2, .{ .gpa = gpa, .source = ".{ 1.2 }", - .diag = null, + .errors = null, }); try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed); } @@ -2017,17 +2018,17 @@ test "std.zon tuples" { // Comptime field assignment { const Vec2 = struct { f32, comptime f32 = 1.5 }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); const parsed = fromSlice(Vec2, .{ .gpa = gpa, .source = ".{ 1.2, 1.5}", - .diag = &diag, + .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:9: error: cannot initialize comptime field \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } } @@ -2045,42 +2046,42 @@ test "std.zon arrays and slices" { const zero = try fromSlice([0]u8, .{ .gpa = gpa, .source = ".{}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &@as([0]u8, .{}), &zero); const one = try fromSlice([1]u8, .{ .gpa = gpa, .source = ".{'a'}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &@as([1]u8, .{'a'}), &one); const two = try fromSlice([2]u8, .{ .gpa = gpa, .source = ".{'a', 'b'}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two); const two_comma = try fromSlice([2]u8, .{ .gpa = gpa, .source = ".{'a', 'b',}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two_comma); const three = try fromSlice([3]u8, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, &three); const sentinel = try fromSlice([3:'z']u8, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", - .diag = null, + .errors = null, }); const expected_sentinel: [3:'z']u8 = .{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, &expected_sentinel, &sentinel); @@ -2092,7 +2093,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero); @@ -2100,7 +2101,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a'}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &.{'a'}, one); @@ -2108,7 +2109,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b'}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two); @@ -2116,7 +2117,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b',}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma); @@ -2124,7 +2125,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .diag = null, + .errors = null, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three); @@ -2132,7 +2133,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .diag = null, + .errors = null, }); const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel); @@ -2147,7 +2148,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{\"abc\"}", - .diag = null, + .errors = null, }); const expected: [1][]const u8 = .{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2159,7 +2160,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{\"abc\"}", - .diag = null, + .errors = null, }); const expected: []const []const u8 = &.{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2173,7 +2174,7 @@ test "std.zon arrays and slices" { const sentinel = try fromSlice([1:2]u8, .{ .gpa = gpa, .source = ".{1}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -2186,7 +2187,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{1}", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -2196,73 +2197,73 @@ test "std.zon arrays and slices" { // Expect 0 find 3 { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([0]u8, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:3: error: index 0 outside of array of length 0\n", "{f}", - .{diag}, + .{errors}, ); } // Expect 1 find 2 { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([1]u8, .{ .gpa = gpa, .source = ".{'a', 'b'}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:8: error: index 1 outside of array of length 1\n", "{f}", - .{diag}, + .{errors}, ); } // Expect 2 find 1 { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([2]u8, .{ .gpa = gpa, .source = ".{'a'}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:2: error: expected 2 array elements; found 1\n", "{f}", - .{diag}, + .{errors}, ); } // Expect 3 find 0 { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([3]u8, .{ .gpa = gpa, .source = ".{}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:2: error: expected 3 array elements; found 0\n", "{f}", - .{diag}, + .{errors}, ); } @@ -2270,31 +2271,31 @@ test "std.zon arrays and slices" { { // Array { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([3]bool, .{ .gpa = gpa, .source = ".{'a', 'b', 'c'}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{errors}); } // Slice { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc([]bool, .{ .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{errors}); } } @@ -2302,40 +2303,40 @@ test "std.zon arrays and slices" { { // Array { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([3]u8, .{ .gpa = gpa, .source = "'a'", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } // Slice { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } } // Address of is not allowed (indirection for slices in ZON is implicit) { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .errors = &errors }), ); try std.testing.expectFmt( "1:3: error: pointers are not available in ZON\n", "{f}", - .{diag}, + .{errors}, ); } } @@ -2352,7 +2353,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"abc\"", - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed); } @@ -2363,7 +2364,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"ab\\nc\"", - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed); } @@ -2374,7 +2375,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"ab\\x00c\"", - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed); } @@ -2382,21 +2383,21 @@ test "std.zon string literal" { // Passing string literal to a mutable slice { { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .diag = &diag }), + fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } } @@ -2407,29 +2408,29 @@ test "std.zon string literal" { defer ast.deinit(gpa); var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(gpa); - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([4:0]u8, .{ .gpa = gpa, .source = "\"abcd\"", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice([4:0]u8, .{ .gpa = gpa, .source = "\\\\abcd", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } } @@ -2440,7 +2441,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"abc\"", - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); @@ -2451,7 +2452,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\\\\abc", - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); @@ -2461,103 +2462,103 @@ test "std.zon string literal" { // Other value terminated slices { { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .diag = &diag }), + fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .diag = &diag }), + fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } } // Expecting string literal, getting something else { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .diag = &diag }), + fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{errors}); } // Expecting string literal, getting an incompatible tuple { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .diag = &diag }), + fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .errors = &errors }), ); - try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{errors}); } // Invalid string literal { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .diag = &diag }), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .errors = &errors }), ); - try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{errors}); } // Slice wrong child type { { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .diag = &diag }), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .diag = &diag }), + fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } } // Bad alignment { { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc([]align(2) const u8, .{ .gpa = gpa, .arena = arena, .source = "\"abc\"", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc([]align(2) const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abc", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); } } @@ -2591,7 +2592,7 @@ test "std.zon string literal" { \\ \\and this. \\} , - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings( "hello, world!\nthis is a multiline string!\n\n...", @@ -2620,36 +2621,36 @@ test "std.zon enum literals" { try std.testing.expectEqual(Enum.foo, try fromSlice(Enum, .{ .gpa = gpa, .source = ".foo", - .diag = null, + .errors = null, })); try std.testing.expectEqual(Enum.bar, try fromSlice(Enum, .{ .gpa = gpa, .source = ".bar", - .diag = null, + .errors = null, })); try std.testing.expectEqual(Enum.baz, try fromSlice(Enum, .{ .gpa = gpa, .source = ".baz", - .diag = null, + .errors = null, })); try std.testing.expectEqual( Enum.@"ab\nc", try fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"ab\\nc\"", - .diag = null, + .errors = null, }), ); // Bad tag { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Enum, .{ .gpa = gpa, .source = ".qux", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( @@ -2658,19 +2659,19 @@ test "std.zon enum literals" { \\ , "{f}", - .{diag}, + .{errors}, ); } // Bad tag that's too long for parser { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"foobarbaz\"", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( @@ -2679,39 +2680,39 @@ test "std.zon enum literals" { \\ , "{f}", - .{diag}, + .{errors}, ); } // Bad type { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Enum, .{ .gpa = gpa, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{errors}); } // Test embedded nulls in an identifier { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(Enum, .{ .gpa = gpa, .source = ".@\"\\x00\"", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:2: error: identifier cannot contain null bytes\n", "{f}", - .{diag}, + .{errors}, ); } } @@ -2726,23 +2727,23 @@ test "std.zon parse bool" { try std.testing.expectEqual(true, try fromSlice(bool, .{ .gpa = gpa, .source = "true", - .diag = null, + .errors = null, })); try std.testing.expectEqual(false, try fromSlice(bool, .{ .gpa = gpa, .source = "false", - .diag = null, + .errors = null, })); // Errors { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(bool, .{ .gpa = gpa, .source = " foo", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( @@ -2750,16 +2751,16 @@ test "std.zon parse bool" { \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\1:2: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(bool, .{ .gpa = gpa, .source = "123", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{f}", .{errors}); } } @@ -2795,46 +2796,46 @@ test "std.zon parse int" { try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, .{ .gpa = gpa, .source = "10", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i16, 24), try fromSlice(i16, .{ .gpa = gpa, .source = "24", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i14, -4), try fromSlice(i14, .{ .gpa = gpa, .source = "-4", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i32, -123), try fromSlice(i32, .{ .gpa = gpa, .source = "-123", - .diag = null, + .errors = null, })); // Test limits try std.testing.expectEqual(@as(i8, 127), try fromSlice(i8, .{ .gpa = gpa, .source = "127", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i8, -128), try fromSlice(i8, .{ .gpa = gpa, .source = "-128", - .diag = null, + .errors = null, })); // Test characters try std.testing.expectEqual(@as(u8, 'a'), try fromSlice(u8, .{ .gpa = gpa, .source = "'a'", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(u8, 'z'), try fromSlice(u8, .{ .gpa = gpa, .source = "'z'", - .diag = null, + .errors = null, })); // Test big integers @@ -2843,7 +2844,7 @@ test "std.zon parse int" { try fromSlice(u65, .{ .gpa = gpa, .source = "36893488147419103231", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -2851,7 +2852,7 @@ test "std.zon parse int" { try fromSlice(u65, .{ .gpa = gpa, .source = "368934_881_474191032_31", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -2859,7 +2860,7 @@ test "std.zon parse int" { try fromSlice(u128, .{ .gpa = gpa, .source = "340282366920938463463374607431768211455", - .diag = null, + .errors = null, }), ); @@ -2869,7 +2870,7 @@ test "std.zon parse int" { try fromSlice(i66, .{ .gpa = gpa, .source = "36893488147419103231", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -2877,33 +2878,33 @@ test "std.zon parse int" { try fromSlice(i66, .{ .gpa = gpa, .source = "-36893488147419103232", - .diag = null, + .errors = null, }), ); { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ .gpa = gpa, .source = "36893488147419103232", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: type 'i66' cannot represent value\n", "{f}", - .{diag}, + .{errors}, ); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ .gpa = gpa, .source = "-36893488147419103233", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: type 'i66' cannot represent value\n", "{f}", - .{diag}, + .{errors}, ); } @@ -2911,224 +2912,224 @@ test "std.zon parse int" { try std.testing.expectEqual(@as(i8, -1), try fromSlice(i8, .{ .gpa = gpa, .source = "-1.0", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i8, 123), try fromSlice(i8, .{ .gpa = gpa, .source = "123.0", - .diag = null, + .errors = null, })); // Test non-decimal integers try std.testing.expectEqual(@as(i16, 0xff), try fromSlice(i16, .{ .gpa = gpa, .source = "0xff", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i16, -0xff), try fromSlice(i16, .{ .gpa = gpa, .source = "-0xff", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i16, 0o77), try fromSlice(i16, .{ .gpa = gpa, .source = "0o77", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i16, -0o77), try fromSlice(i16, .{ .gpa = gpa, .source = "-0o77", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i16, 0b11), try fromSlice(i16, .{ .gpa = gpa, .source = "0b11", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i16, -0b11), try fromSlice(i16, .{ .gpa = gpa, .source = "-0b11", - .diag = null, + .errors = null, })); // Test non-decimal big integers try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ .gpa = gpa, .source = "0x1ffffffffffffffff", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ .gpa = gpa, .source = "0x1ffffffffffffffff", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ .gpa = gpa, .source = "-0x1ffffffffffffffff", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ .gpa = gpa, .source = "0o3777777777777777777777", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ .gpa = gpa, .source = "0o3777777777777777777777", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ .gpa = gpa, .source = "-0o3777777777777777777777", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ .gpa = gpa, .source = "0b11111111111111111111111111111111111111111111111111111111111111111", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ .gpa = gpa, .source = "0b11111111111111111111111111111111111111111111111111111111111111111", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ .gpa = gpa, .source = "-0b11111111111111111111111111111111111111111111111111111111111111111", - .diag = null, + .errors = null, })); // Number with invalid character in the middle { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "32a32", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:3: error: invalid digit 'a' for decimal base\n", "{f}", - .{diag}, + .{errors}, ); } // Failing to parse as int { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "true", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{f}", .{errors}); } // Failing because an int is out of range { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "256", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", - .{diag}, + .{errors}, ); } // Failing because a negative int is out of range { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "-129", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: type 'i8' cannot represent value\n", "{f}", - .{diag}, + .{errors}, ); } // Failing because an unsigned int is negative { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "-1", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", - .{diag}, + .{errors}, ); } // Failing because a float is non-whole { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "1.5", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", - .{diag}, + .{errors}, ); } // Failing because a float is negative { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "-1.0", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: type 'u8' cannot represent value\n", "{f}", - .{diag}, + .{errors}, ); } // Negative integer zero { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "-0", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( \\1:2: error: integer literal '-0' is ambiguous \\1:2: note: use '0' for an integer zero \\1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Negative integer zero casted to float { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-0", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( \\1:2: error: integer literal '-0' is ambiguous \\1:2: note: use '0' for an integer zero \\1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } // Negative float 0 is allowed @@ -3136,71 +3137,71 @@ test "std.zon parse int" { std.math.isNegativeZero(try fromSlice(f32, .{ .gpa = gpa, .source = "-0.0", - .diag = null, + .errors = null, })), ); try std.testing.expect(std.math.isPositiveZero(try fromSlice(f32, .{ .gpa = gpa, .source = "0.0", - .diag = null, + .errors = null, }))); // Double negation is not allowed { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "--2", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", - .{diag}, + .{errors}, ); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "--2.0", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", - .{diag}, + .{errors}, ); } // Invalid int literal { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "0xg", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{f}", .{diag}); + try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{f}", .{errors}); } // Notes on invalid int literal { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ .gpa = gpa, .source = "0123", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( \\1:1: error: number '0123' has leading zero \\1:1: note: use '0o' prefix for octal literals \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } } @@ -3211,29 +3212,29 @@ test "std.zon negative char" { const arena = arena_allocator.allocator(); { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-'a'", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", - .{diag}, + .{errors}, ); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i16, .{ .gpa = gpa, .source = "-'a'", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", - .{diag}, + .{errors}, ); } } @@ -3248,14 +3249,14 @@ test "std.zon parse float" { try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, .{ .gpa = gpa, .source = "0.5", - .diag = null, + .errors = null, })); try std.testing.expectEqual( @as(f32, 123.456), try fromSlice(f32, .{ .gpa = gpa, .source = "123.456", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -3263,47 +3264,47 @@ test "std.zon parse float" { try fromSlice(f64, .{ .gpa = gpa, .source = "-123.456", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, .{ .gpa = gpa, .source = "42.5", - .diag = null, + .errors = null, })); // Test whole numbers with and without decimals try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, .{ .gpa = gpa, .source = "5.0", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, .{ .gpa = gpa, .source = "5", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, .{ .gpa = gpa, .source = "-102.0", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, .{ .gpa = gpa, .source = "-102", - .diag = null, + .errors = null, })); // Test characters and negated characters try std.testing.expectEqual(@as(f32, 'a'), try fromSlice(f32, .{ .gpa = gpa, .source = "'a'", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(f32, 'z'), try fromSlice(f32, .{ .gpa = gpa, .source = "'z'", - .diag = null, + .errors = null, })); // Test big integers @@ -3312,7 +3313,7 @@ test "std.zon parse float" { try fromSlice(f32, .{ .gpa = gpa, .source = "36893488147419103231", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -3320,18 +3321,18 @@ test "std.zon parse float" { try fromSlice(f32, .{ .gpa = gpa, .source = "-36893488147419103231", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(f128, .{ .gpa = gpa, .source = "0x1ffffffffffffffff", - .diag = null, + .errors = null, })); try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(f32, .{ .gpa = gpa, .source = "0x1ffffffffffffffff", - .diag = null, + .errors = null, })); // Exponents, underscores @@ -3340,7 +3341,7 @@ test "std.zon parse float" { try fromSlice(f32, .{ .gpa = gpa, .source = "12_3.0E+77", - .diag = null, + .errors = null, }), ); @@ -3350,7 +3351,7 @@ test "std.zon parse float" { try fromSlice(f32, .{ .gpa = gpa, .source = "0x103.70p-5", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -3358,7 +3359,7 @@ test "std.zon parse float" { try fromSlice(f32, .{ .gpa = gpa, .source = "-0x103.70", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -3366,7 +3367,7 @@ test "std.zon parse float" { try fromSlice(f32, .{ .gpa = gpa, .source = "0x1234_5678.9ABC_CDEFp-10", - .diag = null, + .errors = null, }), ); @@ -3374,120 +3375,120 @@ test "std.zon parse float" { try std.testing.expect(std.math.isPositiveInf(try fromSlice(f32, .{ .gpa = gpa, .source = "inf", - .diag = null, + .errors = null, }))); try std.testing.expect(std.math.isNegativeInf(try fromSlice(f32, .{ .gpa = gpa, .source = "-inf", - .diag = null, + .errors = null, }))); try std.testing.expect(std.math.isNan(try fromSlice(f32, .{ .gpa = gpa, .source = "nan", - .diag = null, + .errors = null, }))); // Negative nan not allowed { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-nan", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", - .{diag}, + .{errors}, ); } // nan as int not allowed { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "nan", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); } // nan as int not allowed { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "nan", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); } // inf as int not allowed { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "inf", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); } // -inf as int not allowed { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ .gpa = gpa, .source = "-inf", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); } // Bad identifier as float { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "foo", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( \\1:1: error: invalid expression \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\1:1: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{diag}); + , "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "-foo", - .diag = &diag, + .errors = &errors, })); try std.testing.expectFmt( "1:1: error: expected number or 'inf' after '-'\n", "{f}", - .{diag}, + .{errors}, ); } // Non float as float { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(f32, .{ .gpa = gpa, .source = "\"foo\"", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{errors}); } } @@ -3514,7 +3515,7 @@ test "std.zon free on error" { \\ .z = "fail", \\} , - .diag = null, + .errors = null, })); } @@ -3535,7 +3536,7 @@ test "std.zon free on error" { \\ "fail", \\} , - .diag = null, + .errors = null, })); } @@ -3552,7 +3553,7 @@ test "std.zon free on error" { \\ .x = "hello", \\} , - .diag = null, + .errors = null, })); // Test freeing partially allocated arrays @@ -3566,7 +3567,7 @@ test "std.zon free on error" { \\ false, \\} , - .diag = null, + .errors = null, })); // Test freeing partially allocated slices @@ -3580,7 +3581,7 @@ test "std.zon free on error" { \\ false, \\} , - .diag = null, + .errors = null, })); // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged @@ -3590,7 +3591,7 @@ test "std.zon free on error" { (try fromSlice(union { x: f32 }, .{ .gpa = gpa, .source = ".{ .x = 1.5 }", - .diag = null, + .errors = null, })).x, ); @@ -3601,7 +3602,7 @@ test "std.zon free on error" { .gpa = gpa, .arena = arena, .source = ".{ .x = \"foo\" }", - .diag = null, + .errors = null, }); try std.testing.expectEqualStrings("foo", result.x); } @@ -3622,7 +3623,7 @@ test "std.zon vector" { try fromSlice(@Vector(0, bool), .{ .gpa = gpa, .source = ".{}", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -3630,7 +3631,7 @@ test "std.zon vector" { try fromSlice(@Vector(3, bool), .{ .gpa = gpa, .source = ".{true, false, true}", - .diag = null, + .errors = null, }), ); @@ -3639,7 +3640,7 @@ test "std.zon vector" { try fromSlice(@Vector(0, f32), .{ .gpa = gpa, .source = ".{}", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -3647,7 +3648,7 @@ test "std.zon vector" { try fromSlice(@Vector(3, f32), .{ .gpa = gpa, .source = ".{1.5, 2.5, 3.5}", - .diag = null, + .errors = null, }), ); @@ -3656,7 +3657,7 @@ test "std.zon vector" { try fromSlice(@Vector(0, u8), .{ .gpa = gpa, .source = ".{}", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -3664,7 +3665,7 @@ test "std.zon vector" { try fromSlice(@Vector(3, u8), .{ .gpa = gpa, .source = ".{2, 4, 6}", - .diag = null, + .errors = null, }), ); @@ -3675,14 +3676,14 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{}", - .diag = null, + .errors = null, }), ); const pointers = try fromSliceAlloc(@Vector(3, *const u8), .{ .gpa = gpa, .arena = arena, .source = ".{2, 4, 6}", - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers); } @@ -3694,99 +3695,99 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{}", - .diag = null, + .errors = null, }), ); const pointers = try fromSliceAlloc(@Vector(3, ?*const u8), .{ .gpa = gpa, .arena = arena, .source = ".{2, null, 6}", - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers); } // Too few fields { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(@Vector(2, f32), .{ .gpa = gpa, .source = ".{0.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:2: error: expected 2 array elements; found 1\n", "{f}", - .{diag}, + .{errors}, ); } // Too many fields { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(@Vector(2, f32), .{ .gpa = gpa, .source = ".{0.5, 1.5, 2.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:13: error: index 2 outside of array of length 2\n", "{f}", - .{diag}, + .{errors}, ); } // Wrong type fields { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(@Vector(3, f32), .{ .gpa = gpa, .source = ".{0.5, true, 2.5}", - .diag = &diag, + .errors = &errors, }), ); try std.testing.expectFmt( "1:8: error: expected type 'f32'\n", "{f}", - .{diag}, + .{errors}, ); } // Wrong type { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSlice(@Vector(3, u8), .{ .gpa = gpa, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{errors}); } // Elements should get freed on error { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(@Vector(3, *u8), .{ .gpa = gpa, .arena = arena, .source = ".{1, true, 3}", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{errors}); } } @@ -3802,7 +3803,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), result.*); } @@ -3812,7 +3813,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), result.*.*); } @@ -3822,7 +3823,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), result.*.*.*); } @@ -3833,7 +3834,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), some.?.*); @@ -3841,7 +3842,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .diag = null, + .errors = null, }); try std.testing.expectEqual(null, none); } @@ -3851,7 +3852,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), some.*.?); @@ -3859,7 +3860,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .diag = null, + .errors = null, }); try std.testing.expectEqual(null, none.*); } @@ -3869,7 +3870,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), some.?.*.*); @@ -3877,7 +3878,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .diag = null, + .errors = null, }); try std.testing.expectEqual(null, none); } @@ -3887,7 +3888,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), some.*.?.*); @@ -3895,7 +3896,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .diag = null, + .errors = null, }); try std.testing.expectEqual(null, none.*); } @@ -3905,7 +3906,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .diag = null, + .errors = null, }); try std.testing.expectEqual(@as(u32, 10), some.*.*.?); @@ -3913,7 +3914,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .diag = null, + .errors = null, }); try std.testing.expectEqual(null, none.*.*); } @@ -3924,7 +3925,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = ".{ 1, 2, 3 }", - .diag = null, + .errors = null, }); try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*); } @@ -3959,7 +3960,7 @@ test "std.zon add pointers" { \\ .f2 = null, \\} , - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(expected, found.?.*); @@ -3967,185 +3968,185 @@ test "std.zon add pointers" { // Test that optional types are flattened correctly in errors { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const u8, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const f32, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const @Vector(3, u8), .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const bool, .{ .gpa = gpa, .arena = arena, .source = "10", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const struct { a: i32 }, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const struct { i32 }, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const union { x: void }, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const [3]u8, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(?[3]u8, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const []u8, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(?[]u8, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const []const u8, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{errors}); } { - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, fromSliceAlloc(*const ?*const enum { foo }, .{ .gpa = gpa, .arena = arena, .source = "true", - .diag = &diag, + .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{diag}); + try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{errors}); } } @@ -4167,7 +4168,7 @@ test "std.zon stop on node" { const result = try fromZoir(Vec2, .{ .ast = &ast, .zoir = &zoir, - .diag = null, + .errors = null, }); try std.testing.expectEqual(result.y, 2.5); @@ -4184,7 +4185,7 @@ test "std.zon stop on node" { const result = try fromZoir(Zoir.Node.Index, .{ .ast = &ast, .zoir = &zoir, - .diag = null, + .errors = null, }); try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(&zoir)); } @@ -4198,7 +4199,7 @@ test "std.zon no alloc" { try fromSlice([3]u8, .{ .gpa = gpa, .source = ".{ 1, 2, 3 }", - .diag = null, + .errors = null, }), ); @@ -4215,12 +4216,12 @@ test "std.zon no alloc" { try fromZoir(Nested, .{ .ast = &ast, .zoir = &zoir, - .diag = null, + .errors = null, }), ); } -test "std.zon errors without diagnostics" { +test "std.zon errors without errorsnostics" { const gpa = std.testing.allocator; var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); @@ -4235,7 +4236,7 @@ test "std.zon errors without diagnostics" { .gpa = gpa, .arena = arena, .source = ".nothing", - .diag = null, + .errors = null, })); const Struct = struct { @@ -4245,7 +4246,7 @@ test "std.zon errors without diagnostics" { .gpa = gpa, .arena = arena, .source = ".{ .name = \"Alice\", .age = 25 }", - .diag = null, + .errors = null, })); const Union = union(enum) { @@ -4256,13 +4257,13 @@ test "std.zon errors without diagnostics" { .gpa = gpa, .arena = arena, .source = ".a", - .diag = null, + .errors = null, })); try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ .gpa = gpa, .arena = arena, .source = ".{ .b = 8 }", - .diag = null, + .errors = null, })); } @@ -4284,7 +4285,7 @@ test "std.zon aligned pointers" { .gpa = gpa, .arena = arena, .source = ".{ .inner = 10 }", - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(expected.inner, found.inner); } @@ -4329,7 +4330,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{}", - .diag = null, + .errors = null, }); try std.testing.expectEqualDeep(expected, found); @@ -4337,7 +4338,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .bar = false }", - .diag = null, + .errors = null, }); expected.bar = false; try std.testing.expectEqualDeep(expected, found); @@ -4346,7 +4347,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .baz = .{ .a = .{ 2, 4 } } }", - .diag = null, + .errors = null, }); expected.baz.a[0] = 2; expected.baz.a[1] = 4; @@ -4356,7 +4357,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .foo = 11, .baz = .{ .b = .d } }", - .diag = null, + .errors = null, }); expected.foo = 11; expected.baz.b = .d; @@ -4366,7 +4367,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .optional = 10 }", - .diag = null, + .errors = null, }); expected.optional = 10; try std.testing.expectEqualDeep(expected, found); @@ -4375,7 +4376,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .optional = null }", - .diag = null, + .errors = null, }); expected.optional = null; try std.testing.expectEqualDeep(expected, found); @@ -4384,7 +4385,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", - .diag = null, + .errors = null, }); expected.ptr.x = 10; expected.ptr.y = 20; @@ -4395,7 +4396,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .str = \"foo\" }", - .diag = null, + .errors = null, }); expected.str = "foo"; try std.testing.expectEqualDeep(expected, found); @@ -4418,7 +4419,7 @@ test "std.zon update optionals" { try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .source = ".{ .foo = .{ .qux = 3 } }", - .diag = null, + .errors = null, }); expected.foo = .{ .qux = 3 }; try std.testing.expectEqual(expected, found); @@ -4427,13 +4428,13 @@ test "std.zon update optionals" { // Updating an optional that starts out null should error if we leave off required fields. { var found: MyStruct = .{ .foo = null }; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .source = ".{ .foo = .{} }", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{diag}); + try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{errors}); } // Updating an optional that starts out non-null should preserve any values we leave off. It's @@ -4446,7 +4447,7 @@ test "std.zon update optionals" { try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .source = ".{ .foo = .{ .baz = 200 } }", - .diag = null, + .errors = null, }); expected.foo.?.baz = 200; try std.testing.expectEqual(expected, found); @@ -4471,7 +4472,7 @@ test "std.zon update optional pointers" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .qux = 3 } }", - .diag = null, + .errors = null, }); expected.foo = &.{ .qux = 3 }; try std.testing.expectEqualDeep(expected, found); @@ -4496,7 +4497,7 @@ test "std.zon update pointers" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .bar = 100 } }", - .diag = null, + .errors = null, }); expected.foo.bar = 100; try std.testing.expectEqualDeep(expected, found); @@ -4513,7 +4514,7 @@ test "std.zon update pointers" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .bar = 100 } }", - .diag = null, + .errors = null, }); expected.foo = &.{ .bar = 100, .baz = 20, .qux = 3 }; try std.testing.expectEqualDeep(expected, found); @@ -4538,7 +4539,7 @@ test "std.zon update unions" { try updateFromSlice(MyUnion, &found, .{ .gpa = gpa, .source = ".{ .foo = .{ .qux = 3 } }", - .diag = null, + .errors = null, }); expected = .{ .foo = .{ .qux = 3 } }; try std.testing.expectEqual(expected, found); @@ -4547,13 +4548,13 @@ test "std.zon update unions" { // Updating a union to a new field should error if we leave off required sub-fields { var found: MyUnion = .none; - var diag: Diagnostics = .init(arena); + var errors: Errors = .init(arena); try std.testing.expectError(error.ParseZon, updateFromSlice(MyUnion, &found, .{ .gpa = gpa, .source = ".{ .foo = .{} }", - .diag = &diag, + .errors = &errors, })); - try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{diag}); + try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{errors}); } // Updating a union should preseve any sub-fields that we left off. It's also okay to leave off @@ -4566,7 +4567,7 @@ test "std.zon update unions" { try updateFromSlice(MyUnion, &found, .{ .gpa = gpa, .source = ".{ .foo = .{ .baz = 200 } }", - .diag = null, + .errors = null, }); expected.foo.baz = 200; try std.testing.expectEqual(expected, found); @@ -4597,7 +4598,7 @@ test "std.zon variants" { try updateFromSlice(Struct, &curr, .{ .gpa = gpa, .source = ".{ .a = 100 }", - .diag = null, + .errors = null, }); try std.testing.expectEqual(end, curr); @@ -4606,7 +4607,7 @@ test "std.zon variants" { .gpa = gpa, .arena = arena, .source = ".{ .a = 100 }", - .diag = null, + .errors = null, }); try std.testing.expectEqual(end, curr); @@ -4614,7 +4615,7 @@ test "std.zon variants" { try updateFromZoir(Struct, &curr, .{ .ast = &ast, .zoir = &zoir, - .diag = null, + .errors = null, }); try std.testing.expectEqual(end, curr); @@ -4623,7 +4624,7 @@ test "std.zon variants" { .arena = arena, .ast = &ast, .zoir = &zoir, - .diag = null, + .errors = null, }); try std.testing.expectEqual(end, curr); } @@ -4640,7 +4641,7 @@ test "std.zon variants" { try fromSlice(Struct, .{ .gpa = gpa, .source = ".{ .a = 100, .b = 20 }", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( @@ -4649,14 +4650,14 @@ test "std.zon variants" { .gpa = gpa, .arena = arena, .source = ".{ .a = 100, .b = 20 }", - .diag = null, + .errors = null, }), ); try std.testing.expectEqual( try fromZoir(Struct, .{ .ast = &ast, .zoir = &zoir, - .diag = null, + .errors = null, }), end, ); @@ -4665,7 +4666,7 @@ test "std.zon variants" { .arena = arena, .ast = &ast, .zoir = &zoir, - .diag = null, + .errors = null, }), end, ); -- 2.54.0 From 5f457157573edbd99c9e685892b8f81c49cf5967 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 20 Jul 2026 00:52:36 -0700 Subject: [PATCH 10/26] Add `alignedCreate` --- lib/std/mem/Allocator.zig | 23 ++++++++++++++++++++++- lib/std/zon/parse.zig | 5 ++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 1b63cc6a0f7ea2305c8ce1f394eccb6136ffd877..43db84febd6f222ef617d48bb136fd46abee2f9d 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -168,14 +168,35 @@ pub inline fn rawFree(a: Allocator, memory: []u8, alignment: Alignment, ret_addr /// Returns a pointer to undefined memory. /// Call `destroy` with the result to free the memory. pub fn create(a: Allocator, comptime T: type) Error!*T { + return a.createAdvancedWithRetAddr(T, null, @returnAddress()); +} + +pub fn alignedCreate( + self: Allocator, + comptime T: type, + /// null means naturally aligned + comptime alignment: ?Alignment, +) Error!*align(if (alignment) |a| a.toByteUnits() else @alignOf(T)) T { + return self.createAdvancedWithRetAddr(T, alignment, @returnAddress()); +} + +pub inline fn createAdvancedWithRetAddr( + self: Allocator, + comptime T: type, + /// null means naturally aligned + comptime alignment: ?Alignment, + return_address: usize, +) Error!*align(if (alignment) |a| a.toByteUnits() else @alignOf(T)) T { if (@sizeOf(T) == 0) { const ptr = comptime std.mem.alignBackward(usize, math.maxInt(usize), @alignOf(T)); return @ptrFromInt(ptr); } - const ptr: *T = @ptrCast(try a.allocBytesAligned(.of(T), @sizeOf(T), @returnAddress())); + const a: Alignment = alignment orelse comptime .of(T); + const ptr: *align(a.toByteUnits()) T = @ptrCast(try self.allocBytesAligned(a, @sizeOf(T), return_address)); return ptr; } + /// `ptr` should be the return value of `create`, or otherwise /// have the same address and alignment property. pub fn destroy(self: Allocator, ptr: anytype) void { diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index bd9281267a160c5636225aa06988036f76823325..4585d111a842c7bc118c30d8e2b3da25324c33fc 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -473,11 +473,10 @@ const Parser = struct { // If the existing value is initialized and non const, use it if (initialized and !pointer.attrs.@"const") break :b out.*; // Otherwise, allocate new memory - const new = &(try self.arena.alignedAlloc( + const new = try self.arena.alignedCreate( pointer.child, if (pointer.attrs.@"align") |a| .fromByteUnits(a) else null, - 1, - ))[0]; + ); // If the original value was initialized, dupe it into the new memory if (initialized) new.* = out.*.*; break :b new; -- 2.54.0 From e3a0e02ef28c96b9fe02fe564a18d44f767036b9 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 20 Jul 2026 00:59:31 -0700 Subject: [PATCH 11/26] Clean up doc comments --- lib/std/mem/Allocator.zig | 1 - lib/std/zon/parse.zig | 26 ++++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 43db84febd6f222ef617d48bb136fd46abee2f9d..be76c4982d1a2bd0284978ff6a57477097b570ca 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -196,7 +196,6 @@ pub inline fn createAdvancedWithRetAddr( return ptr; } - /// `ptr` should be the return value of `create`, or otherwise /// have the same address and alignment property. pub fn destroy(self: Allocator, ptr: anytype) void { diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 4585d111a842c7bc118c30d8e2b3da25324c33fc..3d6af24e713ee3254824beee03a03914be57c539 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -1,11 +1,10 @@ -//! The simplest way to parse ZON at runtime is to use `fromSlice`/`fromSliceAlloc`. +//! Use `fromSlice`/`fromSliceAlloc` to parse a ZON string into a Zig value. Human readable errors +//! are written to an out parameter. //! -//! Parsing from individual Zoir nodes is also available: -//! * `fromZoir`/`fromZoirAlloc` +//! If your data is already available in tree form, consider `fromZoir`/`fromZoirAlloc`. //! -//! To update an existing values, see the `updateFrom*` variants. To get human readable errors, use -//! the `errors` option. For lower level control over parsing, see `std.zig.Zoir`. For importing ZON -//! at compile time, use `@import`. +//! To parse into an existing value, see the `updateFrom*` variants. For lower level control over +//! parsing, see `std.zig.ZonGen`. For importing ZON at compile time, use `@import`. const std = @import("std"); const Allocator = std.mem.Allocator; @@ -91,7 +90,8 @@ pub const Error = struct { }; }; -/// Errors encountered while parsing ZON. +/// Errors encountered while parsing ZON. Consider logging this using the default format method to +/// provide users with feedback on the failure. pub const Errors = struct { arena: Allocator, list: std.ArrayList(Error), @@ -160,9 +160,11 @@ pub fn fromSlice(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZo }); } -/// Similar to `fromSlice`, but updates an existing value `current`. Fields not specified by ZON are -/// left at their current values. Pointers are replaced with new values not mutated in place. On -/// failure, values that were updated prior to the failure are left updated. +/// Similar to `fromSlice`, but updates `value` in place which is assumed to hold a valid value. Any +/// unspecified fields are left unchanged. +/// +/// Untagged unions and slices are replaced in their entiredy. If an error occurs while parsing, +/// `value` remains valid but may be partially updated. pub fn updateFromSlice( T: type, value: *T, @@ -181,7 +183,7 @@ pub fn updateFromSlice( pub const FromSliceAllocOptions = struct { /// Used for scratch allocations. gpa: Allocator, - /// Owns the result. + /// Used for any allocations required by the out type. arena: Allocator, /// The ZON source to parse. source: [:0]const u8, @@ -303,7 +305,7 @@ pub fn updateFromZoir( } pub const FromZoirAllocOptions = struct { - /// Owns the result. + /// Used for any allocations required by the out type. arena: Allocator, /// The AST for this ZON value. ast: *const Ast, -- 2.54.0 From b3bb6d923fef98b0b0da8f0848e235b06a9e473f Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 20 Jul 2026 17:07:30 -0700 Subject: [PATCH 12/26] Update calls to Zoir API --- src/Sema/LowerZon.zig | 52 +++++++++++++++++++++---------------------- src/print_zoir.zig | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index dd8c91c244518ac23d1d5d0be6caa30861f2c4c7..f6d39006af6a0605ad5fd77a8494504bc266b4cb 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -71,7 +71,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - switch (node.get(self.file.zoir.?)) { + switch (node.get(&self.file.zoir.?)) { .true => return .bool_true, .false => return .bool_false, .null => return .null_value, @@ -101,7 +101,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter gpa, io, pt.tid, - val.get(self.file.zoir.?), + val.get(&self.file.zoir.?), .no_embedded_nulls, ), }), @@ -174,7 +174,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter gpa, io, pt.tid, - zoir_name.get(self.file.zoir.?), + zoir_name.get(&self.file.zoir.?), .no_embedded_nulls, ); field_ty.* = ip.typeOf(field_val); @@ -312,7 +312,7 @@ fn checkTypeInner( fn nodeSrc(self: *LowerZon, node: Zoir.Node.Index) LazySrcLoc { return .{ .base_node_inst = self.base_node_inst, - .offset = .{ .node_abs = node.getAstNode(self.file.zoir.?) }, + .offset = .{ .node_abs = node.getAstNode(&self.file.zoir.?) }, }; } @@ -363,7 +363,7 @@ fn lowerExprKnownResTyInner( .optional => return pt.intern(.{ .opt = .{ .ty = res_ty.toIntern(), - .val = if (node.get(self.file.zoir.?) == .null) b: { + .val = if (node.get(&self.file.zoir.?) == .null) b: { break :b .none; } else b: { const child_type = res_ty.optionalChild(pt.zcu); @@ -418,7 +418,7 @@ fn lowerExprKnownResTyInner( } fn lowerBool(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { - return switch (node.get(self.file.zoir.?)) { + return switch (node.get(&self.file.zoir.?)) { .true => .bool_true, .false => .bool_false, else => return error.WrongType, @@ -431,7 +431,7 @@ fn lowerInt( res_ty: Type, ) !InternPool.Index { @setFloatMode(.strict); - return switch (node.get(self.file.zoir.?)) { + return switch (node.get(&self.file.zoir.?)) { .int_literal => |int| switch (int) { .small => |val| { const rhs: i32 = val; @@ -555,7 +555,7 @@ fn lowerFloat( res_ty: Type, ) !InternPool.Index { @setFloatMode(.strict); - const value = switch (node.get(self.file.zoir.?)) { + const value = switch (node.get(&self.file.zoir.?)) { .int_literal => |int| switch (int) { .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))), .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128, .nearest_even)[0]), @@ -592,7 +592,7 @@ fn lowerFloat( } fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { - switch (node.get(self.file.zoir.?)) { + switch (node.get(&self.file.zoir.?)) { .null => return .null_value, else => return error.WrongType, } @@ -600,7 +600,7 @@ fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { const array_info = res_ty.arrayInfo(self.sema.pt.zcu); - const nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const nodes: Zoir.Node.Index.Range = switch (node.get(&self.file.zoir.?)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, @@ -633,13 +633,13 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I const io = comp.io; const ip = &pt.zcu.intern_pool; try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init); - switch (node.get(self.file.zoir.?)) { + switch (node.get(&self.file.zoir.?)) { .enum_literal => |field_name| { const field_name_interned = try ip.getOrPutString( gpa, io, self.sema.pt.tid, - field_name.get(self.file.zoir.?), + field_name.get(&self.file.zoir.?), .no_embedded_nulls, ); const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse { @@ -648,7 +648,7 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I "enum {f} has no member named '{f}'", .{ res_ty.fmt(self.sema.pt), - std.zig.fmtId(field_name.get(self.file.zoir.?)), + std.zig.fmtId(field_name.get(&self.file.zoir.?)), }, ); }; @@ -667,13 +667,13 @@ fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - switch (node.get(self.file.zoir.?)) { + switch (node.get(&self.file.zoir.?)) { .enum_literal => |field_name| { const field_name_interned = try ip.getOrPutString( gpa, io, self.sema.pt.tid, - field_name.get(self.file.zoir.?), + field_name.get(&self.file.zoir.?), .no_embedded_nulls, ); return self.sema.pt.intern(.{ .enum_literal = field_name_interned }); @@ -696,7 +696,7 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const tuple_info = ip.indexToKey(res_ty.toIntern()).tuple_type; - const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const elem_nodes: Zoir.Node.Index.Range = switch (node.get(&self.file.zoir.?)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, @@ -720,7 +720,7 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. "index {} outside tuple of length {}", .{ elems.len, - elem_nodes.at(@intCast(i)).getAstNode(self.file.zoir.?), + elem_nodes.at(@intCast(i)).getAstNode(&self.file.zoir.?), }, ); } @@ -760,7 +760,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool try self.sema.ensureStructDefaultsResolved(res_ty, self.import_loc); const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?; - const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { + const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(&self.file.zoir.?)) { .struct_literal => |fields| fields, .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } }, else => return error.WrongType, @@ -780,7 +780,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool gpa, io, self.sema.pt.tid, - fields.names[i].get(self.file.zoir.?), + fields.names[i].get(&self.file.zoir.?), .no_embedded_nulls, ); const field_node = fields.vals.at(@intCast(i)); @@ -844,7 +844,7 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const string_alignment = ptr_info.flags.alignment == .none or ptr_info.flags.alignment == .@"1"; const string_sentinel = ptr_info.sentinel == .none or ptr_info.sentinel == .zero_u8; if (string_alignment and ptr_info.child == .u8_type and string_sentinel) { - switch (node.get(self.file.zoir.?)) { + switch (node.get(&self.file.zoir.?)) { .string_literal => |val| { const ip_str = try ip.getOrPutString(gpa, io, self.sema.pt.tid, val, .maybe_embedded_nulls); const str_ref = try self.sema.addStrLit(ip_str, val.len); @@ -860,7 +860,7 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. } // Slice literals - const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const elem_nodes: Zoir.Node.Index.Range = switch (node.get(&self.file.zoir.?)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, @@ -927,19 +927,19 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const union_info = pt.zcu.typeToUnion(res_ty).?; const enum_tag_info = ip.loadEnumType(union_info.enum_tag_type); - const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) { + const field_name, const maybe_field_node = switch (node.get(&self.file.zoir.?)) { .enum_literal => |name| b: { const field_name = try ip.getOrPutString( gpa, io, self.sema.pt.tid, - name.get(self.file.zoir.?), + name.get(&self.file.zoir.?), .no_embedded_nulls, ); break :b .{ field_name, null }; }, .struct_literal => b: { - const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { + const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(&self.file.zoir.?)) { .struct_literal => |fields| fields, else => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(self.sema.pt)}), }; @@ -950,7 +950,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. gpa, io, self.sema.pt.tid, - fields.names[0].get(self.file.zoir.?), + fields.names[0].get(&self.file.zoir.?), .no_embedded_nulls, ); break :b .{ field_name, fields.vals.at(0) }; @@ -986,7 +986,7 @@ fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool const vector_info = ip.indexToKey(res_ty.toIntern()).vector_type; - const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const elem_nodes: Zoir.Node.Index.Range = switch (node.get(&self.file.zoir.?)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, diff --git a/src/print_zoir.zig b/src/print_zoir.zig index 93b4553b91ef13048989775d0c67b73574fd161c..412e7db4e085e5e7b58eed9bb819f6b2f4b615a3 100644 --- a/src/print_zoir.zig +++ b/src/print_zoir.zig @@ -54,7 +54,7 @@ const PrintZon = struct { } fn renderNode(pz: *PrintZon, node: Zoir.Node.Index) Error!void { - const zoir = pz.zoir; + const zoir = &pz.zoir; try pz.w.print("%{d} = ", .{@backingInt(node)}); switch (node.get(zoir)) { .true => try pz.w.writeAll("true"), -- 2.54.0 From ab52406e0c5598b651b11f3f7f93ad5ddb875896 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 20 Jul 2026 22:29:10 -0700 Subject: [PATCH 13/26] Fix optional initialization --- lib/std/zon/parse.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 3d6af24e713ee3254824beee03a03914be57c539..eec6034d193183405327ebb3d9e886618c90eb41 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -459,10 +459,12 @@ const Parser = struct { switch (@typeInfo(@TypeOf(out.*))) { .optional => |optional| if (node.get(self.zoir) == .null) { out.* = null; + } else if (initialized and out.* != null) { + try self.parseExprInnerInto(node, &out.*.?, true); } else { - const child_initialized = initialized and out.* != null; - if (!child_initialized) out.* = @as(optional.child, undefined); - try self.parseExprInnerInto(node, &out.*.?, child_initialized); + var some: optional.child = undefined; + try self.parseExprInnerInto(node, &some, false); + out.* = some; }, .bool => out.* = try self.parseBool(node), .int => out.* = try self.parseInt(@TypeOf(out.*), node), -- 2.54.0 From e4a7988fec0d53e5f48a1701aeebacf192198203 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Fri, 7 Aug 2026 15:01:20 -0700 Subject: [PATCH 14/26] Add a path to Errors formatter, add `log` and `fatal` helpers --- lib/std/zon/parse.zig | 719 ++++++++++++++++++++++++++---------------- 1 file changed, 441 insertions(+), 278 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index eec6034d193183405327ebb3d9e886618c90eb41..33c747291f3cf91f4d74803f46deb20914bd2466 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -7,6 +7,7 @@ //! parsing, see `std.zig.ZonGen`. For importing ZON at compile time, use `@import`. const std = @import("std"); +const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const Ast = std.zig.Ast; const Zoir = std.zig.Zoir; @@ -39,7 +40,7 @@ pub const Error = struct { notes: []Note, }; - fn init(ast: *const Ast, options: InitOptions) @This() { + fn init(ast: *const Ast, options: InitOptions) Error { return .{ .msg = options.msg, .loc = astLoc(ast, options.token, options.node_or_offset), @@ -90,40 +91,110 @@ pub const Error = struct { }; }; -/// Errors encountered while parsing ZON. Consider logging this using the default format method to -/// provide users with feedback on the failure. +/// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user. pub const Errors = struct { arena: Allocator, list: std.ArrayList(Error), - pub fn init(arena: Allocator) @This() { + pub fn init(arena: Allocator) Errors { return .{ .arena = arena, .list = .empty, }; } - pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - for (self.list.items) |e| { - try w.print("{d}:{d}: error: {s}\n", .{ - e.loc.line + 1, - e.loc.column + 1, - e.msg, - }); - - for (e.notes) |note| { - try w.print("{d}:{d}: note: {s}\n", .{ - note.loc.line + 1, - note.loc.column + 1, - note.msg, + /// Log the failure with `std.log.err`. + pub fn log( + self: *const Errors, + path: []const u8, + err: error{ OutOfMemory, ParseZon }, + ) void { + std.log.err("{s}: {}\n{f}", .{ path, err, self.fmt(path) }); + } + + test log { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const MyType = struct { + foo: u32, + bar: u32, + }; + + const errors: Errors = .init(arena); + const parsed: MyType = fromSlice(MyType, .{ + .gpa = gpa, + .source = ".{ .foo = 1, .bar = 2 }", + .errors = null, + }) catch |err| b: { + errors.log("input.zon", err); + break :b .{ .foo = 0, .bar = 0 }; + }; + _ = parsed; + } + + /// Log the failure with `std.log.err`, and then terminate the process with exit code 1. + pub fn fatal( + self: *const Errors, + path: []const u8, + err: error{ OutOfMemory, ParseZon }, + ) noreturn { + std.process.fatal("{s}: {}\n{f}", .{ path, err, self.fmt(path) }); + } + + test fatal { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const MyType = struct { + foo: u32, + bar: u32, + }; + + const errors: Errors = .init(arena); + const parsed = fromSlice(MyType, .{ + .gpa = gpa, + .source = ".{ .foo = 1, .bar = 2 }", + .errors = null, + }) catch |err| errors.fatal("input.zon", err); + _ = parsed; + } + + pub fn fmt(self: *const Errors, path: []const u8) Formatter { + return .{ + .path = path, + .errors = self.list.items, + }; + } + + pub const Formatter = struct { + path: []const u8, + errors: []const Error, + + pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { + for (self.errors) |e| { + try w.print("{s}:", .{self.path}); + try w.print("{d}:{d}: error: {s}\n", .{ + e.loc.line + 1, + e.loc.column + 1, + e.msg, }); + + for (e.notes) |note| { + try w.print("{s}:", .{self.path}); + try w.print("{d}:{d}: note: {s}\n", .{ + note.loc.line + 1, + note.loc.column + 1, + note.msg, + }); + } } } - } - - pub fn clear(self: *@This()) void { - self.errors.clearRetainingCapacity(); - } + }; }; pub const FromSliceOptions = struct { @@ -432,7 +503,7 @@ const Parser = struct { const ParseExprError = error{ ParseZon, OutOfMemory }; fn parseExprInto( - self: *@This(), + self: *Parser, node: Zoir.Node.Index, out: anytype, initialized: bool, @@ -519,7 +590,7 @@ const Parser = struct { /// example, `**?**u8` becomes `?u8`, and types that involve user specified type names are just /// referred to by the type of container. fn failExpectedType( - self: @This(), + self: Parser, T: type, node: Zoir.Node.Index, ) error{ ParseZon, OutOfMemory } { @@ -528,7 +599,7 @@ const Parser = struct { } fn failExpectedTypeInner( - self: @This(), + self: Parser, T: type, opt: bool, node: Zoir.Node.Index, @@ -598,7 +669,7 @@ const Parser = struct { } } - fn parseBool(self: @This(), node: Zoir.Node.Index) !bool { + fn parseBool(self: Parser, node: Zoir.Node.Index) !bool { switch (node.get(self.zoir)) { .true => return true, .false => return false, @@ -606,7 +677,7 @@ const Parser = struct { } } - fn parseInt(self: @This(), T: type, node: Zoir.Node.Index) !T { + fn parseInt(self: Parser, T: type, node: Zoir.Node.Index) !T { switch (node.get(self.zoir)) { .int_literal => |int| switch (int) { .small => |val| return std.math.cast(T, val) orelse @@ -623,7 +694,7 @@ const Parser = struct { } } - fn parseFloat(self: @This(), T: type, node: Zoir.Node.Index) !T { + fn parseFloat(self: Parser, T: type, node: Zoir.Node.Index) !T { switch (node.get(self.zoir)) { .int_literal => |int| switch (int) { .small => |val| return @floatFromInt(val), @@ -638,7 +709,7 @@ const Parser = struct { } } - fn parseEnumLiteral(self: @This(), T: type, node: Zoir.Node.Index) !T { + fn parseEnumLiteral(self: Parser, T: type, node: Zoir.Node.Index) !T { switch (node.get(self.zoir)) { .enum_literal => |field_name| { // Create a comptime string map for the enum fields @@ -658,7 +729,7 @@ const Parser = struct { } } - fn parseSlicePointer(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprInnerError!T { + fn parseSlicePointer(self: *Parser, T: type, node: Zoir.Node.Index) ParseExprInnerError!T { switch (node.get(self.zoir)) { .string_literal => return self.parseString(T, node), .array_literal => |nodes| return self.parseSlice(T, nodes), @@ -667,7 +738,7 @@ const Parser = struct { } } - fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprInnerError!T { + fn parseString(self: *Parser, T: type, node: Zoir.Node.Index) ParseExprInnerError!T { const ast_node = node.getAstNode(self.zoir); const pointer = @typeInfo(T).pointer; var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node); @@ -702,7 +773,7 @@ const Parser = struct { } } - fn parseSlice(self: *@This(), T: type, nodes: Zoir.Node.Index.Range) !T { + fn parseSlice(self: *Parser, T: type, nodes: Zoir.Node.Index.Range) !T { const pointer = @typeInfo(T).pointer; // Make sure we're working with a slice @@ -727,7 +798,7 @@ const Parser = struct { return slice; } - fn parseArrayInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { + fn parseArrayInto(self: *Parser, node: Zoir.Node.Index, out: anytype) !void { const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, @@ -761,7 +832,7 @@ const Parser = struct { } } - fn parseVectorInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { + fn parseVectorInto(self: *Parser, node: Zoir.Node.Index, out: anytype) !void { const vector = @typeInfo(@TypeOf(out.*)).vector; var array: [vector.len]vector.child = undefined; try self.parseArrayInto(node, &array); @@ -769,7 +840,7 @@ const Parser = struct { } fn parseStructInto( - self: *@This(), + self: *Parser, node: Zoir.Node.Index, out: anytype, initialized: bool, @@ -843,7 +914,7 @@ const Parser = struct { } } - fn parseTupleInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { + fn parseTupleInto(self: *Parser, node: Zoir.Node.Index, out: anytype) !void { const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, @@ -879,7 +950,7 @@ const Parser = struct { } fn parseUnionInto( - self: *@This(), + self: *Parser, node: Zoir.Node.Index, out: anytype, initialized: bool, @@ -981,7 +1052,7 @@ const Parser = struct { } fn failTokenFmt( - self: @This(), + self: Parser, token: Ast.TokenIndex, offset: u32, comptime fmt: []const u8, @@ -992,7 +1063,7 @@ const Parser = struct { } fn failTokenFmtNote( - self: @This(), + self: Parser, token: Ast.TokenIndex, offset: u32, comptime fmt: []const u8, @@ -1011,7 +1082,7 @@ const Parser = struct { } fn failNodeFmt( - self: @This(), + self: Parser, node: Zoir.Node.Index, comptime fmt: []const u8, args: anytype, @@ -1022,7 +1093,7 @@ const Parser = struct { } fn failToken( - self: @This(), + self: Parser, failure: Error, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); @@ -1031,7 +1102,7 @@ const Parser = struct { } fn failNode( - self: @This(), + self: Parser, node: Zoir.Node.Index, msg: []const u8, ) error{ OutOfMemory, ParseZon } { @@ -1046,7 +1117,7 @@ const Parser = struct { } fn failCannotRepresent( - self: @This(), + self: Parser, T: type, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon } { @@ -1055,7 +1126,7 @@ const Parser = struct { } fn failUnexpected( - self: @This(), + self: Parser, T: type, item_kind: []const u8, node: Zoir.Node.Index, @@ -1111,7 +1182,7 @@ const Parser = struct { // the value matched, but doing so doesn't seem to support any real use cases // so isn't worth the complexity at the moment. fn failComptimeField( - self: @This(), + self: Parser, node: Zoir.Node.Index, field: usize, ) error{ OutOfMemory, ParseZon } { @@ -1306,7 +1377,10 @@ test "std.zon ast errors" { .errors = &errors, }), ); - try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:13: error: expected ',' after initializer + \\ + , "{f}", .{errors.fmt("input.zon")}); } test "std.zon comments" { @@ -1336,11 +1410,7 @@ test "std.zon comments" { , .errors = &errors, })); - try std.testing.expectFmt( - "1:1: error: expected expression, found 'a document comment'\n", - "{f}", - .{errors}, - ); + try std.testing.expectFmt("input.zon:1:1: error: expected expression, found 'a document comment'\n", "{f}", .{errors.fmt("input.zon")}); } } @@ -1356,7 +1426,9 @@ test "std.zon failure/oom formatting" { .source = "\"foo\"", .errors = &errors, })); - try std.testing.expectFmt("", "{f}", .{errors}); + try std.testing.expectFmt( + \\ + , "{f}", .{errors.fmt("input.zon")}); } test "std.zon fromSliceAlloc syntax error" { @@ -1497,13 +1569,10 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( - \\1:4: error: unexpected field 'z' - \\1:4: note: supported: 'x', 'y' + \\input.zon:1:4: error: unexpected field 'z' + \\input.zon:1:4: note: supported: 'x', 'y' \\ - , - "{f}", - .{errors}, - ); + , "{f}", .{errors.fmt("input.zon")}); } // Explicit void field @@ -1519,7 +1588,10 @@ test "std.zon unions" { .errors = &errors, }), ); - try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:6: error: expected type 'void' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Extra field @@ -1535,7 +1607,10 @@ test "std.zon unions" { .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:2: error: expected union + \\ + , "{f}", .{errors.fmt("input.zon")}); } // No fields @@ -1551,7 +1626,10 @@ test "std.zon unions" { .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:2: error: expected union + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Enum literals cannot coerce into untagged unions @@ -1564,7 +1642,10 @@ test "std.zon unions" { .source = ".x", .errors = &errors, })); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:2: error: expected union + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Unknown field for enum literal coercion @@ -1578,13 +1659,10 @@ test "std.zon unions" { .errors = &errors, })); try std.testing.expectFmt( - \\1:2: error: unexpected field 'y' - \\1:2: note: supported: 'x' + \\input.zon:1:2: error: unexpected field 'y' + \\input.zon:1:2: note: supported: 'x' \\ - , - "{f}", - .{errors}, - ); + , "{f}", .{errors.fmt("input.zon")}); } // Non void field for enum literal coercion @@ -1597,7 +1675,10 @@ test "std.zon unions" { .source = ".x", .errors = &errors, })); - try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:2: error: expected union + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -1669,13 +1750,10 @@ test "std.zon structs" { }), ); try std.testing.expectFmt( - \\1:12: error: unexpected field 'z' - \\1:12: note: supported: 'x', 'y' + \\input.zon:1:12: error: unexpected field 'z' + \\input.zon:1:12: note: supported: 'x', 'y' \\ - , - "{f}", - .{errors}, - ); + , "{f}", .{errors.fmt("input.zon")}); } // Duplicate field @@ -1691,10 +1769,10 @@ test "std.zon structs" { }), ); try std.testing.expectFmt( - \\1:4: error: duplicate struct field name - \\1:12: note: duplicate name here + \\input.zon:1:4: error: duplicate struct field name + \\input.zon:1:12: note: duplicate name here \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Ignore unknown fields @@ -1722,10 +1800,10 @@ test "std.zon structs" { }), ); try std.testing.expectFmt( - \\1:4: error: unexpected field 'x' - \\1:4: note: none expected + \\input.zon:1:4: error: unexpected field 'x' + \\input.zon:1:4: note: none expected \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Missing field @@ -1740,7 +1818,10 @@ test "std.zon structs" { .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:2: error: missing required field y + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Default field @@ -1776,9 +1857,9 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\1:18: error: cannot initialize comptime field + \\input.zon:1:18: error: cannot initialize comptime field \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Enum field (regression test, we were previously getting the field name in an @@ -1816,10 +1897,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\1:1: error: types are not available in ZON - \\1:1: note: replace the type with '.' + \\input.zon:1:1: error: types are not available in ZON + \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Arrays @@ -1832,10 +1913,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\1:1: error: types are not available in ZON - \\1:1: note: replace the type with '.' + \\input.zon:1:1: error: types are not available in ZON + \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Slices @@ -1849,10 +1930,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\1:1: error: types are not available in ZON - \\1:1: note: replace the type with '.' + \\input.zon:1:1: error: types are not available in ZON + \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Tuples @@ -1865,10 +1946,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\1:1: error: types are not available in ZON - \\1:1: note: replace the type with '.' + \\input.zon:1:1: error: types are not available in ZON + \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Nested @@ -1881,10 +1962,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\1:9: error: types are not available in ZON - \\1:9: note: replace the type with '.' + \\input.zon:1:9: error: types are not available in ZON + \\input.zon:1:9: note: replace the type with '.' \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } } } @@ -1955,7 +2036,10 @@ test "std.zon tuples" { .errors = &errors, }), ); - try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:14: error: index 2 outside of tuple length 2 + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Extra field @@ -1971,10 +2055,9 @@ test "std.zon tuples" { }), ); try std.testing.expectFmt( - "1:2: error: missing tuple field with index 1\n", - "{f}", - .{errors}, - ); + \\input.zon:1:2: error: missing tuple field with index 1 + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Tuple with unexpected field names @@ -1989,7 +2072,7 @@ test "std.zon tuples" { .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{errors}); + try std.testing.expectFmt("input.zon:1:2: error: expected tuple\n", "{f}", .{errors.fmt("input.zon")}); } // Struct with missing field names @@ -2004,7 +2087,10 @@ test "std.zon tuples" { .errors = &errors, }), ); - try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:2: error: expected struct + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Comptime field @@ -2029,14 +2115,16 @@ test "std.zon tuples" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\1:9: error: cannot initialize comptime field + \\input.zon:1:9: error: cannot initialize comptime field \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } } // Test sizes 0 to 3 since small sizes get parsed differently test "std.zon arrays and slices" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/20881 + const gpa = std.testing.allocator; var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); @@ -2210,10 +2298,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - "1:3: error: index 0 outside of array of length 0\n", - "{f}", - .{errors}, - ); + \\input.zon:1:3: error: index 0 outside of array of length 0 + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Expect 1 find 2 @@ -2228,10 +2315,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - "1:8: error: index 1 outside of array of length 1\n", - "{f}", - .{errors}, - ); + \\input.zon:1:8: error: index 1 outside of array of length 1 + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Expect 2 find 1 @@ -2246,10 +2332,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - "1:2: error: expected 2 array elements; found 1\n", - "{f}", - .{errors}, - ); + \\input.zon:1:2: error: expected 2 array elements; found 1 + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Expect 3 find 0 @@ -2263,11 +2348,7 @@ test "std.zon arrays and slices" { .errors = &errors, }), ); - try std.testing.expectFmt( - "1:2: error: expected 3 array elements; found 0\n", - "{f}", - .{errors}, - ); + try std.testing.expectFmt("input.zon:1:2: error: expected 3 array elements; found 0\n", "{f}", .{errors.fmt("input.zon")}); } // Wrong inner type @@ -2283,7 +2364,10 @@ test "std.zon arrays and slices" { .errors = &errors, }), ); - try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:3: error: expected type 'bool' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Slice @@ -2298,7 +2382,7 @@ test "std.zon arrays and slices" { .errors = &errors, }), ); - try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{errors}); + try std.testing.expectFmt("input.zon:1:3: error: expected type 'bool'\n", "{f}", .{errors.fmt("input.zon")}); } } @@ -2315,7 +2399,10 @@ test "std.zon arrays and slices" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Slice @@ -2325,7 +2412,7 @@ test "std.zon arrays and slices" { error.ParseZon, fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); } } @@ -2337,10 +2424,9 @@ test "std.zon arrays and slices" { fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .errors = &errors }), ); try std.testing.expectFmt( - "1:3: error: pointers are not available in ZON\n", - "{f}", - .{errors}, - ); + \\input.zon:1:3: error: pointers are not available in ZON + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2391,7 +2477,7 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); } { @@ -2400,7 +2486,10 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2420,7 +2509,7 @@ test "std.zon string literal" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); } { @@ -2433,7 +2522,10 @@ test "std.zon string literal" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2470,7 +2562,7 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); } { @@ -2479,7 +2571,10 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2490,7 +2585,10 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected string + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Expecting string literal, getting an incompatible tuple @@ -2500,7 +2598,10 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .errors = &errors }), ); - try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:3: error: expected type 'u8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Invalid string literal @@ -2510,7 +2611,10 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .errors = &errors }), ); - try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:3: error: invalid escape character: 'a' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Slice wrong child type @@ -2521,7 +2625,10 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -2530,7 +2637,10 @@ test "std.zon string literal" { error.ParseZon, fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .errors = &errors }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2547,7 +2657,7 @@ test "std.zon string literal" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); } { @@ -2561,7 +2671,10 @@ test "std.zon string literal" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2657,13 +2770,10 @@ test "std.zon enum literals" { }), ); try std.testing.expectFmt( - \\1:2: error: unexpected enum literal 'qux' - \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' + \\input.zon:1:2: error: unexpected enum literal 'qux' + \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , - "{f}", - .{errors}, - ); + , "{f}", .{errors.fmt("input.zon")}); } // Bad tag that's too long for parser @@ -2678,13 +2788,10 @@ test "std.zon enum literals" { }), ); try std.testing.expectFmt( - \\1:2: error: unexpected enum literal 'foobarbaz' - \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' + \\input.zon:1:2: error: unexpected enum literal 'foobarbaz' + \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , - "{f}", - .{errors}, - ); + , "{f}", .{errors.fmt("input.zon")}); } // Bad type @@ -2698,7 +2805,10 @@ test "std.zon enum literals" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected enum literal + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Test embedded nulls in an identifier @@ -2713,10 +2823,9 @@ test "std.zon enum literals" { }), ); try std.testing.expectFmt( - "1:2: error: identifier cannot contain null bytes\n", - "{f}", - .{errors}, - ); + \\input.zon:1:2: error: identifier cannot contain null bytes + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2750,11 +2859,11 @@ test "std.zon parse bool" { }), ); try std.testing.expectFmt( - \\1:2: error: invalid expression - \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' - \\1:2: note: precede identifier with '.' for an enum literal + \\input.zon:1:2: error: invalid expression + \\input.zon:1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' + \\input.zon:1:2: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } { var errors: Errors = .init(arena); @@ -2763,7 +2872,10 @@ test "std.zon parse bool" { .source = "123", .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type 'bool' + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2892,10 +3004,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: type 'i66' cannot represent value\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: type 'i66' cannot represent value + \\ + , "{f}", .{errors.fmt("input.zon")}); } { var errors: Errors = .init(arena); @@ -2905,10 +3016,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: type 'i66' cannot represent value\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: type 'i66' cannot represent value + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Test parsing whole number floats as integers @@ -3011,10 +3121,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:3: error: invalid digit 'a' for decimal base\n", - "{f}", - .{errors}, - ); + \\input.zon:1:3: error: invalid digit 'a' for decimal base + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Failing to parse as int @@ -3025,7 +3134,10 @@ test "std.zon parse int" { .source = "true", .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type 'u8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Failing because an int is out of range @@ -3037,10 +3149,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: type 'u8' cannot represent value\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: type 'u8' cannot represent value + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Failing because a negative int is out of range @@ -3052,10 +3163,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: type 'i8' cannot represent value\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: type 'i8' cannot represent value + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Failing because an unsigned int is negative @@ -3067,10 +3177,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: type 'u8' cannot represent value\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: type 'u8' cannot represent value + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Failing because a float is non-whole @@ -3082,10 +3191,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: type 'u8' cannot represent value\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: type 'u8' cannot represent value + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Failing because a float is negative @@ -3097,10 +3205,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: type 'u8' cannot represent value\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: type 'u8' cannot represent value + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Negative integer zero @@ -3112,11 +3219,11 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\1:2: error: integer literal '-0' is ambiguous - \\1:2: note: use '0' for an integer zero - \\1:2: note: use '-0.0' for a floating-point signed zero + \\input.zon:1:2: error: integer literal '-0' is ambiguous + \\input.zon:1:2: note: use '0' for an integer zero + \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Negative integer zero casted to float @@ -3128,11 +3235,11 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\1:2: error: integer literal '-0' is ambiguous - \\1:2: note: use '0' for an integer zero - \\1:2: note: use '-0.0' for a floating-point signed zero + \\input.zon:1:2: error: integer literal '-0' is ambiguous + \\input.zon:1:2: note: use '0' for an integer zero + \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } // Negative float 0 is allowed @@ -3157,11 +3264,7 @@ test "std.zon parse int" { .source = "--2", .errors = &errors, })); - try std.testing.expectFmt( - "1:1: error: expected number or 'inf' after '-'\n", - "{f}", - .{errors}, - ); + try std.testing.expectFmt("input.zon:1:1: error: expected number or 'inf' after '-'\n", "{f}", .{errors.fmt("input.zon")}); } { @@ -3175,10 +3278,9 @@ test "std.zon parse int" { }), ); try std.testing.expectFmt( - "1:1: error: expected number or 'inf' after '-'\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: expected number or 'inf' after '-' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Invalid int literal @@ -3189,7 +3291,10 @@ test "std.zon parse int" { .source = "0xg", .errors = &errors, })); - try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:3: error: invalid digit 'g' for hex base + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Notes on invalid int literal @@ -3201,10 +3306,10 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\1:1: error: number '0123' has leading zero - \\1:1: note: use '0o' prefix for octal literals + \\input.zon:1:1: error: number '0123' has leading zero + \\input.zon:1:1: note: use '0o' prefix for octal literals \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -3222,10 +3327,9 @@ test "std.zon negative char" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: expected number or 'inf' after '-'\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: expected number or 'inf' after '-' + \\ + , "{f}", .{errors.fmt("input.zon")}); } { var errors: Errors = .init(arena); @@ -3235,14 +3339,15 @@ test "std.zon negative char" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: expected number or 'inf' after '-'\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: expected number or 'inf' after '-' + \\ + , "{f}", .{errors.fmt("input.zon")}); } } test "std.zon parse float" { + if (builtin.cpu.arch == .x86) return error.SkipZigTest; + const gpa = std.testing.allocator; var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); @@ -3399,11 +3504,7 @@ test "std.zon parse float" { .source = "-nan", .errors = &errors, })); - try std.testing.expectFmt( - "1:1: error: expected number or 'inf' after '-'\n", - "{f}", - .{errors}, - ); + try std.testing.expectFmt("input.zon:1:1: error: expected number or 'inf' after '-'\n", "{f}", .{errors.fmt("input.zon")}); } // nan as int not allowed @@ -3414,7 +3515,10 @@ test "std.zon parse float" { .source = "nan", .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type 'i8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // nan as int not allowed @@ -3425,7 +3529,10 @@ test "std.zon parse float" { .source = "nan", .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type 'i8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // inf as int not allowed @@ -3436,7 +3543,10 @@ test "std.zon parse float" { .source = "inf", .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type 'i8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // -inf as int not allowed @@ -3447,7 +3557,10 @@ test "std.zon parse float" { .source = "-inf", .errors = &errors, })); - try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type 'i8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Bad identifier as float @@ -3459,11 +3572,11 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\1:1: error: invalid expression - \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' - \\1:1: note: precede identifier with '.' for an enum literal + \\input.zon:1:1: error: invalid expression + \\input.zon:1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' + \\input.zon:1:1: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -3474,10 +3587,9 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - "1:1: error: expected number or 'inf' after '-'\n", - "{f}", - .{errors}, - ); + \\input.zon:1:1: error: expected number or 'inf' after '-' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Non float as float @@ -3491,7 +3603,10 @@ test "std.zon parse float" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type 'f32' + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -3612,7 +3727,7 @@ test "std.zon free on error" { } test "std.zon vector" { - const builtin = @import("builtin"); + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/15330 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .s390x) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25957 const gpa = std.testing.allocator; @@ -3722,10 +3837,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - "1:2: error: expected 2 array elements; found 1\n", - "{f}", - .{errors}, - ); + \\input.zon:1:2: error: expected 2 array elements; found 1 + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Too many fields @@ -3740,10 +3854,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - "1:13: error: index 2 outside of array of length 2\n", - "{f}", - .{errors}, - ); + \\input.zon:1:13: error: index 2 outside of array of length 2 + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Wrong type fields @@ -3758,10 +3871,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - "1:8: error: expected type 'f32'\n", - "{f}", - .{errors}, - ); + \\input.zon:1:8: error: expected type 'f32' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Wrong type @@ -3775,7 +3887,10 @@ test "std.zon vector" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type '@Vector(3, u8)' + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Elements should get freed on error @@ -3790,7 +3905,10 @@ test "std.zon vector" { .errors = &errors, }), ); - try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:6: error: expected type 'u8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -3981,7 +4099,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type '?u8' + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -3995,7 +4116,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type '?f32' + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4009,7 +4133,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type '?@Vector(3, u8)' + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4023,7 +4150,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected type '?bool' + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4037,7 +4167,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional struct + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4051,7 +4184,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional tuple + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4065,7 +4201,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional union + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4079,7 +4218,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional array + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4093,7 +4235,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional array + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4107,7 +4252,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional array + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4121,7 +4269,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional array + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4135,7 +4286,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional string + \\ + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4149,7 +4303,10 @@ test "std.zon add pointers" { .errors = &errors, }), ); - try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:1: error: expected optional enum literal + \\ + , "{f}", .{errors.fmt("input.zon")}); } } @@ -4437,7 +4594,10 @@ test "std.zon update optionals" { .source = ".{ .foo = .{} }", .errors = &errors, })); - try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:12: error: missing required field qux + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Updating an optional that starts out non-null should preserve any values we leave off. It's @@ -4557,7 +4717,10 @@ test "std.zon update unions" { .source = ".{ .foo = .{} }", .errors = &errors, })); - try std.testing.expectFmt("1:12: error: missing required field qux\n", "{f}", .{errors}); + try std.testing.expectFmt( + \\input.zon:1:12: error: missing required field qux + \\ + , "{f}", .{errors.fmt("input.zon")}); } // Updating a union should preseve any sub-fields that we left off. It's also okay to leave off -- 2.54.0 From a6552e7d009883ae33a1b6aa24d216df6c964f17 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Fri, 7 Aug 2026 17:50:51 -0700 Subject: [PATCH 15/26] Cleans up memory management and error reporting - Use the same arena for errors and results - Store errors as a slice rather than an array list - Make the errors container non-optional - Rename functions like `fromSliceAlloc`/`fromSlice` to `fromSlice`/`fromSliceNoAlloc` - Cleans up error formatting and internal helper structure --- lib/std/zon/parse.zig | 1912 ++++++++++++++++++++++------------------- 1 file changed, 1036 insertions(+), 876 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 33c747291f3cf91f4d74803f46deb20914bd2466..7636040cdd64055e99f34e4470fd707fde60312f 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -1,7 +1,7 @@ -//! Use `fromSlice`/`fromSliceAlloc` to parse a ZON string into a Zig value. Human readable errors +//! Use `fromSlice`/`fromSlice` to parse a ZON string into a Zig value. Human readable errors //! are written to an out parameter. //! -//! If your data is already available in tree form, consider `fromZoir`/`fromZoirAlloc`. +//! If your data is already available in tree form, consider `fromZoir`/`fromZoir`. //! //! To parse into an existing value, see the `updateFrom*` variants. For lower level control over //! parsing, see `std.zig.ZonGen`. For importing ZON at compile time, use `@import`. @@ -93,15 +93,9 @@ pub const Error = struct { /// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user. pub const Errors = struct { - arena: Allocator, - list: std.ArrayList(Error), + items: []const Error, - pub fn init(arena: Allocator) Errors { - return .{ - .arena = arena, - .list = .empty, - }; - } + pub const empty: Errors = .{ .items = &.{} }; /// Log the failure with `std.log.err`. pub fn log( @@ -109,7 +103,7 @@ pub const Errors = struct { path: []const u8, err: error{ OutOfMemory, ParseZon }, ) void { - std.log.err("{s}: {}\n{f}", .{ path, err, self.fmt(path) }); + std.log.err("{f}", .{self.fmt(path, err)}); } test log { @@ -117,17 +111,18 @@ pub const Errors = struct { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const MyType = struct { foo: u32, bar: u32, }; - const errors: Errors = .init(arena); - const parsed: MyType = fromSlice(MyType, .{ + const parsed: MyType = fromSliceNoAlloc(MyType, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", - .errors = null, + .errors = &errors, }) catch |err| b: { errors.log("input.zon", err); break :b .{ .foo = 0, .bar = 0 }; @@ -141,7 +136,7 @@ pub const Errors = struct { path: []const u8, err: error{ OutOfMemory, ParseZon }, ) noreturn { - std.process.fatal("{s}: {}\n{f}", .{ path, err, self.fmt(path) }); + std.process.fatal("{f}", .{self.fmt(path, err)}); } test fatal { @@ -149,34 +144,42 @@ pub const Errors = struct { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const MyType = struct { foo: u32, bar: u32, }; - const errors: Errors = .init(arena); - const parsed = fromSlice(MyType, .{ + const parsed = fromSliceNoAlloc(MyType, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", - .errors = null, + .errors = &errors, }) catch |err| errors.fatal("input.zon", err); _ = parsed; } - pub fn fmt(self: *const Errors, path: []const u8) Formatter { + pub fn fmt( + self: *const Errors, + path: []const u8, + err: error{ OutOfMemory, ParseZon }, + ) Formatter { return .{ + .items = self.items, .path = path, - .errors = self.list.items, + .err = err, }; } pub const Formatter = struct { path: []const u8, - errors: []const Error, + items: []const Error, + err: error{ OutOfMemory, ParseZon }, pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - for (self.errors) |e| { + try w.print("{s}: {}\n", .{ self.path, self.err }); + for (self.items) |e| { try w.print("{s}:", .{self.path}); try w.print("{d}:{d}: error: {s}\n", .{ e.loc.line + 1, @@ -200,263 +203,181 @@ pub const Errors = struct { pub const FromSliceOptions = struct { /// Used for scratch allocations. gpa: Allocator, + /// Used for allocating errors and results. + arena: Allocator, /// The ZON source to parse. source: [:0]const u8, - /// If non-null, any errors encountered are appended here. - errors: ?*Errors, + /// When an error is returned from the parser, a human readable error description of the failure + /// is stored here. + errors: *Errors, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; /// Parses the given slice as ZON. /// -/// Returns `error.OutOfMemory` on allocation failure, or `error.ParseZon` error if the ZON is -/// invalid or can not be deserialized into type `T`. -/// -/// When the parser returns `error.ParseZon`, it will also store a human readable explanation in -/// `errors` if non null. -/// -/// Asserts at compile time that the result type doesn't contain pointers. As such, the result -/// doesn't need to be freed. -/// -/// An allocator is still required for temporary allocations made during parsing. +/// Human readable errors are written to `options.errors`. `options.arena` owns the errors, and any +/// pointers that are part of `T`. pub fn fromSlice(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { + var value: T = undefined; + try fromSliceInner(T, &value, false, options); + return value; +} + +/// Like `fromSlice` but the result type may not contain pointers, allowing it to outlive +/// `options.arena`. +pub fn fromSliceNoAlloc(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { comptime assert(!requiresAllocator(T)); - return fromSliceAlloc(T, .{ - .gpa = options.gpa, - .arena = .failing, - .source = options.source, - .errors = options.errors, - .ignore_unknown_fields = options.ignore_unknown_fields, - }); + return fromSlice(T, options); } -/// Similar to `fromSlice`, but updates `value` in place which is assumed to hold a valid value. Any -/// unspecified fields are left unchanged. +/// Similar to `fromSlice`, but updates `value` in place. `value` must be initialized before this +/// call, fields not specified in ZON are left unchanged. /// -/// Untagged unions and slices are replaced in their entiredy. If an error occurs while parsing, -/// `value` remains valid but may be partially updated. +/// Whenever a slice or an untagged union is specified, it completely overwites the previous value. +/// On error, `value` remains valid, but may be partially updated. pub fn updateFromSlice( T: type, value: *T, options: FromSliceOptions, +) error{ OutOfMemory, ParseZon }!void { + try fromSliceInner(T, value, true, options); +} + +/// Like `updateFromSlice` but the `T` may not contain pointers, allowing `value` to outlive +/// `options.arena`. +pub fn updateFromSliceNoAlloc( + T: type, + value: *T, + options: FromSliceOptions, ) error{ OutOfMemory, ParseZon }!void { comptime assert(!requiresAllocator(T)); - try updateFromSliceAlloc(T, value, .{ - .gpa = options.gpa, - .arena = .failing, - .source = options.source, - .errors = options.errors, - .ignore_unknown_fields = options.ignore_unknown_fields, - }); + try fromSliceInner(T, value, true, options); } -pub const FromSliceAllocOptions = struct { - /// Used for scratch allocations. - gpa: Allocator, - /// Used for any allocations required by the out type. - arena: Allocator, - /// The ZON source to parse. - source: [:0]const u8, - /// If non-null, any errors encountered are appended here. - errors: ?*Errors, - /// If true, unknown fields do not error. - ignore_unknown_fields: bool = false, -}; - -/// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see -/// `free`. -pub fn fromSliceAlloc(T: type, options: FromSliceAllocOptions) error{ OutOfMemory, ParseZon }!T { - var value: T = undefined; - try parseSliceAlloc(T, &value, .{ - .gpa = options.gpa, - .arena = options.arena, - .source = options.source, - .errors = options.errors, - .ignore_unknown_fields = options.ignore_unknown_fields, - .initialized = false, - }); - return value; -} - -/// Like updateFromSlice`, but the result may contain pointers. -pub fn updateFromSliceAlloc( +fn fromSliceInner( T: type, value: *T, - options: FromSliceAllocOptions, -) error{ OutOfMemory, ParseZon }!void { - try parseSliceAlloc(T, value, .{ - .gpa = options.gpa, - .arena = options.arena, - .source = options.source, - .errors = options.errors, - .ignore_unknown_fields = options.ignore_unknown_fields, - .initialized = true, - }); -} - -const ParseSliceAllocOptions = struct { - gpa: Allocator, - arena: Allocator, - source: [:0]const u8, - errors: ?*Errors, - ignore_unknown_fields: bool, initialized: bool, -}; - -fn parseSliceAlloc( - T: type, - value: *T, - options: ParseSliceAllocOptions, + options: FromSliceOptions, ) error{ OutOfMemory, ParseZon }!void { + var errors: std.ArrayList(Error) = .empty; + defer options.errors.items = errors.items; + var ast = try std.zig.Ast.parse(options.gpa, options.source, .{ .mode = .zon }); defer ast.deinit(options.gpa); var zoir = try ZonGen.generate(options.gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(options.gpa); - try parseZoirAlloc(T, value, .{ + try inner(T, value, initialized, .{ .arena = options.arena, .ast = &ast, .zoir = &zoir, .node = .root, - .errors = options.errors, + .errors = &errors, .ignore_unknown_fields = options.ignore_unknown_fields, - .initialized = options.initialized, }); } pub const FromZoirOptions = struct { - /// The AST for this this ZON value. + /// Used for allocating errors and results. + arena: Allocator, + /// The AST for this ZON value. ast: *const Ast, /// The zig object intermediate representation of this ZON value. zoir: *const Zoir, /// The node to start parsing at. node: Zoir.Node.Index = .root, - /// If non-null, any errors encountered are appended here. - errors: ?*Errors, + /// When an error is returned from the parser, a human readable error description of the failure + /// is stored here. + errors: *Errors, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. -pub fn fromZoir(T: type, options: FromZoirOptions) error{ParseZon}!T { +pub fn fromZoir(T: type, options: FromZoirOptions) error{ OutOfMemory, ParseZon }!T { + var value: T = undefined; + try fromZoirInner(T, &value, false, options); + return value; +} + +/// Like `fromSliceNoAlloc`, but operates on `Zoir` instead of ZON source. +pub fn fromZoirNoAlloc(T: type, options: FromZoirOptions) error{ OutOfMemory, ParseZon }!T { comptime assert(!requiresAllocator(T)); - return fromZoirAlloc(T, .{ - .arena = .failing, - .ast = options.ast, - .zoir = options.zoir, - .node = options.node, - .errors = options.errors, - .ignore_unknown_fields = options.ignore_unknown_fields, - }) catch |err| switch (err) { - error.OutOfMemory => unreachable, // Checked by comptime assertion above - else => |e| return e, - }; + return fromZoir(T, options); } -/// Like `updateSlice`, but operates on `Zoir` instead of ZON source. +/// Like `updateFromSlice`, but operates on `Zoir` instead of ZON source. pub fn updateFromZoir( T: type, value: *T, options: FromZoirOptions, -) error{ParseZon}!void { +) error{ OutOfMemory, ParseZon }!void { + return fromZoirInner(T, value, true, options); +} + +/// Like `updateFromSliceNoAlloc`, but operates on `Zoir` instead of ZON source. +pub fn updateFromZoirNoAlloc( + T: type, + value: *T, + options: FromZoirOptions, +) error{ OutOfMemory, ParseZon }!void { comptime assert(!requiresAllocator(T)); - updateFromZoirAlloc(T, value, .{ - .arena = .failing, + return updateFromZoir(T, value, options); +} + +fn fromZoirInner( + T: type, + value: *T, + initialized: bool, + options: FromZoirOptions, +) error{ OutOfMemory, ParseZon }!void { + var errors: std.ArrayList(Error) = .empty; + defer options.errors.items = errors.items; + return inner(T, value, initialized, .{ + .arena = options.arena, .ast = options.ast, .zoir = options.zoir, - .node = options.node, - .errors = options.errors, + .errors = &errors, .ignore_unknown_fields = options.ignore_unknown_fields, - }) catch |err| switch (err) { - error.OutOfMemory => unreachable, // Checked by comptime assertion above - else => |e| return e, - }; + }); } -pub const FromZoirAllocOptions = struct { - /// Used for any allocations required by the out type. +const InnerOptions = struct { arena: Allocator, - /// The AST for this ZON value. ast: *const Ast, - /// The zig object intermediate representation of this ZON value. zoir: *const Zoir, - /// The node to start parsing at. node: Zoir.Node.Index = .root, - /// If non-null, any errors encountered are appended here. - errors: ?*Errors, - /// If true, unknown fields do not error. - ignore_unknown_fields: bool = false, -}; - -/// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source. -pub fn fromZoirAlloc(T: type, options: FromZoirAllocOptions) error{ OutOfMemory, ParseZon }!T { - var value: T = undefined; - try parseZoirAlloc(T, &value, .{ - .arena = options.arena, - .ast = options.ast, - .zoir = options.zoir, - .node = options.node, - .errors = options.errors, - .ignore_unknown_fields = options.ignore_unknown_fields, - .initialized = false, - }); - return value; -} - -/// Like updateSliceAlloc`, but operates on `Zoir` instead of ZON source. -pub fn updateFromZoirAlloc( - T: type, - value: *T, - options: FromZoirAllocOptions, -) error{ OutOfMemory, ParseZon }!void { - return parseZoirAlloc(T, value, .{ - .arena = options.arena, - .ast = options.ast, - .zoir = options.zoir, - .node = options.node, - .errors = options.errors, - .ignore_unknown_fields = options.ignore_unknown_fields, - .initialized = true, - }); -} - -const ParseZoirAllocOptions = struct { - arena: Allocator, - ast: *const Ast, - zoir: *const Zoir, - node: Zoir.Node.Index, - errors: ?*Errors, + errors: *std.ArrayList(Error), ignore_unknown_fields: bool, - initialized: bool, }; -fn parseZoirAlloc( +fn inner( T: type, value: *T, - options: ParseZoirAllocOptions, + initialized: bool, + options: InnerOptions, ) error{ OutOfMemory, ParseZon }!void { comptime assert(canParseType(T)); if (options.zoir.hasCompileErrors()) { - if (options.errors) |errors| { - for (options.zoir.compile_errors) |e| { - var notes: std.ArrayList(Error.Note) = .empty; - for (e.getNotes(options.zoir)) |note| { - try notes.append(errors.arena, .init(options.ast, .{ - .msg = try errors.arena.dupe(u8, note.msg.get(options.zoir)), - .token = note.token, - .node_or_offset = note.node_or_offset, - })); - } - try errors.list.append(errors.arena, .init(options.ast, .{ - .msg = try errors.arena.dupe(u8, e.msg.get(options.zoir)), - .token = e.token, - .node_or_offset = e.node_or_offset, - .notes = try notes.toOwnedSlice(errors.arena), + for (options.zoir.compile_errors) |e| { + var notes: std.ArrayList(Error.Note) = .empty; + for (e.getNotes(options.zoir)) |note| { + try notes.append(options.arena, .init(options.ast, .{ + .msg = try options.arena.dupe(u8, note.msg.get(options.zoir)), + .token = note.token, + .node_or_offset = note.node_or_offset, })); } + try options.errors.append(options.arena, .init(options.ast, .{ + .msg = try options.arena.dupe(u8, e.msg.get(options.zoir)), + .token = e.token, + .node_or_offset = e.node_or_offset, + .notes = notes.items, + })); } return error.ParseZon; } @@ -469,7 +390,7 @@ fn parseZoirAlloc( .errors = options.errors, }; - try parser.parseExprInto(options.node, value, options.initialized); + try parser.parseExprInto(options.node, value, initialized); } fn requiresAllocator(T: type) bool { @@ -497,7 +418,7 @@ const Parser = struct { arena: Allocator, ast: *const Ast, zoir: *const Zoir, - errors: ?*Errors, + errors: *std.ArrayList(Error), ignore_unknown_fields: bool, const ParseExprError = error{ ParseZon, OutOfMemory }; @@ -508,7 +429,7 @@ const Parser = struct { out: anytype, initialized: bool, ) ParseExprError!void { - return self.parseExprInnerInto(node, out, initialized) catch |err| switch (err) { + return self.parseExprIntoInner(node, out, initialized) catch |err| switch (err) { error.WrongType => return self.failExpectedType(@TypeOf(out.*), node), else => |e| return e, }; @@ -516,7 +437,7 @@ const Parser = struct { const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType }; - fn parseExprInnerInto( + fn parseExprIntoInner( self: *@This(), node: Zoir.Node.Index, out: anytype, @@ -531,10 +452,10 @@ const Parser = struct { .optional => |optional| if (node.get(self.zoir) == .null) { out.* = null; } else if (initialized and out.* != null) { - try self.parseExprInnerInto(node, &out.*.?, true); + try self.parseExprIntoInner(node, &out.*.?, true); } else { var some: optional.child = undefined; - try self.parseExprInnerInto(node, &some, false); + try self.parseExprIntoInner(node, &some, false); out.* = some; }, .bool => out.* = try self.parseBool(node), @@ -556,7 +477,7 @@ const Parser = struct { if (initialized) new.* = out.*.*; break :b new; }; - try self.parseExprInnerInto( + try self.parseExprIntoInner( node, new, initialized, @@ -1072,10 +993,10 @@ const Parser = struct { ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); comptime assert(args.len > 0); - if (self.errors) |errors| try errors.list.append(errors.arena, .init(self.ast, .{ + try self.errors.append(self.arena, .init(self.ast, .{ .token = .fromToken(token), .node_or_offset = offset, - .msg = try std.fmt.allocPrint(errors.arena, fmt, args), + .msg = try std.fmt.allocPrint(self.arena, fmt, args), .notes = notes, })); return error.ParseZon; @@ -1097,7 +1018,7 @@ const Parser = struct { failure: Error, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - if (self.errors) |errors| try errors.list.append(errors.arena, failure); + try self.errors.append(self.arena, failure); return error.ParseZon; } @@ -1134,7 +1055,6 @@ const Parser = struct { name: []const u8, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - const errors = self.errors orelse return error.ParseZon; const token = if (field) |f| b: { var buf: [2]Ast.Node.Index = undefined; const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?; @@ -1145,13 +1065,13 @@ const Parser = struct { inline .@"struct", .@"union", .@"enum" => |info| { var notes: std.ArrayList(Error.Note) = .empty; if (info.field_names.len == 0) { - try notes.append(errors.arena, .init(self.ast, .{ + try notes.append(self.arena, .init(self.ast, .{ .token = .fromToken(token), .node_or_offset = 0, .msg = "none expected", })); } else { - var aw: std.Io.Writer.Allocating = .init(errors.arena); + var aw: std.Io.Writer.Allocating = .init(self.arena); aw.writer.writeAll("supported: ") catch return error.OutOfMemory; inline for (info.field_names, 0..) |field_name, i| { if (i != 0) aw.writer.writeAll(", ") catch return error.OutOfMemory; @@ -1160,7 +1080,7 @@ const Parser = struct { .allow_underscore = true, })}) catch return error.OutOfMemory; } - try notes.append(errors.arena, .init(self.ast, .{ + try notes.append(self.arena, .init(self.ast, .{ .token = .fromToken(token), .node_or_offset = 0, .msg = try aw.toOwnedSlice(), @@ -1171,7 +1091,7 @@ const Parser = struct { 0, "unexpected {s} '{s}'", .{ item_kind, name }, - try notes.toOwnedSlice(errors.arena), + notes.items, ); }, else => comptime unreachable, @@ -1187,7 +1107,6 @@ const Parser = struct { field: usize, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); - if (self.errors == null) return error.ParseZon; const ast_node = node.getAstNode(self.zoir); var buf: [2]Ast.Node.Index = undefined; const token = if (self.ast.fullStructInit(&buf, ast_node)) |struct_init| b: { @@ -1368,19 +1287,21 @@ test "std.zon ast errors" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .init(arena); + var errors: Errors = .empty; try std.testing.expectError( error.ParseZon, - fromSlice(struct {}, .{ + fromSliceNoAlloc(struct {}, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1 .y = 2}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:13: error: expected ',' after initializer \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } test "std.zon comments" { @@ -1388,21 +1309,23 @@ test "std.zon comments" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; - try std.testing.expectEqual(@as(u8, 10), fromSlice(u8, .{ + try std.testing.expectEqual(@as(u8, 10), fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = \\// comment \\10 // comment \\// comment , - .errors = null, + .errors = &errors, })); { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = \\//! comment \\10 // comment @@ -1410,7 +1333,11 @@ test "std.zon comments" { , .errors = &errors, })); - try std.testing.expectFmt("input.zon:1:1: error: expected expression, found 'a document comment'\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected expression, found 'a document comment' + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -1419,26 +1346,33 @@ test "std.zon failure/oom formatting" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .init(arena); - try std.testing.expectError(error.OutOfMemory, fromSliceAlloc([]const u8, .{ + var errors: Errors = .empty; + + try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{ .gpa = .failing, .arena = arena, .source = "\"foo\"", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } -test "std.zon fromSliceAlloc syntax error" { +test "std.zon fromSlice syntax error" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + var errors: Errors = .empty; try std.testing.expectError( error.ParseZon, - fromSlice(u8, .{ + fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = ".{", - .errors = null, + .errors = &errors, }), ); } @@ -1448,37 +1382,40 @@ test "std.zon optional" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Basic usage { - const none = try fromSlice(?u32, .{ + const none = try fromSliceNoAlloc(?u32, .{ .gpa = gpa, + .arena = arena, .source = "null", - .errors = null, + .errors = &errors, }); try std.testing.expect(none == null); - const some = try fromSlice(?u32, .{ + const some = try fromSliceNoAlloc(?u32, .{ .gpa = gpa, + .arena = arena, .source = "1", - .errors = null, + .errors = &errors, }); try std.testing.expect(some.? == 1); } // Deep free { - const none = try fromSliceAlloc(?[]const u8, .{ + const none = try fromSlice(?[]const u8, .{ .gpa = gpa, .arena = arena, .source = "null", - .errors = null, + .errors = &errors, }); try std.testing.expect(none == null); - const some = try fromSliceAlloc(?[]const u8, .{ + const some = try fromSlice(?[]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings("foo", some.?); } @@ -1489,47 +1426,54 @@ test "std.zon unions" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Unions { const Tagged = union(enum) { x: f32, @"y y": bool, z, @"z z" }; const Untagged = union { x: f32, @"y y": bool, z: void, @"z z": void }; - const tagged_x = try fromSlice(Tagged, .{ + const tagged_x = try fromSliceNoAlloc(Tagged, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.5}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x); - const tagged_y = try fromSlice(Tagged, .{ + const tagged_y = try fromSliceNoAlloc(Tagged, .{ .gpa = gpa, + .arena = arena, .source = ".{.@\"y y\" = true}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Tagged{ .@"y y" = true }, tagged_y); - const tagged_z_shorthand = try fromSlice(Tagged, .{ + const tagged_z_shorthand = try fromSliceNoAlloc(Tagged, .{ .gpa = gpa, + .arena = arena, .source = ".z", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand); - const tagged_zz_shorthand = try fromSlice(Tagged, .{ + const tagged_zz_shorthand = try fromSliceNoAlloc(Tagged, .{ .gpa = gpa, + .arena = arena, .source = ".@\"z z\"", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand); - const untagged_x = try fromSlice(Untagged, .{ + const untagged_x = try fromSliceNoAlloc(Untagged, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.5}", - .errors = null, + .errors = &errors, }); try std.testing.expect(untagged_x.x == 1.5); - const untagged_y = try fromSlice(Untagged, .{ + const untagged_y = try fromSliceNoAlloc(Untagged, .{ .gpa = gpa, + .arena = arena, .source = ".{.@\"y y\" = true}", - .errors = null, + .errors = &errors, }); try std.testing.expect(untagged_y.@"y y"); } @@ -1538,19 +1482,19 @@ test "std.zon unions" { { const Union = union(enum) { bar: []const u8, baz: bool }; - const noalloc = try fromSliceAlloc(Union, .{ + const noalloc = try fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.baz = false}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Union{ .baz = false }, noalloc); - const alloc = try fromSliceAlloc(Union, .{ + const alloc = try fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.bar = \"qux\"}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc); } @@ -1558,10 +1502,9 @@ test "std.zon unions" { // Unknown field { const Union = union { x: f32, y: f32 }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, .{ + fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.z=2.5}", @@ -1569,19 +1512,19 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:4: error: unexpected field 'z' \\input.zon:1:4: note: supported: 'x', 'y' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Explicit void field { const Union = union(enum) { x: void }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, .{ + fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.x=1}", @@ -1589,18 +1532,18 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:6: error: expected type 'void' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Extra field { const Union = union { x: f32, y: bool }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, .{ + fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".{.x = 1.5, .y = true}", @@ -1608,18 +1551,18 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // No fields { const Union = union { x: f32, y: bool }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(Union, .{ + fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".{}", @@ -1627,58 +1570,59 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Enum literals cannot coerce into untagged unions { const Union = union { x: void }; - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".x", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Unknown field for enum literal coercion { const Union = union(enum) { x: void }; - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".y", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: unexpected field 'y' \\input.zon:1:2: note: supported: 'x' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Non void field for enum literal coercion { const Union = union(enum) { x: f32 }; - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".x", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -1687,6 +1631,7 @@ test "std.zon structs" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Structs (various sizes tested since they're parsed differently) { @@ -1695,31 +1640,35 @@ test "std.zon structs" { const Vec2 = struct { x: f32, y: f32 }; const Vec3 = struct { x: f32, y: f32, z: f32 }; - const zero = try fromSlice(Vec0, .{ + const zero = try fromSliceNoAlloc(Vec0, .{ .gpa = gpa, + .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec0{}, zero); - const one = try fromSlice(Vec1, .{ + const one = try fromSliceNoAlloc(Vec1, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.2}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec1{ .x = 1.2 }, one); - const two = try fromSlice(Vec2, .{ + const two = try fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.2, .y = 3.4}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 3.4 }, two); - const three = try fromSlice(Vec3, .{ + const three = try fromSliceNoAlloc(Vec3, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.2, .y = 3.4, .z = 5.6}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three); } @@ -1728,11 +1677,11 @@ test "std.zon structs" { { const Foo = struct { bar: []const u8, baz: []const []const u8 }; - const parsed = try fromSliceAlloc(Foo, .{ + const parsed = try fromSlice(Foo, .{ .gpa = gpa, .arena = arena, .source = ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed); } @@ -1740,49 +1689,52 @@ test "std.zon structs" { // Unknown field { const Vec2 = struct { x: f32, y: f32 }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ + fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x=1.5, .z=2.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:12: error: unexpected field 'z' \\input.zon:1:12: note: supported: 'x', 'y' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Duplicate field { const Vec2 = struct { x: f32, y: f32 }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ + fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x=1.5, .x=2.5, .x=3.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:4: error: duplicate struct field name \\input.zon:1:12: note: duplicate name here \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Ignore unknown fields { const Vec2 = struct { x: f32, y: f32 = 2.0 }; - const parsed = try fromSlice(Vec2, .{ + const parsed = try fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{ .x = 1.0, .z = 3.0 }", .ignore_unknown_fields = true, - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec2{ .x = 1.0, .y = 2.0 }, parsed); } @@ -1790,47 +1742,50 @@ test "std.zon structs" { // Unknown field when struct has no fields (regression test) { const Vec2 = struct {}; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ + fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x=1.5, .z=2.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:4: error: unexpected field 'x' \\input.zon:1:4: note: none expected \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Missing field { const Vec2 = struct { x: f32, y: f32 }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Vec2, .{ + fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x=1.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: missing required field y \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Default field { const Vec2 = struct { x: f32, y: f32 = 1.5 }; - const parsed = try fromSlice(Vec2, .{ + const parsed = try fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.2}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } @@ -1838,10 +1793,11 @@ test "std.zon structs" { // Comptime field { const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; - const parsed = try fromSlice(Vec2, .{ + const parsed = try fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.2}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } @@ -1849,27 +1805,29 @@ test "std.zon structs" { // Comptime field assignment { const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; - var errors: Errors = .init(arena); - const parsed = fromSlice(Vec2, .{ + const parsed = fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{.x = 1.2, .y = 1.5}", .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:18: error: cannot initialize comptime field \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Enum field (regression test, we were previously getting the field name in an // incorrect way that broke for enum values) { const Vec0 = struct { x: enum { x } }; - const parsed = try fromSlice(Vec0, .{ + const parsed = try fromSliceNoAlloc(Vec0, .{ .gpa = gpa, + .arena = arena, .source = ".{ .x = .x }", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec0{ .x = .x }, parsed); } @@ -1877,10 +1835,11 @@ test "std.zon structs" { // Enum field and struct field with @ { const Vec0 = struct { @"x x": enum { @"x x" } }; - const parsed = try fromSlice(Vec0, .{ + const parsed = try fromSliceNoAlloc(Vec0, .{ .gpa = gpa, + .arena = arena, .source = ".{ .@\"x x\" = .@\"x x\" }", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec0{ .@"x x" = .@"x x" }, parsed); } @@ -1889,40 +1848,41 @@ test "std.zon structs" { { // Structs { - var errors: Errors = .init(arena); - const parsed = fromSlice(struct {}, .{ + const parsed = fromSliceNoAlloc(struct {}, .{ .gpa = gpa, + .arena = arena, .source = "Empty{}", .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Arrays { - var errors: Errors = .init(arena); - const parsed = fromSlice([3]u8, .{ + const parsed = fromSliceNoAlloc([3]u8, .{ .gpa = gpa, + .arena = arena, .source = "[3]u8{1, 2, 3}", .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Slices { - var errors: Errors = .init(arena); - const parsed = fromSliceAlloc([]u8, .{ + const parsed = fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "[]u8{1, 2, 3}", @@ -1930,42 +1890,45 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Tuples { - var errors: Errors = .init(arena); - const parsed = fromSlice(struct { u8, u8, u8 }, .{ + const parsed = fromSliceNoAlloc(struct { u8, u8, u8 }, .{ .gpa = gpa, + .arena = arena, .source = "Tuple{1, 2, 3}", .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Nested { - var errors: Errors = .init(arena); - const parsed = fromSlice(struct {}, .{ + const parsed = fromSliceNoAlloc(struct {}, .{ .gpa = gpa, + .arena = arena, .source = ".{ .x = Tuple{1, 2, 3} }", .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:9: error: types are not available in ZON \\input.zon:1:9: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } } @@ -1975,6 +1938,7 @@ test "std.zon tuples" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Structs (various sizes tested since they're parsed differently) { @@ -1983,31 +1947,35 @@ test "std.zon tuples" { const Tuple2 = struct { f32, bool }; const Tuple3 = struct { f32, bool, u8 }; - const zero = try fromSlice(Tuple0, .{ + const zero = try fromSliceNoAlloc(Tuple0, .{ .gpa = gpa, + .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Tuple0{}, zero); - const one = try fromSlice(Tuple1, .{ + const one = try fromSliceNoAlloc(Tuple1, .{ .gpa = gpa, + .arena = arena, .source = ".{1.2}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Tuple1{1.2}, one); - const two = try fromSlice(Tuple2, .{ + const two = try fromSliceNoAlloc(Tuple2, .{ .gpa = gpa, + .arena = arena, .source = ".{1.2, true}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Tuple2{ 1.2, true }, two); - const three = try fromSlice(Tuple3, .{ + const three = try fromSliceNoAlloc(Tuple3, .{ .gpa = gpa, + .arena = arena, .source = ".{1.2, false, 3}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Tuple3{ 1.2, false, 3 }, three); } @@ -2015,11 +1983,11 @@ test "std.zon tuples" { // Deep free { const Tuple = struct { []const u8, []const u8 }; - const parsed = try fromSliceAlloc(Tuple, .{ + const parsed = try fromSlice(Tuple, .{ .gpa = gpa, .arena = arena, .source = ".{\"hello\", \"world\"}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed); } @@ -2027,79 +1995,87 @@ test "std.zon tuples" { // Extra field { const Tuple = struct { f32, bool }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, .{ + fromSliceNoAlloc(Tuple, .{ .gpa = gpa, + .arena = arena, .source = ".{0.5, true, 123}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:14: error: index 2 outside of tuple length 2 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Extra field { const Tuple = struct { f32, bool }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, .{ + fromSliceNoAlloc(Tuple, .{ .gpa = gpa, + .arena = arena, .source = ".{0.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: missing tuple field with index 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Tuple with unexpected field names { const Tuple = struct { f32 }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Tuple, .{ + fromSliceNoAlloc(Tuple, .{ .gpa = gpa, + .arena = arena, .source = ".{.foo = 10.0}", .errors = &errors, }), ); - try std.testing.expectFmt("input.zon:1:2: error: expected tuple\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:2: error: expected tuple + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Struct with missing field names { const Struct = struct { foo: f32 }; - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Struct, .{ + fromSliceNoAlloc(Struct, .{ .gpa = gpa, + .arena = arena, .source = ".{10.0}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: expected struct \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Comptime field { const Vec2 = struct { f32, comptime f32 = 1.5 }; - const parsed = try fromSlice(Vec2, .{ + const parsed = try fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{ 1.2 }", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed); } @@ -2107,17 +2083,18 @@ test "std.zon tuples" { // Comptime field assignment { const Vec2 = struct { f32, comptime f32 = 1.5 }; - var errors: Errors = .init(arena); - const parsed = fromSlice(Vec2, .{ + const parsed = fromSliceNoAlloc(Vec2, .{ .gpa = gpa, + .arena = arena, .source = ".{ 1.2, 1.5}", .errors = &errors, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:9: error: cannot initialize comptime field \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -2129,50 +2106,57 @@ test "std.zon arrays and slices" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Literals { // Arrays { - const zero = try fromSlice([0]u8, .{ + const zero = try fromSliceNoAlloc([0]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &@as([0]u8, .{}), &zero); - const one = try fromSlice([1]u8, .{ + const one = try fromSliceNoAlloc([1]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a'}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &@as([1]u8, .{'a'}), &one); - const two = try fromSlice([2]u8, .{ + const two = try fromSliceNoAlloc([2]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b'}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two); - const two_comma = try fromSlice([2]u8, .{ + const two_comma = try fromSliceNoAlloc([2]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b',}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two_comma); - const three = try fromSlice([3]u8, .{ + const three = try fromSliceNoAlloc([3]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, &three); - const sentinel = try fromSlice([3:'z']u8, .{ + const sentinel = try fromSliceNoAlloc([3:'z']u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = null, + .errors = &errors, }); const expected_sentinel: [3:'z']u8 = .{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, &expected_sentinel, &sentinel); @@ -2180,51 +2164,51 @@ test "std.zon arrays and slices" { // Slice literals { - const zero = try fromSliceAlloc([]const u8, .{ + const zero = try fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero); - const one = try fromSliceAlloc([]u8, .{ + const one = try fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = ".{'a'}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &.{'a'}, one); - const two = try fromSliceAlloc([]const u8, .{ + const two = try fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{'a', 'b'}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two); - const two_comma = try fromSliceAlloc([]const u8, .{ + const two_comma = try fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{'a', 'b',}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma); - const three = try fromSliceAlloc([]u8, .{ + const three = try fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three); - const sentinel = try fromSliceAlloc([:'z']const u8, .{ + const sentinel = try fromSlice([:'z']const u8, .{ .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = null, + .errors = &errors, }); const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel); @@ -2235,11 +2219,11 @@ test "std.zon arrays and slices" { { // Arrays { - const parsed = try fromSliceAlloc([1][]const u8, .{ + const parsed = try fromSlice([1][]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{\"abc\"}", - .errors = null, + .errors = &errors, }); const expected: [1][]const u8 = .{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2247,11 +2231,11 @@ test "std.zon arrays and slices" { // Slice literals { - const parsed = try fromSliceAlloc([]const []const u8, .{ + const parsed = try fromSlice([]const []const u8, .{ .gpa = gpa, .arena = arena, .source = ".{\"abc\"}", - .errors = null, + .errors = &errors, }); const expected: []const []const u8 = &.{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2262,10 +2246,11 @@ test "std.zon arrays and slices" { { // Arrays { - const sentinel = try fromSlice([1:2]u8, .{ + const sentinel = try fromSliceNoAlloc([1:2]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{1}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -2274,11 +2259,11 @@ test "std.zon arrays and slices" { // Slice literals { - const sentinel = try fromSliceAlloc([:2]align(4) u8, .{ + const sentinel = try fromSlice([:2]align(4) u8, .{ .gpa = gpa, .arena = arena, .source = ".{1}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -2288,101 +2273,112 @@ test "std.zon arrays and slices" { // Expect 0 find 3 { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([0]u8, .{ + fromSliceNoAlloc([0]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b', 'c'}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:3: error: index 0 outside of array of length 0 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Expect 1 find 2 { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([1]u8, .{ + fromSliceNoAlloc([1]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b'}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:8: error: index 1 outside of array of length 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Expect 2 find 1 { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([2]u8, .{ + fromSliceNoAlloc([2]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{'a'}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: expected 2 array elements; found 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Expect 3 find 0 { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([3]u8, .{ + fromSliceNoAlloc([3]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{}", .errors = &errors, }), ); - try std.testing.expectFmt("input.zon:1:2: error: expected 3 array elements; found 0\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:2: error: expected 3 array elements; found 0 + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Wrong inner type { // Array { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([3]bool, .{ + fromSliceNoAlloc([3]bool, .{ .gpa = gpa, + .arena = arena, .source = ".{'a', 'b', 'c'}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:3: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Slice { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]bool, .{ + fromSlice([]bool, .{ .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", .errors = &errors, }), ); - try std.testing.expectFmt("input.zon:1:3: error: expected type 'bool'\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:3: error: expected type 'bool' + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -2390,43 +2386,47 @@ test "std.zon arrays and slices" { { // Array { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([3]u8, .{ + fromSliceNoAlloc([3]u8, .{ .gpa = gpa, + .arena = arena, .source = "'a'", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Slice { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .errors = &errors }), ); - try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } // Address of is not allowed (indirection for slices in ZON is implicit) { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:3: error: pointers are not available in ZON \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -2435,36 +2435,37 @@ test "std.zon string literal" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Basic string literal { - const parsed = try fromSliceAlloc([]const u8, .{ + const parsed = try fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"abc\"", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed); } // String literal with escape characters { - const parsed = try fromSliceAlloc([]const u8, .{ + const parsed = try fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"ab\\nc\"", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed); } // String literal with embedded null { - const parsed = try fromSliceAlloc([]const u8, .{ + const parsed = try fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"ab\\x00c\"", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed); } @@ -2472,24 +2473,27 @@ test "std.zon string literal" { // Passing string literal to a mutable slice { { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .errors = &errors }), ); - try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -2500,54 +2504,59 @@ test "std.zon string literal" { defer ast.deinit(gpa); var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(gpa); - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([4:0]u8, .{ + fromSliceNoAlloc([4:0]u8, .{ .gpa = gpa, + .arena = arena, .source = "\"abcd\"", .errors = &errors, }), ); - try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice([4:0]u8, .{ + fromSliceNoAlloc([4:0]u8, .{ .gpa = gpa, + .arena = arena, .source = "\\\\abcd", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } // Zero terminated slices { { - const parsed: [:0]const u8 = try fromSliceAlloc([:0]const u8, .{ + const parsed: [:0]const u8 = try fromSlice([:0]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"abc\"", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); } { - const parsed: [:0]const u8 = try fromSliceAlloc([:0]const u8, .{ + const parsed: [:0]const u8 = try fromSlice([:0]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abc", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); @@ -2557,114 +2566,119 @@ test "std.zon string literal" { // Other value terminated slices { { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .errors = &errors }), + fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .errors = &errors }), ); - try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .errors = &errors }), + fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } // Expecting string literal, getting something else { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .errors = &errors }), + fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected string \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Expecting string literal, getting an incompatible tuple { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .errors = &errors }), + fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:3: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Invalid string literal { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .errors = &errors }), + fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:3: error: invalid escape character: 'a' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Slice wrong child type { { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .errors = &errors }), + fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .errors = &errors }), + fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .errors = &errors }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } // Bad alignment { { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]align(2) const u8, .{ + fromSlice([]align(2) const u8, .{ .gpa = gpa, .arena = arena, .source = "\"abc\"", .errors = &errors, }), ); - try std.testing.expectFmt("input.zon:1:1: error: expected array\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected array + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc([]align(2) const u8, .{ + fromSlice([]align(2) const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abc", @@ -2672,9 +2686,10 @@ test "std.zon string literal" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -2687,7 +2702,7 @@ test "std.zon string literal" { message2: String, message3: String, }; - const parsed = try fromSliceAlloc(S, .{ + const parsed = try fromSlice(S, .{ .gpa = gpa, .arena = arena, .source = @@ -2708,7 +2723,7 @@ test "std.zon string literal" { \\ \\and this. \\} , - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings( "hello, world!\nthis is a multiline string!\n\n...", @@ -2725,6 +2740,7 @@ test "std.zon enum literals" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const Enum = enum { foo, @@ -2734,98 +2750,106 @@ test "std.zon enum literals" { }; // Tags that exist - try std.testing.expectEqual(Enum.foo, try fromSlice(Enum, .{ + try std.testing.expectEqual(Enum.foo, try fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".foo", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(Enum.bar, try fromSlice(Enum, .{ + try std.testing.expectEqual(Enum.bar, try fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".bar", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(Enum.baz, try fromSlice(Enum, .{ + try std.testing.expectEqual(Enum.baz, try fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".baz", - .errors = null, + .errors = &errors, })); try std.testing.expectEqual( Enum.@"ab\nc", - try fromSlice(Enum, .{ + try fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".@\"ab\\nc\"", - .errors = null, + .errors = &errors, }), ); // Bad tag { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ + fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".qux", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: unexpected enum literal 'qux' \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Bad tag that's too long for parser { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ + fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".@\"foobarbaz\"", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: unexpected enum literal 'foobarbaz' \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Bad type { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ + fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = "true", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Test embedded nulls in an identifier { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(Enum, .{ + fromSliceNoAlloc(Enum, .{ .gpa = gpa, + .arena = arena, .source = ".@\"\\x00\"", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: identifier cannot contain null bytes \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -2834,48 +2858,53 @@ test "std.zon parse bool" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Correct bools - try std.testing.expectEqual(true, try fromSlice(bool, .{ + try std.testing.expectEqual(true, try fromSliceNoAlloc(bool, .{ .gpa = gpa, + .arena = arena, .source = "true", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(false, try fromSlice(bool, .{ + try std.testing.expectEqual(false, try fromSliceNoAlloc(bool, .{ .gpa = gpa, + .arena = arena, .source = "false", - .errors = null, + .errors = &errors, })); // Errors { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(bool, .{ + fromSliceNoAlloc(bool, .{ .gpa = gpa, + .arena = arena, .source = " foo", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: invalid expression \\input.zon:1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\input.zon:1:2: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(bool, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(bool, .{ .gpa = gpa, + .arena = arena, .source = "123", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -2906,410 +2935,461 @@ test "std.zon parse int" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Test various numbers and types - try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, .{ + try std.testing.expectEqual(@as(u8, 10), try fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "10", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i16, 24), try fromSlice(i16, .{ + try std.testing.expectEqual(@as(i16, 24), try fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "24", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i14, -4), try fromSlice(i14, .{ + try std.testing.expectEqual(@as(i14, -4), try fromSliceNoAlloc(i14, .{ .gpa = gpa, + .arena = arena, .source = "-4", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i32, -123), try fromSlice(i32, .{ + try std.testing.expectEqual(@as(i32, -123), try fromSliceNoAlloc(i32, .{ .gpa = gpa, + .arena = arena, .source = "-123", - .errors = null, + .errors = &errors, })); // Test limits - try std.testing.expectEqual(@as(i8, 127), try fromSlice(i8, .{ + try std.testing.expectEqual(@as(i8, 127), try fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "127", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i8, -128), try fromSlice(i8, .{ + try std.testing.expectEqual(@as(i8, -128), try fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "-128", - .errors = null, + .errors = &errors, })); // Test characters - try std.testing.expectEqual(@as(u8, 'a'), try fromSlice(u8, .{ + try std.testing.expectEqual(@as(u8, 'a'), try fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "'a'", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(u8, 'z'), try fromSlice(u8, .{ + try std.testing.expectEqual(@as(u8, 'z'), try fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "'z'", - .errors = null, + .errors = &errors, })); // Test big integers try std.testing.expectEqual( @as(u65, 36893488147419103231), - try fromSlice(u65, .{ + try fromSliceNoAlloc(u65, .{ .gpa = gpa, + .arena = arena, .source = "36893488147419103231", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @as(u65, 36893488147419103231), - try fromSlice(u65, .{ + try fromSliceNoAlloc(u65, .{ .gpa = gpa, + .arena = arena, .source = "368934_881_474191032_31", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @as(u128, 340282366920938463463374607431768211455), - try fromSlice(u128, .{ + try fromSliceNoAlloc(u128, .{ .gpa = gpa, + .arena = arena, .source = "340282366920938463463374607431768211455", - .errors = null, + .errors = &errors, }), ); // Test big integer limits try std.testing.expectEqual( @as(i66, 36893488147419103231), - try fromSlice(i66, .{ + try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "36893488147419103231", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @as(i66, -36893488147419103232), - try fromSlice(i66, .{ + try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "-36893488147419103232", - .errors = null, + .errors = &errors, }), ); { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "36893488147419103232", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'i66' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i66, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "-36893488147419103233", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'i66' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Test parsing whole number floats as integers - try std.testing.expectEqual(@as(i8, -1), try fromSlice(i8, .{ + try std.testing.expectEqual(@as(i8, -1), try fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "-1.0", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i8, 123), try fromSlice(i8, .{ + try std.testing.expectEqual(@as(i8, 123), try fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "123.0", - .errors = null, + .errors = &errors, })); // Test non-decimal integers - try std.testing.expectEqual(@as(i16, 0xff), try fromSlice(i16, .{ + try std.testing.expectEqual(@as(i16, 0xff), try fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "0xff", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i16, -0xff), try fromSlice(i16, .{ + try std.testing.expectEqual(@as(i16, -0xff), try fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "-0xff", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i16, 0o77), try fromSlice(i16, .{ + try std.testing.expectEqual(@as(i16, 0o77), try fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "0o77", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i16, -0o77), try fromSlice(i16, .{ + try std.testing.expectEqual(@as(i16, -0o77), try fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "-0o77", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i16, 0b11), try fromSlice(i16, .{ + try std.testing.expectEqual(@as(i16, 0b11), try fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "0b11", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i16, -0b11), try fromSlice(i16, .{ + try std.testing.expectEqual(@as(i16, -0b11), try fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "-0b11", - .errors = null, + .errors = &errors, })); // Test non-decimal big integers - try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ + try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSliceNoAlloc(u65, .{ .gpa = gpa, + .arena = arena, .source = "0x1ffffffffffffffff", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ + try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "0x1ffffffffffffffff", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ + try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "-0x1ffffffffffffffff", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ + try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSliceNoAlloc(u65, .{ .gpa = gpa, + .arena = arena, .source = "0o3777777777777777777777", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ + try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "0o3777777777777777777777", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ + try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "-0o3777777777777777777777", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(u65, .{ + try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSliceNoAlloc(u65, .{ .gpa = gpa, + .arena = arena, .source = "0b11111111111111111111111111111111111111111111111111111111111111111", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(i66, .{ + try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "0b11111111111111111111111111111111111111111111111111111111111111111", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(i66, .{ + try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, + .arena = arena, .source = "-0b11111111111111111111111111111111111111111111111111111111111111111", - .errors = null, + .errors = &errors, })); // Number with invalid character in the middle { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "32a32", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:3: error: invalid digit 'a' for decimal base \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Failing to parse as int { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "true", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Failing because an int is out of range { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "256", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Failing because a negative int is out of range { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "-129", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'i8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Failing because an unsigned int is negative { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "-1", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Failing because a float is non-whole { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "1.5", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Failing because a float is negative { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "-1.0", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Negative integer zero { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "-0", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: integer literal '-0' is ambiguous \\input.zon:1:2: note: use '0' for an integer zero \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Negative integer zero casted to float { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-0", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: integer literal '-0' is ambiguous \\input.zon:1:2: note: use '0' for an integer zero \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Negative float 0 is allowed try std.testing.expect( - std.math.isNegativeZero(try fromSlice(f32, .{ + std.math.isNegativeZero(try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-0.0", - .errors = null, + .errors = &errors, })), ); - try std.testing.expect(std.math.isPositiveZero(try fromSlice(f32, .{ + try std.testing.expect(std.math.isPositiveZero(try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "0.0", - .errors = null, + .errors = &errors, }))); // Double negation is not allowed { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "--2", .errors = &errors, })); - try std.testing.expectFmt("input.zon:1:1: error: expected number or 'inf' after '-'\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected number or 'inf' after '-' + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(f32, .{ + fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "--2.0", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Invalid int literal { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "0xg", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:3: error: invalid digit 'g' for hex base \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Notes on invalid int literal { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(u8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, + .arena = arena, .source = "0123", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: number '0123' has leading zero \\input.zon:1:1: note: use '0o' prefix for octal literals \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -3318,30 +3398,33 @@ test "std.zon negative char" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-'a'", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i16, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i16, .{ .gpa = gpa, + .arena = arena, .source = "-'a'", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -3352,261 +3435,294 @@ test "std.zon parse float" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Test decimals - try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, .{ + try std.testing.expectEqual(@as(f16, 0.5), try fromSliceNoAlloc(f16, .{ .gpa = gpa, + .arena = arena, .source = "0.5", - .errors = null, + .errors = &errors, })); try std.testing.expectEqual( @as(f32, 123.456), - try fromSlice(f32, .{ + try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "123.456", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @as(f64, -123.456), - try fromSlice(f64, .{ + try fromSliceNoAlloc(f64, .{ .gpa = gpa, + .arena = arena, .source = "-123.456", - .errors = null, + .errors = &errors, }), ); - try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, .{ + try std.testing.expectEqual(@as(f128, 42.5), try fromSliceNoAlloc(f128, .{ .gpa = gpa, + .arena = arena, .source = "42.5", - .errors = null, + .errors = &errors, })); // Test whole numbers with and without decimals - try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, .{ + try std.testing.expectEqual(@as(f16, 5.0), try fromSliceNoAlloc(f16, .{ .gpa = gpa, + .arena = arena, .source = "5.0", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, .{ + try std.testing.expectEqual(@as(f16, 5.0), try fromSliceNoAlloc(f16, .{ .gpa = gpa, + .arena = arena, .source = "5", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, .{ + try std.testing.expectEqual(@as(f32, -102), try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-102.0", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, .{ + try std.testing.expectEqual(@as(f32, -102), try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-102", - .errors = null, + .errors = &errors, })); // Test characters and negated characters - try std.testing.expectEqual(@as(f32, 'a'), try fromSlice(f32, .{ + try std.testing.expectEqual(@as(f32, 'a'), try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "'a'", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(f32, 'z'), try fromSlice(f32, .{ + try std.testing.expectEqual(@as(f32, 'z'), try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "'z'", - .errors = null, + .errors = &errors, })); // Test big integers try std.testing.expectEqual( @as(f32, 36893488147419103231.0), - try fromSlice(f32, .{ + try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "36893488147419103231", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @as(f32, -36893488147419103231.0), - try fromSlice(f32, .{ + try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-36893488147419103231", - .errors = null, + .errors = &errors, }), ); - try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(f128, .{ + try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSliceNoAlloc(f128, .{ .gpa = gpa, + .arena = arena, .source = "0x1ffffffffffffffff", - .errors = null, + .errors = &errors, })); - try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(f32, .{ + try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "0x1ffffffffffffffff", - .errors = null, + .errors = &errors, })); // Exponents, underscores try std.testing.expectEqual( @as(f32, 123.0E+77), - try fromSlice(f32, .{ + try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "12_3.0E+77", - .errors = null, + .errors = &errors, }), ); // Hexadecimal try std.testing.expectEqual( @as(f32, 0x103.70p-5), - try fromSlice(f32, .{ + try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "0x103.70p-5", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @as(f32, -0x103.70), - try fromSlice(f32, .{ + try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-0x103.70", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @as(f32, 0x1234_5678.9ABC_CDEFp-10), - try fromSlice(f32, .{ + try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "0x1234_5678.9ABC_CDEFp-10", - .errors = null, + .errors = &errors, }), ); // inf, nan - try std.testing.expect(std.math.isPositiveInf(try fromSlice(f32, .{ + try std.testing.expect(std.math.isPositiveInf(try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "inf", - .errors = null, + .errors = &errors, }))); - try std.testing.expect(std.math.isNegativeInf(try fromSlice(f32, .{ + try std.testing.expect(std.math.isNegativeInf(try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-inf", - .errors = null, + .errors = &errors, }))); - try std.testing.expect(std.math.isNan(try fromSlice(f32, .{ + try std.testing.expect(std.math.isNan(try fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "nan", - .errors = null, + .errors = &errors, }))); // Negative nan not allowed { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-nan", .errors = &errors, })); - try std.testing.expectFmt("input.zon:1:1: error: expected number or 'inf' after '-'\n", "{f}", .{errors.fmt("input.zon")}); + try std.testing.expectFmt( + \\input.zon: error.ParseZon + \\input.zon:1:1: error: expected number or 'inf' after '-' + \\ + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // nan as int not allowed { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "nan", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // nan as int not allowed { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "nan", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // inf as int not allowed { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "inf", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // -inf as int not allowed { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(i8, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i8, .{ .gpa = gpa, + .arena = arena, .source = "-inf", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Bad identifier as float { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "foo", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: invalid expression \\input.zon:1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\input.zon:1:1: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, fromSlice(f32, .{ + try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "-foo", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Non float as float { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(f32, .{ + fromSliceNoAlloc(f32, .{ .gpa = gpa, + .arena = arena, .source = "\"foo\"", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'f32' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -3615,6 +3731,7 @@ test "std.zon free on error" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Test freeing partially allocated structs { @@ -3623,7 +3740,7 @@ test "std.zon free on error" { y: []const u8, z: bool, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Struct, .{ .gpa = gpa, .arena = arena, .source = @@ -3633,7 +3750,7 @@ test "std.zon free on error" { \\ .z = "fail", \\} , - .errors = null, + .errors = &errors, })); } @@ -3644,7 +3761,7 @@ test "std.zon free on error" { []const u8, bool, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Struct, .{ .gpa = gpa, .arena = arena, .source = @@ -3654,7 +3771,7 @@ test "std.zon free on error" { \\ "fail", \\} , - .errors = null, + .errors = &errors, })); } @@ -3663,7 +3780,7 @@ test "std.zon free on error" { x: []const u8, y: bool, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Struct, .{ .gpa = gpa, .arena = arena, .source = @@ -3671,11 +3788,11 @@ test "std.zon free on error" { \\ .x = "hello", \\} , - .errors = null, + .errors = &errors, })); // Test freeing partially allocated arrays - try std.testing.expectError(error.ParseZon, fromSliceAlloc([3][]const u8, .{ + try std.testing.expectError(error.ParseZon, fromSlice([3][]const u8, .{ .gpa = gpa, .arena = arena, .source = @@ -3685,11 +3802,11 @@ test "std.zon free on error" { \\ false, \\} , - .errors = null, + .errors = &errors, })); // Test freeing partially allocated slices - try std.testing.expectError(error.ParseZon, fromSliceAlloc([][]const u8, .{ + try std.testing.expectError(error.ParseZon, fromSlice([][]const u8, .{ .gpa = gpa, .arena = arena, .source = @@ -3699,28 +3816,29 @@ test "std.zon free on error" { \\ false, \\} , - .errors = null, + .errors = &errors, })); // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged // unions. try std.testing.expectEqual( @as(f32, 1.5), - (try fromSlice(union { x: f32 }, .{ + (try fromSliceNoAlloc(union { x: f32 }, .{ .gpa = gpa, + .arena = arena, .source = ".{ .x = 1.5 }", - .errors = null, + .errors = &errors, })).x, ); // We can also parse types that can't be freed if it's impossible for an error to occur after // the allocation, as is the case here. { - const result = try fromSliceAlloc(union { x: []const u8 }, .{ + const result = try fromSlice(union { x: []const u8 }, .{ .gpa = gpa, .arena = arena, .source = ".{ .x = \"foo\" }", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualStrings("foo", result.x); } @@ -3734,74 +3852,81 @@ test "std.zon vector" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Passing cases try std.testing.expectEqual( @Vector(0, bool){}, - try fromSlice(@Vector(0, bool), .{ + try fromSliceNoAlloc(@Vector(0, bool), .{ .gpa = gpa, + .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @Vector(3, bool){ true, false, true }, - try fromSlice(@Vector(3, bool), .{ + try fromSliceNoAlloc(@Vector(3, bool), .{ .gpa = gpa, + .arena = arena, .source = ".{true, false, true}", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @Vector(0, f32){}, - try fromSlice(@Vector(0, f32), .{ + try fromSliceNoAlloc(@Vector(0, f32), .{ .gpa = gpa, + .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @Vector(3, f32){ 1.5, 2.5, 3.5 }, - try fromSlice(@Vector(3, f32), .{ + try fromSliceNoAlloc(@Vector(3, f32), .{ .gpa = gpa, + .arena = arena, .source = ".{1.5, 2.5, 3.5}", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @Vector(0, u8){}, - try fromSlice(@Vector(0, u8), .{ + try fromSliceNoAlloc(@Vector(0, u8), .{ .gpa = gpa, + .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( @Vector(3, u8){ 2, 4, 6 }, - try fromSlice(@Vector(3, u8), .{ + try fromSliceNoAlloc(@Vector(3, u8), .{ .gpa = gpa, + .arena = arena, .source = ".{2, 4, 6}", - .errors = null, + .errors = &errors, }), ); { try std.testing.expectEqual( @Vector(0, *const u8){}, - try fromSliceAlloc(@Vector(0, *const u8), .{ + try fromSlice(@Vector(0, *const u8), .{ .gpa = gpa, .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }), ); - const pointers = try fromSliceAlloc(@Vector(3, *const u8), .{ + const pointers = try fromSlice(@Vector(3, *const u8), .{ .gpa = gpa, .arena = arena, .source = ".{2, 4, 6}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers); } @@ -3809,96 +3934,99 @@ test "std.zon vector" { { try std.testing.expectEqual( @Vector(0, ?*const u8){}, - try fromSliceAlloc(@Vector(0, ?*const u8), .{ + try fromSlice(@Vector(0, ?*const u8), .{ .gpa = gpa, .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }), ); - const pointers = try fromSliceAlloc(@Vector(3, ?*const u8), .{ + const pointers = try fromSlice(@Vector(3, ?*const u8), .{ .gpa = gpa, .arena = arena, .source = ".{2, null, 6}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers); } // Too few fields { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(2, f32), .{ + fromSliceNoAlloc(@Vector(2, f32), .{ .gpa = gpa, + .arena = arena, .source = ".{0.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:2: error: expected 2 array elements; found 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Too many fields { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(2, f32), .{ + fromSliceNoAlloc(@Vector(2, f32), .{ .gpa = gpa, + .arena = arena, .source = ".{0.5, 1.5, 2.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:13: error: index 2 outside of array of length 2 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Wrong type fields { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(3, f32), .{ + fromSliceNoAlloc(@Vector(3, f32), .{ .gpa = gpa, + .arena = arena, .source = ".{0.5, true, 2.5}", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:8: error: expected type 'f32' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Wrong type { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(3, u8), .{ + fromSliceNoAlloc(@Vector(3, u8), .{ .gpa = gpa, + .arena = arena, .source = "true", .errors = &errors, }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '@Vector(3, u8)' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Elements should get freed on error { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(@Vector(3, *u8), .{ + fromSlice(@Vector(3, *u8), .{ .gpa = gpa, .arena = arena, .source = ".{1, true, 3}", @@ -3906,9 +4034,10 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:6: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } @@ -3917,136 +4046,137 @@ test "std.zon add pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Primitive with varying levels of pointers { - const result = try fromSliceAlloc(*u32, .{ + const result = try fromSlice(*u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), result.*); } { - const result = try fromSliceAlloc(**u32, .{ + const result = try fromSlice(**u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), result.*.*); } { - const result = try fromSliceAlloc(***u32, .{ + const result = try fromSlice(***u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), result.*.*.*); } // Primitive optional with varying levels of pointers { - const some = try fromSliceAlloc(?*u32, .{ + const some = try fromSlice(?*u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), some.?.*); - const none = try fromSliceAlloc(?*u32, .{ + const none = try fromSlice(?*u32, .{ .gpa = gpa, .arena = arena, .source = "null", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(null, none); } { - const some = try fromSliceAlloc(*?u32, .{ + const some = try fromSlice(*?u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), some.*.?); - const none = try fromSliceAlloc(*?u32, .{ + const none = try fromSlice(*?u32, .{ .gpa = gpa, .arena = arena, .source = "null", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(null, none.*); } { - const some = try fromSliceAlloc(?**u32, .{ + const some = try fromSlice(?**u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), some.?.*.*); - const none = try fromSliceAlloc(?**u32, .{ + const none = try fromSlice(?**u32, .{ .gpa = gpa, .arena = arena, .source = "null", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(null, none); } { - const some = try fromSliceAlloc(*?*u32, .{ + const some = try fromSlice(*?*u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), some.*.?.*); - const none = try fromSliceAlloc(*?*u32, .{ + const none = try fromSlice(*?*u32, .{ .gpa = gpa, .arena = arena, .source = "null", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(null, none.*); } { - const some = try fromSliceAlloc(**?u32, .{ + const some = try fromSlice(**?u32, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(@as(u32, 10), some.*.*.?); - const none = try fromSliceAlloc(**?u32, .{ + const none = try fromSlice(**?u32, .{ .gpa = gpa, .arena = arena, .source = "null", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(null, none.*.*); } // Pointer to an array { - const result = try fromSliceAlloc(*[3]u8, .{ + const result = try fromSlice(*[3]u8, .{ .gpa = gpa, .arena = arena, .source = ".{ 1, 2, 3 }", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*); } @@ -4069,7 +4199,7 @@ test "std.zon add pointers" { .f2 = &null, }; - const found = try fromSliceAlloc(?*Outer, .{ + const found = try fromSlice(?*Outer, .{ .gpa = gpa, .arena = arena, .source = @@ -4081,7 +4211,7 @@ test "std.zon add pointers" { \\ .f2 = null, \\} , - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(expected, found.?.*); @@ -4089,10 +4219,9 @@ test "std.zon add pointers" { // Test that optional types are flattened correctly in errors { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const u8, .{ + fromSlice(*const ?*const u8, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4100,16 +4229,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const f32, .{ + fromSlice(*const ?*const f32, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4117,16 +4246,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?f32' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const @Vector(3, u8), .{ + fromSlice(*const ?*const @Vector(3, u8), .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4134,16 +4263,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?@Vector(3, u8)' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const bool, .{ + fromSlice(*const ?*const bool, .{ .gpa = gpa, .arena = arena, .source = "10", @@ -4151,16 +4280,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?bool' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const struct { a: i32 }, .{ + fromSlice(*const ?*const struct { a: i32 }, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4168,16 +4297,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional struct \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const struct { i32 }, .{ + fromSlice(*const ?*const struct { i32 }, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4185,16 +4314,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional tuple \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const union { x: void }, .{ + fromSlice(*const ?*const union { x: void }, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4202,16 +4331,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const [3]u8, .{ + fromSlice(*const ?*const [3]u8, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4219,16 +4348,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(?[3]u8, .{ + fromSlice(?[3]u8, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4236,16 +4365,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const []u8, .{ + fromSlice(*const ?*const []u8, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4253,16 +4382,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(?[]u8, .{ + fromSlice(?[]u8, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4270,16 +4399,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const []const u8, .{ + fromSlice(*const ?*const []const u8, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4287,16 +4416,16 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional string \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } { - var errors: Errors = .init(arena); try std.testing.expectError( error.ParseZon, - fromSliceAlloc(*const ?*const enum { foo }, .{ + fromSlice(*const ?*const enum { foo }, .{ .gpa = gpa, .arena = arena, .source = "true", @@ -4304,14 +4433,19 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } } test "std.zon stop on node" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + var errors: Errors = .empty; { const Vec2 = struct { @@ -4325,10 +4459,11 @@ test "std.zon stop on node" { var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(gpa); - const result = try fromZoir(Vec2, .{ + const result = try fromZoirNoAlloc(Vec2, .{ + .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(result.y, 2.5); @@ -4342,10 +4477,11 @@ test "std.zon stop on node" { var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(gpa); - const result = try fromZoir(Zoir.Node.Index, .{ + const result = try fromZoirNoAlloc(Zoir.Node.Index, .{ + .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(&zoir)); } @@ -4353,13 +4489,18 @@ test "std.zon stop on node" { test "std.zon no alloc" { const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + var errors: Errors = .empty; try std.testing.expectEqual( [3]u8{ 1, 2, 3 }, - try fromSlice([3]u8, .{ + try fromSliceNoAlloc([3]u8, .{ .gpa = gpa, + .arena = arena, .source = ".{ 1, 2, 3 }", - .errors = null, + .errors = &errors, }), ); @@ -4373,10 +4514,11 @@ test "std.zon no alloc" { try std.testing.expectEqual( Nested{ 1, 2, .{ 3, 4 } }, - try fromZoir(Nested, .{ + try fromZoirNoAlloc(Nested, .{ + .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = null, + .errors = &errors, }), ); } @@ -4386,44 +4528,45 @@ test "std.zon errors without errorsnostics" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const Enum = enum { foo, bar, baz, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Enum, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Enum, .{ .gpa = gpa, .arena = arena, .source = ".nothing", - .errors = null, + .errors = &errors, })); const Struct = struct { name: []const u8, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Struct, .{ .gpa = gpa, .arena = arena, .source = ".{ .name = \"Alice\", .age = 25 }", - .errors = null, + .errors = &errors, })); const Union = union(enum) { x, y: u32, }; - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".a", - .errors = null, + .errors = &errors, })); - try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, .{ + try std.testing.expectError(error.ParseZon, fromSlice(Union, .{ .gpa = gpa, .arena = arena, .source = ".{ .b = 8 }", - .errors = null, + .errors = &errors, })); } @@ -4432,6 +4575,7 @@ test "std.zon aligned pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const n: u8 align(8) = 10; const Foo = struct { @@ -4441,11 +4585,11 @@ test "std.zon aligned pointers" { const expected: Foo = .{ .inner = &n, }; - const found = try fromSliceAlloc(Foo, .{ + const found = try fromSlice(Foo, .{ .gpa = gpa, .arena = arena, .source = ".{ .inner = 10 }", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(expected.inner, found.inner); } @@ -4455,6 +4599,7 @@ test "std.zon update basic" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const Vector = struct { x: f32, y: f32, z: f32 }; const MyStruct = struct { @@ -4486,77 +4631,77 @@ test "std.zon update basic" { .str = "hello, world", }; - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{}", - .errors = null, + .errors = &errors, }); try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .bar = false }", - .errors = null, + .errors = &errors, }); expected.bar = false; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .baz = .{ .a = .{ 2, 4 } } }", - .errors = null, + .errors = &errors, }); expected.baz.a[0] = 2; expected.baz.a[1] = 4; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .foo = 11, .baz = .{ .b = .d } }", - .errors = null, + .errors = &errors, }); expected.foo = 11; expected.baz.b = .d; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .optional = 10 }", - .errors = null, + .errors = &errors, }); expected.optional = 10; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .optional = null }", - .errors = null, + .errors = &errors, }); expected.optional = null; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", - .errors = null, + .errors = &errors, }); expected.ptr.x = 10; expected.ptr.y = 20; expected.ptr.z = 30; try std.testing.expectEqualDeep(expected, found); - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .str = \"foo\" }", - .errors = null, + .errors = &errors, }); expected.str = "foo"; try std.testing.expectEqualDeep(expected, found); @@ -4567,6 +4712,7 @@ test "std.zon update optionals" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const MyStruct = struct { foo: ?struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, @@ -4576,10 +4722,11 @@ test "std.zon update optionals" { { var expected: MyStruct = .{ .foo = null }; var found = expected; - try updateFromSlice(MyStruct, &found, .{ + try updateFromSliceNoAlloc(MyStruct, &found, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = .{ .qux = 3 } }", - .errors = null, + .errors = &errors, }); expected.foo = .{ .qux = 3 }; try std.testing.expectEqual(expected, found); @@ -4588,16 +4735,17 @@ test "std.zon update optionals" { // Updating an optional that starts out null should error if we leave off required fields. { var found: MyStruct = .{ .foo = null }; - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, updateFromSlice(MyStruct, &found, .{ + try std.testing.expectError(error.ParseZon, updateFromSliceNoAlloc(MyStruct, &found, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = .{} }", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:12: error: missing required field qux \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Updating an optional that starts out non-null should preserve any values we leave off. It's @@ -4607,10 +4755,11 @@ test "std.zon update optionals" { .foo = .{ .bar = 10, .baz = 20, .qux = 30 }, }; var found = expected; - try updateFromSlice(MyStruct, &found, .{ + try updateFromSliceNoAlloc(MyStruct, &found, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = .{ .baz = 200 } }", - .errors = null, + .errors = &errors, }); expected.foo.?.baz = 200; try std.testing.expectEqual(expected, found); @@ -4622,6 +4771,7 @@ test "std.zon update optional pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const MyStruct = struct { foo: ?*const struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, @@ -4631,11 +4781,11 @@ test "std.zon update optional pointers" { { var expected: MyStruct = .{ .foo = null }; var found = expected; - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .qux = 3 } }", - .errors = null, + .errors = &errors, }); expected.foo = &.{ .qux = 3 }; try std.testing.expectEqualDeep(expected, found); @@ -4647,6 +4797,7 @@ test "std.zon update pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; // Updating a pointer should leave fields we don't specify unchanged { @@ -4656,11 +4807,11 @@ test "std.zon update pointers" { var ptr: @typeInfo(@FieldType(MyStruct, "foo")).pointer.child = .{ .qux = 3 }; var expected: MyStruct = .{ .foo = &ptr }; var found = expected; - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .bar = 100 } }", - .errors = null, + .errors = &errors, }); expected.foo.bar = 100; try std.testing.expectEqualDeep(expected, found); @@ -4673,11 +4824,11 @@ test "std.zon update pointers" { }; var expected: MyStruct = .{ .foo = &.{ .qux = 3 } }; var found = expected; - try updateFromSliceAlloc(MyStruct, &found, .{ + try updateFromSlice(MyStruct, &found, .{ .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .bar = 100 } }", - .errors = null, + .errors = &errors, }); expected.foo = &.{ .bar = 100, .baz = 20, .qux = 3 }; try std.testing.expectEqualDeep(expected, found); @@ -4689,6 +4840,7 @@ test "std.zon update unions" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const MyUnion = union(enum) { none: void, @@ -4699,10 +4851,11 @@ test "std.zon update unions" { { var expected: MyUnion = .none; var found = expected; - try updateFromSlice(MyUnion, &found, .{ + try updateFromSliceNoAlloc(MyUnion, &found, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = .{ .qux = 3 } }", - .errors = null, + .errors = &errors, }); expected = .{ .foo = .{ .qux = 3 } }; try std.testing.expectEqual(expected, found); @@ -4711,16 +4864,17 @@ test "std.zon update unions" { // Updating a union to a new field should error if we leave off required sub-fields { var found: MyUnion = .none; - var errors: Errors = .init(arena); - try std.testing.expectError(error.ParseZon, updateFromSlice(MyUnion, &found, .{ + try std.testing.expectError(error.ParseZon, updateFromSliceNoAlloc(MyUnion, &found, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = .{} }", .errors = &errors, })); try std.testing.expectFmt( + \\input.zon: error.ParseZon \\input.zon:1:12: error: missing required field qux \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); } // Updating a union should preseve any sub-fields that we left off. It's also okay to leave off @@ -4730,10 +4884,11 @@ test "std.zon update unions" { .foo = .{ .bar = 10, .baz = 20, .qux = 30 }, }; var found = expected; - try updateFromSlice(MyUnion, &found, .{ + try updateFromSliceNoAlloc(MyUnion, &found, .{ .gpa = gpa, + .arena = arena, .source = ".{ .foo = .{ .baz = 200 } }", - .errors = null, + .errors = &errors, }); expected.foo.baz = 200; try std.testing.expectEqual(expected, found); @@ -4748,6 +4903,7 @@ test "std.zon variants" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + var errors: Errors = .empty; const Struct = struct { a: u32, b: u32 }; const start: Struct = .{ .a = 10, .b = 20 }; @@ -4761,36 +4917,38 @@ test "std.zon variants" { defer zoir.deinit(gpa); var curr = start; + try updateFromSliceNoAlloc(Struct, &curr, .{ + .gpa = gpa, + .arena = arena, + .source = ".{ .a = 100 }", + .errors = &errors, + }); + try std.testing.expectEqual(end, curr); + + curr = start; try updateFromSlice(Struct, &curr, .{ .gpa = gpa, + .arena = arena, .source = ".{ .a = 100 }", - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(end, curr); curr = start; - try updateFromSliceAlloc(Struct, &curr, .{ - .gpa = gpa, + try updateFromZoirNoAlloc(Struct, &curr, .{ .arena = arena, - .source = ".{ .a = 100 }", - .errors = null, + .ast = &ast, + .zoir = &zoir, + .errors = &errors, }); try std.testing.expectEqual(end, curr); curr = start; try updateFromZoir(Struct, &curr, .{ - .ast = &ast, - .zoir = &zoir, - .errors = null, - }); - try std.testing.expectEqual(end, curr); - - curr = start; - try updateFromZoirAlloc(Struct, &curr, .{ .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = null, + .errors = &errors, }); try std.testing.expectEqual(end, curr); } @@ -4802,37 +4960,39 @@ test "std.zon variants" { const zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(gpa); + try std.testing.expectEqual( + end, + try fromSliceNoAlloc(Struct, .{ + .gpa = gpa, + .arena = arena, + .source = ".{ .a = 100, .b = 20 }", + .errors = &errors, + }), + ); try std.testing.expectEqual( end, try fromSlice(Struct, .{ .gpa = gpa, + .arena = arena, .source = ".{ .a = 100, .b = 20 }", - .errors = null, + .errors = &errors, }), ); try std.testing.expectEqual( + try fromZoirNoAlloc(Struct, .{ + .arena = arena, + .ast = &ast, + .zoir = &zoir, + .errors = &errors, + }), end, - try fromSliceAlloc(Struct, .{ - .gpa = gpa, - .arena = arena, - .source = ".{ .a = 100, .b = 20 }", - .errors = null, - }), ); try std.testing.expectEqual( try fromZoir(Struct, .{ - .ast = &ast, - .zoir = &zoir, - .errors = null, - }), - end, - ); - try std.testing.expectEqual( - try fromZoirAlloc(Struct, .{ .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = null, + .errors = &errors, }), end, ); -- 2.54.0 From 3b779c2e276d4b22a8d398b36379c173a607fa21 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Wed, 12 Aug 2026 15:04:31 -0700 Subject: [PATCH 16/26] Add decltests for common functions --- lib/std/zon/parse.zig | 99 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 7636040cdd64055e99f34e4470fd707fde60312f..c6aed1d58ea00e35cac085a8fb5f8b7e1a2f7108 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -224,6 +224,44 @@ pub fn fromSlice(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZo return value; } +test fromSlice { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const TextureOptions = struct { + const AddressMode = enum { clamp, reflect, wrap, zero }; + address_mode: struct { u: AddressMode = .wrap, v: AddressMode = .wrap }, + mipmaps: bool = true, + premultiply: bool = true, + }; + + const source = + \\.{ + \\ .address_mode = .{ .u = .clamp }, + \\ .mipmaps = false, + \\} + ; + + var errors: Errors = .empty; + const options = fromSlice(TextureOptions, .{ + .gpa = gpa, + .arena = arena, + .source = source, + .errors = &errors, + }) catch |err| { + errors.log("texture_options.zon", err); + return err; + }; + + try std.testing.expectEqualDeep(TextureOptions{ + .address_mode = .{ .u = .clamp, .v = .wrap }, + .mipmaps = false, + .premultiply = true, + }, options); +} + /// Like `fromSlice` but the result type may not contain pointers, allowing it to outlive /// `options.arena`. pub fn fromSliceNoAlloc(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { @@ -244,6 +282,67 @@ pub fn updateFromSlice( try fromSliceInner(T, value, true, options); } +test updateFromSlice { + const gpa = std.testing.allocator; + var arena_allocator: ArenaAllocator = .init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const MyTextEditorConfig = struct { + indentation: enum { spaces, tabs }, + columns: []const u16, + dark_mode: enum { auto, dark, light }, + theme: []const u8, + }; + + const global_config = + \\.{ + \\ .indentation = .spaces, + \\ .columns = .{ 100 }, + \\ .dark_mode = .dark, + \\} + ; + const project_config = + \\.{ + \\ .indentation = .tabs, + \\} + ; + + var config: MyTextEditorConfig = .{ + .indentation = .spaces, + .columns = &.{}, + .dark_mode = .auto, + .theme = "default", + }; + + var errors: Errors = .empty; + updateFromSlice(MyTextEditorConfig, &config, .{ + .gpa = gpa, + .arena = arena, + .source = global_config, + .errors = &errors, + }) catch |err| { + errors.log("global_config.zon", err); + return err; + }; + updateFromSlice(MyTextEditorConfig, &config, .{ + .gpa = gpa, + .arena = arena, + .source = project_config, + .errors = &errors, + }) catch |err| { + errors.log("project_config.zon", err); + return err; + }; + + try std.testing.expectEqualDeep(MyTextEditorConfig{ + .indentation = .tabs, + .columns = &.{100}, + .dark_mode = .dark, + .theme = "default", + }, config); +} + /// Like `updateFromSlice` but the `T` may not contain pointers, allowing `value` to outlive /// `options.arena`. pub fn updateFromSliceNoAlloc( -- 2.54.0 From 472a0305b82890592095f98904eca4a1a6a4ef06 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Wed, 12 Aug 2026 15:09:25 -0700 Subject: [PATCH 17/26] Rename all option structs to `Options` --- lib/std/zon/parse.zig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index c6aed1d58ea00e35cac085a8fb5f8b7e1a2f7108..48db9ec015b3e57fab9f4697613682ad3d299053 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -33,14 +33,14 @@ pub const Error = struct { node_or_offset: u32, notes: []Note, - const InitOptions = struct { + const Options = struct { msg: []const u8, token: Ast.OptionalTokenIndex, node_or_offset: u32, notes: []Note, }; - fn init(ast: *const Ast, options: InitOptions) Error { + fn init(ast: *const Ast, options: Error.Options) Error { return .{ .msg = options.msg, .loc = astLoc(ast, options.token, options.node_or_offset), @@ -74,13 +74,13 @@ pub const Error = struct { /// Otherwise, this is a byte offset into `token`. node_or_offset: u32, - const InitOptions = struct { + const Options = struct { msg: []const u8, token: Ast.OptionalTokenIndex, node_or_offset: u32, }; - fn init(ast: *const Ast, options: @This().InitOptions) @This() { + fn init(ast: *const Ast, options: Note.Options) @This() { return .{ .msg = options.msg, .loc = astLoc(ast, options.token, options.node_or_offset), @@ -200,7 +200,7 @@ pub const Errors = struct { }; }; -pub const FromSliceOptions = struct { +pub const Options = struct { /// Used for scratch allocations. gpa: Allocator, /// Used for allocating errors and results. @@ -218,7 +218,7 @@ pub const FromSliceOptions = struct { /// /// Human readable errors are written to `options.errors`. `options.arena` owns the errors, and any /// pointers that are part of `T`. -pub fn fromSlice(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { +pub fn fromSlice(T: type, options: Options) error{ OutOfMemory, ParseZon }!T { var value: T = undefined; try fromSliceInner(T, &value, false, options); return value; @@ -264,7 +264,7 @@ test fromSlice { /// Like `fromSlice` but the result type may not contain pointers, allowing it to outlive /// `options.arena`. -pub fn fromSliceNoAlloc(T: type, options: FromSliceOptions) error{ OutOfMemory, ParseZon }!T { +pub fn fromSliceNoAlloc(T: type, options: Options) error{ OutOfMemory, ParseZon }!T { comptime assert(!requiresAllocator(T)); return fromSlice(T, options); } @@ -277,7 +277,7 @@ pub fn fromSliceNoAlloc(T: type, options: FromSliceOptions) error{ OutOfMemory, pub fn updateFromSlice( T: type, value: *T, - options: FromSliceOptions, + options: Options, ) error{ OutOfMemory, ParseZon }!void { try fromSliceInner(T, value, true, options); } @@ -348,7 +348,7 @@ test updateFromSlice { pub fn updateFromSliceNoAlloc( T: type, value: *T, - options: FromSliceOptions, + options: Options, ) error{ OutOfMemory, ParseZon }!void { comptime assert(!requiresAllocator(T)); try fromSliceInner(T, value, true, options); @@ -358,7 +358,7 @@ fn fromSliceInner( T: type, value: *T, initialized: bool, - options: FromSliceOptions, + options: Options, ) error{ OutOfMemory, ParseZon }!void { var errors: std.ArrayList(Error) = .empty; defer options.errors.items = errors.items; -- 2.54.0 From a959eadb9d0fb48c7959e5a3cd894f3d81025900 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Wed, 12 Aug 2026 15:16:02 -0700 Subject: [PATCH 18/26] Documentation cleanup --- lib/std/zon/parse.zig | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 48db9ec015b3e57fab9f4697613682ad3d299053..0fe7be37161f75776a1cb9fde739cd25ac041770 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -214,10 +214,7 @@ pub const Options = struct { ignore_unknown_fields: bool = false, }; -/// Parses the given slice as ZON. -/// -/// Human readable errors are written to `options.errors`. `options.arena` owns the errors, and any -/// pointers that are part of `T`. +/// Parses the given slice as ZON, writing any errors to `options.errors`. pub fn fromSlice(T: type, options: Options) error{ OutOfMemory, ParseZon }!T { var value: T = undefined; try fromSliceInner(T, &value, false, options); @@ -269,11 +266,12 @@ pub fn fromSliceNoAlloc(T: type, options: Options) error{ OutOfMemory, ParseZon return fromSlice(T, options); } -/// Similar to `fromSlice`, but updates `value` in place. `value` must be initialized before this -/// call, fields not specified in ZON are left unchanged. +/// Similar to `fromSlice`, but updates the existing contents of `value` in place. Fields +/// unspecified by ZON are left unchanged. /// -/// Whenever a slice or an untagged union is specified, it completely overwites the previous value. -/// On error, `value` remains valid, but may be partially updated. +/// Assumes that `value` points to a fully initialized value prior to this call. Slices and untagged +/// unions are replaced in their entirey when specified. On error, `value` remains valid, but may be +/// partially updated. pub fn updateFromSlice( T: type, value: *T, -- 2.54.0 From db1189f519dc54ba43e8d9dc724197e745304c5f Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sat, 26 Sep 2026 12:45:34 -0700 Subject: [PATCH 19/26] Remove no longer necessary skip test --- lib/std/zon/parse.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 0fe7be37161f75776a1cb9fde739cd25ac041770..b05d6d48f7b6eb94e08e8ce91fd7c15043523e40 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -2197,8 +2197,6 @@ test "std.zon tuples" { // Test sizes 0 to 3 since small sizes get parsed differently test "std.zon arrays and slices" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/20881 - const gpa = std.testing.allocator; var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); -- 2.54.0 From 3fc23b2c607d23c4e6423d44b88a246502933d45 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sat, 26 Sep 2026 12:51:47 -0700 Subject: [PATCH 20/26] Explicitly reference tests on errors They'll run either way since `Errors` is referenced, but it's nice to be explicit. --- lib/std/zon/parse.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index b05d6d48f7b6eb94e08e8ce91fd7c15043523e40..0d750dff210d4f7efe850d34f675b2798108527b 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -5093,3 +5093,7 @@ test "std.zon variants" { ); } } + +test { + _ = Errors; +} -- 2.54.0 From b80b26c0eaafa8d028aea72a56e9bd1c19d42b32 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sat, 26 Sep 2026 13:11:28 -0700 Subject: [PATCH 21/26] Remove unecessary inline for --- lib/std/zon/parse.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 0d750dff210d4f7efe850d34f675b2798108527b..9a13d85d98ba8b543908d40c578db60e03907a40 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -841,7 +841,7 @@ const Parser = struct { } // Parse the elements and return the array - inline for (out, 0..) |*elem, i| { + for (out, 0..) |*elem, i| { try self.parseExprInto(nodes.at(@intCast(i)), elem, false); } -- 2.54.0 From 9cbd85b3410952b1f931849044f707222df52270 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sun, 27 Sep 2026 03:48:48 -0700 Subject: [PATCH 22/26] Fix tests, remove error arg from formatters --- lib/std/zon/parse.zig | 364 ++++++++++++++++-------------------------- 1 file changed, 135 insertions(+), 229 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 9a13d85d98ba8b543908d40c578db60e03907a40..6011f768f6a681da686c46eaeb585e6ddcf6970e 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -98,12 +98,8 @@ pub const Errors = struct { pub const empty: Errors = .{ .items = &.{} }; /// Log the failure with `std.log.err`. - pub fn log( - self: *const Errors, - path: []const u8, - err: error{ OutOfMemory, ParseZon }, - ) void { - std.log.err("{f}", .{self.fmt(path, err)}); + pub fn log(self: *const Errors, path: []const u8) void { + std.log.err("{f}", .{self.fmt(path)}); } test log { @@ -123,20 +119,19 @@ pub const Errors = struct { .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", .errors = &errors, - }) catch |err| b: { - errors.log("input.zon", err); - break :b .{ .foo = 0, .bar = 0 }; + }) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ParseZon => b: { + errors.log("input.zon"); + break :b .{ .foo = 0, .bar = 0 }; + }, }; _ = parsed; } /// Log the failure with `std.log.err`, and then terminate the process with exit code 1. - pub fn fatal( - self: *const Errors, - path: []const u8, - err: error{ OutOfMemory, ParseZon }, - ) noreturn { - std.process.fatal("{f}", .{self.fmt(path, err)}); + pub fn fatal(self: *const Errors, path: []const u8) noreturn { + std.process.fatal("{f}", .{self.fmt(path)}); } test fatal { @@ -156,29 +151,25 @@ pub const Errors = struct { .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", .errors = &errors, - }) catch |err| errors.fatal("input.zon", err); + }) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ParseZon => errors.fatal("input.zon"), + }; _ = parsed; } - pub fn fmt( - self: *const Errors, - path: []const u8, - err: error{ OutOfMemory, ParseZon }, - ) Formatter { + pub fn fmt(self: *const Errors, path: []const u8) Formatter { return .{ .items = self.items, .path = path, - .err = err, }; } pub const Formatter = struct { path: []const u8, items: []const Error, - err: error{ OutOfMemory, ParseZon }, pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - try w.print("{s}: {}\n", .{ self.path, self.err }); for (self.items) |e| { try w.print("{s}:", .{self.path}); try w.print("{d}:{d}: error: {s}\n", .{ @@ -247,9 +238,12 @@ test fromSlice { .arena = arena, .source = source, .errors = &errors, - }) catch |err| { - errors.log("texture_options.zon", err); - return err; + }) catch |err| switch (err) { + error.OutOfMemory => return err, + error.ParseZon => { + errors.log("texture_options.zon"); + return err; + }, }; try std.testing.expectEqualDeep(TextureOptions{ @@ -319,18 +313,24 @@ test updateFromSlice { .arena = arena, .source = global_config, .errors = &errors, - }) catch |err| { - errors.log("global_config.zon", err); - return err; + }) catch |err| switch (err) { + error.OutOfMemory => return err, + error.ParseZon => { + errors.log("global_config.zon"); + return err; + }, }; updateFromSlice(MyTextEditorConfig, &config, .{ .gpa = gpa, .arena = arena, .source = project_config, .errors = &errors, - }) catch |err| { - errors.log("project_config.zon", err); - return err; + }) catch |err| switch (err) { + error.OutOfMemory => return err, + error.ParseZon => { + errors.log("project_config.zon"); + return err; + }, }; try std.testing.expectEqualDeep(MyTextEditorConfig{ @@ -1395,10 +1395,9 @@ test "std.zon ast errors" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:13: error: expected ',' after initializer \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } test "std.zon comments" { @@ -1431,10 +1430,9 @@ test "std.zon comments" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected expression, found 'a document comment' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -1452,9 +1450,8 @@ test "std.zon failure/oom formatting" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } test "std.zon fromSlice syntax error" { @@ -1472,6 +1469,10 @@ test "std.zon fromSlice syntax error" { .errors = &errors, }), ); + try std.testing.expectFmt( + \\input.zon:1:3: error: expected expression, found 'EOF' + \\ + , "{f}", .{errors.fmt("input.zon")}); } test "std.zon optional" { @@ -1609,11 +1610,10 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:4: error: unexpected field 'z' \\input.zon:1:4: note: supported: 'x', 'y' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Explicit void field @@ -1629,10 +1629,9 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:6: error: expected type 'void' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Extra field @@ -1648,10 +1647,9 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // No fields @@ -1667,10 +1665,9 @@ test "std.zon unions" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Enum literals cannot coerce into untagged unions @@ -1683,10 +1680,9 @@ test "std.zon unions" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Unknown field for enum literal coercion @@ -1699,11 +1695,10 @@ test "std.zon unions" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: unexpected field 'y' \\input.zon:1:2: note: supported: 'x' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Non void field for enum literal coercion @@ -1716,10 +1711,9 @@ test "std.zon unions" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -1796,11 +1790,10 @@ test "std.zon structs" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:12: error: unexpected field 'z' \\input.zon:1:12: note: supported: 'x', 'y' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Duplicate field @@ -1816,11 +1809,10 @@ test "std.zon structs" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:4: error: duplicate struct field name \\input.zon:1:12: note: duplicate name here \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Ignore unknown fields @@ -1849,11 +1841,10 @@ test "std.zon structs" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:4: error: unexpected field 'x' \\input.zon:1:4: note: none expected \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Missing field @@ -1869,10 +1860,9 @@ test "std.zon structs" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: missing required field y \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Default field @@ -1910,10 +1900,9 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:18: error: cannot initialize comptime field \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Enum field (regression test, we were previously getting the field name in an @@ -1953,11 +1942,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Arrays @@ -1970,11 +1958,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Slices @@ -1987,11 +1974,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Tuples @@ -2004,11 +1990,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Nested @@ -2021,11 +2006,10 @@ test "std.zon structs" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:9: error: types are not available in ZON \\input.zon:1:9: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } } @@ -2102,10 +2086,9 @@ test "std.zon tuples" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:14: error: index 2 outside of tuple length 2 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Extra field @@ -2121,10 +2104,9 @@ test "std.zon tuples" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: missing tuple field with index 1 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Tuple with unexpected field names @@ -2140,10 +2122,9 @@ test "std.zon tuples" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected tuple \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Struct with missing field names @@ -2159,10 +2140,9 @@ test "std.zon tuples" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected struct \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Comptime field @@ -2188,10 +2168,9 @@ test "std.zon tuples" { }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:9: error: cannot initialize comptime field \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2378,10 +2357,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: index 0 outside of array of length 0 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Expect 1 find 2 @@ -2396,10 +2374,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:8: error: index 1 outside of array of length 1 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Expect 2 find 1 @@ -2414,10 +2391,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected 2 array elements; found 1 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Expect 3 find 0 @@ -2432,10 +2408,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected 3 array elements; found 0 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Wrong inner type @@ -2452,10 +2427,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Slice @@ -2470,10 +2444,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2491,10 +2464,9 @@ test "std.zon arrays and slices" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Slice @@ -2504,10 +2476,9 @@ test "std.zon arrays and slices" { fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2518,10 +2489,9 @@ test "std.zon arrays and slices" { fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: pointers are not available in ZON \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2573,10 +2543,9 @@ test "std.zon string literal" { fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -2585,10 +2554,9 @@ test "std.zon string literal" { fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2609,10 +2577,9 @@ test "std.zon string literal" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -2626,10 +2593,9 @@ test "std.zon string literal" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2666,10 +2632,9 @@ test "std.zon string literal" { fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -2678,10 +2643,9 @@ test "std.zon string literal" { fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2692,10 +2656,9 @@ test "std.zon string literal" { fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected string \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Expecting string literal, getting an incompatible tuple @@ -2705,10 +2668,9 @@ test "std.zon string literal" { fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Invalid string literal @@ -2718,10 +2680,9 @@ test "std.zon string literal" { fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: invalid escape character: 'a' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Slice wrong child type @@ -2732,10 +2693,9 @@ test "std.zon string literal" { fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -2744,10 +2704,9 @@ test "std.zon string literal" { fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .errors = &errors }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2764,10 +2723,9 @@ test "std.zon string literal" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -2781,10 +2739,9 @@ test "std.zon string literal" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2885,11 +2842,10 @@ test "std.zon enum literals" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: unexpected enum literal 'qux' \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Bad tag that's too long for parser @@ -2904,11 +2860,10 @@ test "std.zon enum literals" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: unexpected enum literal 'foobarbaz' \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Bad type @@ -2923,10 +2878,9 @@ test "std.zon enum literals" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected enum literal \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Test embedded nulls in an identifier @@ -2941,10 +2895,9 @@ test "std.zon enum literals" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: identifier cannot contain null bytes \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -2981,12 +2934,11 @@ test "std.zon parse bool" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: invalid expression \\input.zon:1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\input.zon:1:2: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(bool, .{ @@ -2996,10 +2948,9 @@ test "std.zon parse bool" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -3142,10 +3093,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'i66' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i66, .{ @@ -3155,10 +3105,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'i66' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Test parsing whole number floats as integers @@ -3278,10 +3227,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: invalid digit 'a' for decimal base \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Failing to parse as int @@ -3293,10 +3241,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Failing because an int is out of range @@ -3308,10 +3255,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Failing because a negative int is out of range @@ -3323,10 +3269,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'i8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Failing because an unsigned int is negative @@ -3338,10 +3283,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Failing because a float is non-whole @@ -3353,10 +3297,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Failing because a float is negative @@ -3368,10 +3311,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Negative integer zero @@ -3383,12 +3325,11 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: integer literal '-0' is ambiguous \\input.zon:1:2: note: use '0' for an integer zero \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Negative integer zero casted to float @@ -3400,12 +3341,11 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: integer literal '-0' is ambiguous \\input.zon:1:2: note: use '0' for an integer zero \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Negative float 0 is allowed @@ -3433,10 +3373,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -3450,10 +3389,9 @@ test "std.zon parse int" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Invalid int literal @@ -3465,10 +3403,9 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:3: error: invalid digit 'g' for hex base \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Notes on invalid int literal @@ -3480,11 +3417,10 @@ test "std.zon parse int" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: number '0123' has leading zero \\input.zon:1:1: note: use '0o' prefix for octal literals \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -3503,10 +3439,9 @@ test "std.zon negative char" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i16, .{ @@ -3516,10 +3451,9 @@ test "std.zon negative char" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -3705,10 +3639,9 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // nan as int not allowed @@ -3720,10 +3653,9 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // nan as int not allowed @@ -3735,10 +3667,9 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // inf as int not allowed @@ -3750,10 +3681,9 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // -inf as int not allowed @@ -3765,10 +3695,9 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Bad identifier as float @@ -3780,12 +3709,11 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: invalid expression \\input.zon:1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\input.zon:1:1: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -3796,10 +3724,9 @@ test "std.zon parse float" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Non float as float @@ -3814,10 +3741,9 @@ test "std.zon parse float" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type 'f32' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -4057,10 +3983,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:2: error: expected 2 array elements; found 1 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Too many fields @@ -4075,10 +4000,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:13: error: index 2 outside of array of length 2 \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Wrong type fields @@ -4093,10 +4017,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:8: error: expected type 'f32' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Wrong type @@ -4111,10 +4034,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '@Vector(3, u8)' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Elements should get freed on error @@ -4129,10 +4051,9 @@ test "std.zon vector" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:6: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -4324,10 +4245,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?u8' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4341,10 +4261,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?f32' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4358,10 +4277,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?@Vector(3, u8)' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4375,10 +4293,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected type '?bool' \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4392,10 +4309,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional struct \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4409,10 +4325,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional tuple \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4426,10 +4341,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional union \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4443,10 +4357,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4460,10 +4373,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4477,10 +4389,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4494,10 +4405,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4511,10 +4421,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional string \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } { @@ -4528,10 +4437,9 @@ test "std.zon add pointers" { }), ); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:1: error: expected optional enum literal \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } } @@ -4837,10 +4745,9 @@ test "std.zon update optionals" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:12: error: missing required field qux \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Updating an optional that starts out non-null should preserve any values we leave off. It's @@ -4966,10 +4873,9 @@ test "std.zon update unions" { .errors = &errors, })); try std.testing.expectFmt( - \\input.zon: error.ParseZon \\input.zon:1:12: error: missing required field qux \\ - , "{f}", .{errors.fmt("input.zon", error.ParseZon)}); + , "{f}", .{errors.fmt("input.zon")}); } // Updating a union should preseve any sub-fields that we left off. It's also okay to leave off -- 2.54.0 From 20fd6977376c0f159a15a8b6264480f9875436c1 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sun, 27 Sep 2026 04:18:52 -0700 Subject: [PATCH 23/26] Better API for reporting OOM --- lib/std/zon/parse.zig | 122 ++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 46 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 6011f768f6a681da686c46eaeb585e6ddcf6970e..d48e1be0391dec49e5483a8303ae5011914a00c7 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -94,8 +94,12 @@ pub const Error = struct { /// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user. pub const Errors = struct { items: []const Error, + oom: bool, - pub const empty: Errors = .{ .items = &.{} }; + pub const empty: Errors = .{ + .items = &.{}, + .oom = false, + }; /// Log the failure with `std.log.err`. pub fn log(self: *const Errors, path: []const u8) void { @@ -119,12 +123,9 @@ pub const Errors = struct { .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", .errors = &errors, - }) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.ParseZon => b: { - errors.log("input.zon"); - break :b .{ .foo = 0, .bar = 0 }; - }, + }) catch b: { + errors.log("input.zon"); + break :b .{ .foo = 0, .bar = 0 }; }; _ = parsed; } @@ -151,16 +152,14 @@ pub const Errors = struct { .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", .errors = &errors, - }) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.ParseZon => errors.fatal("input.zon"), - }; + }) catch errors.fatal("input.zon"); _ = parsed; } pub fn fmt(self: *const Errors, path: []const u8) Formatter { return .{ .items = self.items, + .oom = self.oom, .path = path, }; } @@ -168,8 +167,12 @@ pub const Errors = struct { pub const Formatter = struct { path: []const u8, items: []const Error, + oom: bool, pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { + if (self.oom) { + try w.print("{s}: out of memory", .{self.path}); + } for (self.items) |e| { try w.print("{s}:", .{self.path}); try w.print("{d}:{d}: error: {s}\n", .{ @@ -189,6 +192,11 @@ pub const Errors = struct { } } }; + + fn checkOom(self: *Errors, result: anytype) @TypeOf(result) { + self.oom |= result == error.OutOfMemory; + return result; + } }; pub const Options = struct { @@ -238,12 +246,9 @@ test fromSlice { .arena = arena, .source = source, .errors = &errors, - }) catch |err| switch (err) { - error.OutOfMemory => return err, - error.ParseZon => { - errors.log("texture_options.zon"); - return err; - }, + }) catch |err| { + errors.log("texture_options.zon"); + return err; }; try std.testing.expectEqualDeep(TextureOptions{ @@ -313,24 +318,18 @@ test updateFromSlice { .arena = arena, .source = global_config, .errors = &errors, - }) catch |err| switch (err) { - error.OutOfMemory => return err, - error.ParseZon => { - errors.log("global_config.zon"); - return err; - }, + }) catch |err| { + errors.log("global_config.zon"); + return err; }; updateFromSlice(MyTextEditorConfig, &config, .{ .gpa = gpa, .arena = arena, .source = project_config, .errors = &errors, - }) catch |err| switch (err) { - error.OutOfMemory => return err, - error.ParseZon => { - errors.log("project_config.zon"); - return err; - }, + }) catch |err| { + errors.log("project_config.zon"); + return err; }; try std.testing.expectEqualDeep(MyTextEditorConfig{ @@ -358,23 +357,32 @@ fn fromSliceInner( initialized: bool, options: Options, ) error{ OutOfMemory, ParseZon }!void { + options.errors.oom = false; var errors: std.ArrayList(Error) = .empty; defer options.errors.items = errors.items; - var ast = try std.zig.Ast.parse(options.gpa, options.source, .{ .mode = .zon }); + var ast = try options.errors.checkOom(std.zig.Ast.parse( + options.gpa, + options.source, + .{ .mode = .zon }, + )); defer ast.deinit(options.gpa); - var zoir = try ZonGen.generate(options.gpa, ast, .{ .parse_str_lits = false }); + var zoir = try options.errors.checkOom(ZonGen.generate( + options.gpa, + ast, + .{ .parse_str_lits = false }, + )); defer zoir.deinit(options.gpa); - try inner(T, value, initialized, .{ + try options.errors.checkOom(inner(T, value, initialized, .{ .arena = options.arena, .ast = &ast, .zoir = &zoir, .node = .root, .errors = &errors, .ignore_unknown_fields = options.ignore_unknown_fields, - }); + })); } pub const FromZoirOptions = struct { @@ -431,15 +439,16 @@ fn fromZoirInner( initialized: bool, options: FromZoirOptions, ) error{ OutOfMemory, ParseZon }!void { + options.errors.oom = false; var errors: std.ArrayList(Error) = .empty; defer options.errors.items = errors.items; - return inner(T, value, initialized, .{ + return options.errors.checkOom(inner(T, value, initialized, .{ .arena = options.arena, .ast = options.ast, .zoir = options.zoir, .errors = &errors, .ignore_unknown_fields = options.ignore_unknown_fields, - }); + })); } const InnerOptions = struct { @@ -1441,17 +1450,38 @@ test "std.zon failure/oom formatting" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; - - try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{ - .gpa = .failing, - .arena = arena, - .source = "\"foo\"", - .errors = &errors, - })); - try std.testing.expectFmt( - \\ - , "{f}", .{errors.fmt("input.zon")}); + + { + var errors: Errors = .empty; + try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{ + .gpa = .failing, + .arena = arena, + .source = "\"foo\"", + .errors = &errors, + })); + try std.testing.expectFmt( + \\input.zon: out of memory + , "{f}", .{errors.fmt("input.zon")}); + } + + { + var ast = try std.zig.Ast.parse(gpa, "\"foo\"", .{ .mode = .zon }); + defer ast.deinit(gpa); + + var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); + defer zoir.deinit(gpa); + + var errors: Errors = .empty; + try std.testing.expectError(error.OutOfMemory, fromZoir([]const u8, .{ + .arena = .failing, + .ast = &ast, + .zoir = &zoir, + .errors = &errors, + })); + try std.testing.expectFmt( + \\input.zon: out of memory + , "{f}", .{errors.fmt("input.zon")}); + } } test "std.zon fromSlice syntax error" { -- 2.54.0 From 139666586ec5a59ff4d4e6973b857c058dc9b572 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 28 Sep 2026 00:05:30 -0700 Subject: [PATCH 24/26] Revert "Better API for reporting OOM" This reverts commit 20fd6977376c0f159a15a8b6264480f9875436c1. --- lib/std/zon/parse.zig | 122 ++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 76 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index d48e1be0391dec49e5483a8303ae5011914a00c7..6011f768f6a681da686c46eaeb585e6ddcf6970e 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -94,12 +94,8 @@ pub const Error = struct { /// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user. pub const Errors = struct { items: []const Error, - oom: bool, - pub const empty: Errors = .{ - .items = &.{}, - .oom = false, - }; + pub const empty: Errors = .{ .items = &.{} }; /// Log the failure with `std.log.err`. pub fn log(self: *const Errors, path: []const u8) void { @@ -123,9 +119,12 @@ pub const Errors = struct { .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", .errors = &errors, - }) catch b: { - errors.log("input.zon"); - break :b .{ .foo = 0, .bar = 0 }; + }) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ParseZon => b: { + errors.log("input.zon"); + break :b .{ .foo = 0, .bar = 0 }; + }, }; _ = parsed; } @@ -152,14 +151,16 @@ pub const Errors = struct { .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", .errors = &errors, - }) catch errors.fatal("input.zon"); + }) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ParseZon => errors.fatal("input.zon"), + }; _ = parsed; } pub fn fmt(self: *const Errors, path: []const u8) Formatter { return .{ .items = self.items, - .oom = self.oom, .path = path, }; } @@ -167,12 +168,8 @@ pub const Errors = struct { pub const Formatter = struct { path: []const u8, items: []const Error, - oom: bool, pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - if (self.oom) { - try w.print("{s}: out of memory", .{self.path}); - } for (self.items) |e| { try w.print("{s}:", .{self.path}); try w.print("{d}:{d}: error: {s}\n", .{ @@ -192,11 +189,6 @@ pub const Errors = struct { } } }; - - fn checkOom(self: *Errors, result: anytype) @TypeOf(result) { - self.oom |= result == error.OutOfMemory; - return result; - } }; pub const Options = struct { @@ -246,9 +238,12 @@ test fromSlice { .arena = arena, .source = source, .errors = &errors, - }) catch |err| { - errors.log("texture_options.zon"); - return err; + }) catch |err| switch (err) { + error.OutOfMemory => return err, + error.ParseZon => { + errors.log("texture_options.zon"); + return err; + }, }; try std.testing.expectEqualDeep(TextureOptions{ @@ -318,18 +313,24 @@ test updateFromSlice { .arena = arena, .source = global_config, .errors = &errors, - }) catch |err| { - errors.log("global_config.zon"); - return err; + }) catch |err| switch (err) { + error.OutOfMemory => return err, + error.ParseZon => { + errors.log("global_config.zon"); + return err; + }, }; updateFromSlice(MyTextEditorConfig, &config, .{ .gpa = gpa, .arena = arena, .source = project_config, .errors = &errors, - }) catch |err| { - errors.log("project_config.zon"); - return err; + }) catch |err| switch (err) { + error.OutOfMemory => return err, + error.ParseZon => { + errors.log("project_config.zon"); + return err; + }, }; try std.testing.expectEqualDeep(MyTextEditorConfig{ @@ -357,32 +358,23 @@ fn fromSliceInner( initialized: bool, options: Options, ) error{ OutOfMemory, ParseZon }!void { - options.errors.oom = false; var errors: std.ArrayList(Error) = .empty; defer options.errors.items = errors.items; - var ast = try options.errors.checkOom(std.zig.Ast.parse( - options.gpa, - options.source, - .{ .mode = .zon }, - )); + var ast = try std.zig.Ast.parse(options.gpa, options.source, .{ .mode = .zon }); defer ast.deinit(options.gpa); - var zoir = try options.errors.checkOom(ZonGen.generate( - options.gpa, - ast, - .{ .parse_str_lits = false }, - )); + var zoir = try ZonGen.generate(options.gpa, ast, .{ .parse_str_lits = false }); defer zoir.deinit(options.gpa); - try options.errors.checkOom(inner(T, value, initialized, .{ + try inner(T, value, initialized, .{ .arena = options.arena, .ast = &ast, .zoir = &zoir, .node = .root, .errors = &errors, .ignore_unknown_fields = options.ignore_unknown_fields, - })); + }); } pub const FromZoirOptions = struct { @@ -439,16 +431,15 @@ fn fromZoirInner( initialized: bool, options: FromZoirOptions, ) error{ OutOfMemory, ParseZon }!void { - options.errors.oom = false; var errors: std.ArrayList(Error) = .empty; defer options.errors.items = errors.items; - return options.errors.checkOom(inner(T, value, initialized, .{ + return inner(T, value, initialized, .{ .arena = options.arena, .ast = options.ast, .zoir = options.zoir, .errors = &errors, .ignore_unknown_fields = options.ignore_unknown_fields, - })); + }); } const InnerOptions = struct { @@ -1450,38 +1441,17 @@ test "std.zon failure/oom formatting" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - - { - var errors: Errors = .empty; - try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{ - .gpa = .failing, - .arena = arena, - .source = "\"foo\"", - .errors = &errors, - })); - try std.testing.expectFmt( - \\input.zon: out of memory - , "{f}", .{errors.fmt("input.zon")}); - } - - { - var ast = try std.zig.Ast.parse(gpa, "\"foo\"", .{ .mode = .zon }); - defer ast.deinit(gpa); - - var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); - defer zoir.deinit(gpa); - - var errors: Errors = .empty; - try std.testing.expectError(error.OutOfMemory, fromZoir([]const u8, .{ - .arena = .failing, - .ast = &ast, - .zoir = &zoir, - .errors = &errors, - })); - try std.testing.expectFmt( - \\input.zon: out of memory - , "{f}", .{errors.fmt("input.zon")}); - } + var errors: Errors = .empty; + + try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{ + .gpa = .failing, + .arena = arena, + .source = "\"foo\"", + .errors = &errors, + })); + try std.testing.expectFmt( + \\ + , "{f}", .{errors.fmt("input.zon")}); } test "std.zon fromSlice syntax error" { -- 2.54.0 From 58ce0f3d913dfe0cb91dc151c40128c433f41d98 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 28 Sep 2026 00:20:25 -0700 Subject: [PATCH 25/26] Rename `Errors` to `Diagnostics` to match other uses of this pattern in std --- lib/std/zon/parse.zig | 1051 ++++++++++++++++++++--------------------- 1 file changed, 502 insertions(+), 549 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 6011f768f6a681da686c46eaeb585e6ddcf6970e..c2422539c3e2bd8005c6f95bcfafc2ac8bf6e9e1 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -23,82 +23,14 @@ const ArenaAllocator = std.heap.ArenaAllocator; /// Rename when adding or removing support for a type. const valid_types = {}; -/// An error encountered while parsing ZON. -pub const Error = struct { - msg: []const u8, - loc: Ast.Location, - token: Ast.OptionalTokenIndex, - /// If `token == .none`, this is an `Ast.Node.Index`. - /// Otherwise, this is a byte offset into `token`. - node_or_offset: u32, - notes: []Note, - - const Options = struct { - msg: []const u8, - token: Ast.OptionalTokenIndex, - node_or_offset: u32, - notes: []Note, - }; - - fn init(ast: *const Ast, options: Error.Options) Error { - return .{ - .msg = options.msg, - .loc = astLoc(ast, options.token, options.node_or_offset), - .token = options.token, - .node_or_offset = options.node_or_offset, - .notes = options.notes, - }; - } - - fn astLoc( - ast: *const Ast, - maybe_token: Ast.OptionalTokenIndex, - node_or_offset: u32, - ) Ast.Location { - if (maybe_token.unwrap()) |token| { - var loc = ast.tokenLocation(0, token); - loc.column += node_or_offset; - return loc; - } else { - const ast_node: Ast.Node.Index = @fromBackingInt(@intCast(node_or_offset)); - const token = ast.nodeMainToken(ast_node); - return ast.tokenLocation(0, token); - } - } - - pub const Note = struct { - msg: []const u8, - loc: Ast.Location, - token: Ast.OptionalTokenIndex, - /// If `token == .none`, this is an `Ast.Node.Index`. - /// Otherwise, this is a byte offset into `token`. - node_or_offset: u32, - - const Options = struct { - msg: []const u8, - token: Ast.OptionalTokenIndex, - node_or_offset: u32, - }; - - fn init(ast: *const Ast, options: Note.Options) @This() { - return .{ - .msg = options.msg, - .loc = astLoc(ast, options.token, options.node_or_offset), - .token = options.token, - .node_or_offset = options.node_or_offset, - }; - } - }; -}; - /// Errors encountered while parsing ZON. See `log` and `fatal` for reporting errors to the user. -pub const Errors = struct { - items: []const Error, +pub const Diagnostics = struct { + errors: []const Error, - pub const empty: Errors = .{ .items = &.{} }; + pub const empty: Diagnostics = .{ .errors = &.{} }; /// Log the failure with `std.log.err`. - pub fn log(self: *const Errors, path: []const u8) void { + pub fn log(self: *const Diagnostics, path: []const u8) void { std.log.err("{f}", .{self.fmt(path)}); } @@ -107,7 +39,7 @@ pub const Errors = struct { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const MyType = struct { foo: u32, @@ -118,11 +50,11 @@ pub const Errors = struct { .gpa = gpa, .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", - .errors = &errors, + .diagnostics = &diagnostics, }) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.ParseZon => b: { - errors.log("input.zon"); + diagnostics.log("input.zon"); break :b .{ .foo = 0, .bar = 0 }; }, }; @@ -130,7 +62,7 @@ pub const Errors = struct { } /// Log the failure with `std.log.err`, and then terminate the process with exit code 1. - pub fn fatal(self: *const Errors, path: []const u8) noreturn { + pub fn fatal(self: *const Diagnostics, path: []const u8) noreturn { std.process.fatal("{f}", .{self.fmt(path)}); } @@ -139,7 +71,7 @@ pub const Errors = struct { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const MyType = struct { foo: u32, @@ -150,27 +82,44 @@ pub const Errors = struct { .gpa = gpa, .arena = arena, .source = ".{ .foo = 1, .bar = 2 }", - .errors = &errors, + .diagnostics = &diagnostics, }) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.ParseZon => errors.fatal("input.zon"), + error.ParseZon => diagnostics.fatal("input.zon"), }; _ = parsed; } - pub fn fmt(self: *const Errors, path: []const u8) Formatter { + /// Formats a human readable description of any errors. + pub fn fmt(self: *const Diagnostics, path: []const u8) Formatter { return .{ - .items = self.items, + .errors = self.errors, .path = path, }; } + fn astLoc( + ast: *const Ast, + maybe_token: Ast.OptionalTokenIndex, + node_or_offset: u32, + ) Ast.Location { + if (maybe_token.unwrap()) |token| { + var loc = ast.tokenLocation(0, token); + loc.column += node_or_offset; + return loc; + } else { + const ast_node: Ast.Node.Index = @fromBackingInt(@intCast(node_or_offset)); + const token = ast.nodeMainToken(ast_node); + return ast.tokenLocation(0, token); + } + } + pub const Formatter = struct { path: []const u8, - items: []const Error, + errors: []const Error, pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - for (self.items) |e| { + for (self.errors) |e| { try w.print("{s}:", .{self.path}); try w.print("{d}:{d}: error: {s}\n", .{ e.loc.line + 1, @@ -189,23 +138,74 @@ pub const Errors = struct { } } }; + + pub const Error = struct { + msg: []const u8, + loc: Ast.Location, + token: Ast.OptionalTokenIndex, + /// If `token == .none`, this is an `Ast.Node.Index`. + /// Otherwise, this is a byte offset into `token`. + node_or_offset: u32, + notes: []Note, + + const Options = struct { + msg: []const u8, + token: Ast.OptionalTokenIndex, + node_or_offset: u32, + notes: []Note, + }; + + fn init(ast: *const Ast, options: Error.Options) Error { + return .{ + .msg = options.msg, + .loc = astLoc(ast, options.token, options.node_or_offset), + .token = options.token, + .node_or_offset = options.node_or_offset, + .notes = options.notes, + }; + } + }; + + pub const Note = struct { + msg: []const u8, + loc: Ast.Location, + token: Ast.OptionalTokenIndex, + /// If `token == .none`, this is an `Ast.Node.Index`. + /// Otherwise, this is a byte offset into `token`. + node_or_offset: u32, + + const Options = struct { + msg: []const u8, + token: Ast.OptionalTokenIndex, + node_or_offset: u32, + }; + + fn init(ast: *const Ast, options: Note.Options) @This() { + return .{ + .msg = options.msg, + .loc = astLoc(ast, options.token, options.node_or_offset), + .token = options.token, + .node_or_offset = options.node_or_offset, + }; + } + }; }; pub const Options = struct { /// Used for scratch allocations. gpa: Allocator, - /// Used for allocating errors and results. + /// Used for allocating diagnostics and results. arena: Allocator, /// The ZON source to parse. source: [:0]const u8, /// When an error is returned from the parser, a human readable error description of the failure /// is stored here. - errors: *Errors, + diagnostics: *Diagnostics, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; -/// Parses the given slice as ZON, writing any errors to `options.errors`. +/// Parses the given slice as ZON, writing any errors to `options.diagnostics`. pub fn fromSlice(T: type, options: Options) error{ OutOfMemory, ParseZon }!T { var value: T = undefined; try fromSliceInner(T, &value, false, options); @@ -232,16 +232,16 @@ test fromSlice { \\} ; - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const options = fromSlice(TextureOptions, .{ .gpa = gpa, .arena = arena, .source = source, - .errors = &errors, + .diagnostics = &diagnostics, }) catch |err| switch (err) { error.OutOfMemory => return err, error.ParseZon => { - errors.log("texture_options.zon"); + diagnostics.log("texture_options.zon"); return err; }, }; @@ -307,16 +307,16 @@ test updateFromSlice { .theme = "default", }; - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; updateFromSlice(MyTextEditorConfig, &config, .{ .gpa = gpa, .arena = arena, .source = global_config, - .errors = &errors, + .diagnostics = &diagnostics, }) catch |err| switch (err) { error.OutOfMemory => return err, error.ParseZon => { - errors.log("global_config.zon"); + diagnostics.log("global_config.zon"); return err; }, }; @@ -324,11 +324,11 @@ test updateFromSlice { .gpa = gpa, .arena = arena, .source = project_config, - .errors = &errors, + .diagnostics = &diagnostics, }) catch |err| switch (err) { error.OutOfMemory => return err, error.ParseZon => { - errors.log("project_config.zon"); + diagnostics.log("project_config.zon"); return err; }, }; @@ -358,8 +358,8 @@ fn fromSliceInner( initialized: bool, options: Options, ) error{ OutOfMemory, ParseZon }!void { - var errors: std.ArrayList(Error) = .empty; - defer options.errors.items = errors.items; + var errors: std.ArrayList(Diagnostics.Error) = .empty; + defer options.diagnostics.errors = errors.items; var ast = try std.zig.Ast.parse(options.gpa, options.source, .{ .mode = .zon }); defer ast.deinit(options.gpa); @@ -378,7 +378,7 @@ fn fromSliceInner( } pub const FromZoirOptions = struct { - /// Used for allocating errors and results. + /// Used for allocating diagnostics and results. arena: Allocator, /// The AST for this ZON value. ast: *const Ast, @@ -388,7 +388,7 @@ pub const FromZoirOptions = struct { node: Zoir.Node.Index = .root, /// When an error is returned from the parser, a human readable error description of the failure /// is stored here. - errors: *Errors, + diagnostics: *Diagnostics, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, }; @@ -431,8 +431,8 @@ fn fromZoirInner( initialized: bool, options: FromZoirOptions, ) error{ OutOfMemory, ParseZon }!void { - var errors: std.ArrayList(Error) = .empty; - defer options.errors.items = errors.items; + var errors: std.ArrayList(Diagnostics.Error) = .empty; + defer options.diagnostics.errors = errors.items; return inner(T, value, initialized, .{ .arena = options.arena, .ast = options.ast, @@ -447,7 +447,7 @@ const InnerOptions = struct { ast: *const Ast, zoir: *const Zoir, node: Zoir.Node.Index = .root, - errors: *std.ArrayList(Error), + errors: *std.ArrayList(Diagnostics.Error), ignore_unknown_fields: bool, }; @@ -461,7 +461,7 @@ fn inner( if (options.zoir.hasCompileErrors()) { for (options.zoir.compile_errors) |e| { - var notes: std.ArrayList(Error.Note) = .empty; + var notes: std.ArrayList(Diagnostics.Note) = .empty; for (e.getNotes(options.zoir)) |note| { try notes.append(options.arena, .init(options.ast, .{ .msg = try options.arena.dupe(u8, note.msg.get(options.zoir)), @@ -515,7 +515,7 @@ const Parser = struct { arena: Allocator, ast: *const Ast, zoir: *const Zoir, - errors: *std.ArrayList(Error), + errors: *std.ArrayList(Diagnostics.Error), ignore_unknown_fields: bool, const ParseExprError = error{ ParseZon, OutOfMemory }; @@ -1086,7 +1086,7 @@ const Parser = struct { offset: u32, comptime fmt: []const u8, args: anytype, - notes: []Error.Note, + notes: []Diagnostics.Note, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); comptime assert(args.len > 0); @@ -1112,7 +1112,7 @@ const Parser = struct { fn failToken( self: Parser, - failure: Error, + failure: Diagnostics.Error, ) error{ OutOfMemory, ParseZon } { @branchHint(.cold); try self.errors.append(self.arena, failure); @@ -1160,7 +1160,7 @@ const Parser = struct { } else self.ast.nodeMainToken(node.getAstNode(self.zoir)); switch (@typeInfo(T)) { inline .@"struct", .@"union", .@"enum" => |info| { - var notes: std.ArrayList(Error.Note) = .empty; + var notes: std.ArrayList(Diagnostics.Note) = .empty; if (info.field_names.len == 0) { try notes.append(self.arena, .init(self.ast, .{ .token = .fromToken(token), @@ -1384,20 +1384,20 @@ test "std.zon ast errors" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; try std.testing.expectError( error.ParseZon, fromSliceNoAlloc(struct {}, .{ .gpa = gpa, .arena = arena, .source = ".{.x = 1 .y = 2}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:13: error: expected ',' after initializer \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } test "std.zon comments" { @@ -1405,7 +1405,7 @@ test "std.zon comments" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; try std.testing.expectEqual(@as(u8, 10), fromSliceNoAlloc(u8, .{ .gpa = gpa, @@ -1415,7 +1415,7 @@ test "std.zon comments" { \\10 // comment \\// comment , - .errors = &errors, + .diagnostics = &diagnostics, })); { @@ -1427,12 +1427,12 @@ test "std.zon comments" { \\10 // comment \\// comment , - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected expression, found 'a document comment' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -1441,17 +1441,17 @@ test "std.zon failure/oom formatting" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{ .gpa = .failing, .arena = arena, .source = "\"foo\"", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } test "std.zon fromSlice syntax error" { @@ -1459,20 +1459,20 @@ test "std.zon fromSlice syntax error" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; try std.testing.expectError( error.ParseZon, fromSliceNoAlloc(u8, .{ .gpa = gpa, .arena = arena, .source = ".{", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:3: error: expected expression, found 'EOF' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } test "std.zon optional" { @@ -1480,7 +1480,7 @@ test "std.zon optional" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Basic usage { @@ -1488,14 +1488,14 @@ test "std.zon optional" { .gpa = gpa, .arena = arena, .source = "null", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expect(none == null); const some = try fromSliceNoAlloc(?u32, .{ .gpa = gpa, .arena = arena, .source = "1", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expect(some.? == 1); } @@ -1506,14 +1506,14 @@ test "std.zon optional" { .gpa = gpa, .arena = arena, .source = "null", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expect(none == null); const some = try fromSlice(?[]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings("foo", some.?); } @@ -1524,7 +1524,7 @@ test "std.zon unions" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Unions { @@ -1535,28 +1535,28 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.5}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x); const tagged_y = try fromSliceNoAlloc(Tagged, .{ .gpa = gpa, .arena = arena, .source = ".{.@\"y y\" = true}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Tagged{ .@"y y" = true }, tagged_y); const tagged_z_shorthand = try fromSliceNoAlloc(Tagged, .{ .gpa = gpa, .arena = arena, .source = ".z", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand); const tagged_zz_shorthand = try fromSliceNoAlloc(Tagged, .{ .gpa = gpa, .arena = arena, .source = ".@\"z z\"", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand); @@ -1564,14 +1564,14 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.5}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expect(untagged_x.x == 1.5); const untagged_y = try fromSliceNoAlloc(Untagged, .{ .gpa = gpa, .arena = arena, .source = ".{.@\"y y\" = true}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expect(untagged_y.@"y y"); } @@ -1584,7 +1584,7 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.baz = false}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Union{ .baz = false }, noalloc); @@ -1592,7 +1592,7 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.bar = \"qux\"}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc); } @@ -1606,14 +1606,14 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.z=2.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:4: error: unexpected field 'z' \\input.zon:1:4: note: supported: 'x', 'y' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Explicit void field @@ -1625,13 +1625,13 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.x=1}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:6: error: expected type 'void' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Extra field @@ -1643,13 +1643,13 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.5, .y = true}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // No fields @@ -1661,13 +1661,13 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Enum literals cannot coerce into untagged unions @@ -1677,12 +1677,12 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".x", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Unknown field for enum literal coercion @@ -1692,13 +1692,13 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".y", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:2: error: unexpected field 'y' \\input.zon:1:2: note: supported: 'x' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Non void field for enum literal coercion @@ -1708,12 +1708,12 @@ test "std.zon unions" { .gpa = gpa, .arena = arena, .source = ".x", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:2: error: expected union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -1722,7 +1722,7 @@ test "std.zon structs" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Structs (various sizes tested since they're parsed differently) { @@ -1735,7 +1735,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec0{}, zero); @@ -1743,7 +1743,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.2}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec1{ .x = 1.2 }, one); @@ -1751,7 +1751,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.2, .y = 3.4}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 3.4 }, two); @@ -1759,7 +1759,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.2, .y = 3.4, .z = 5.6}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three); } @@ -1772,7 +1772,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed); } @@ -1786,14 +1786,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x=1.5, .z=2.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:12: error: unexpected field 'z' \\input.zon:1:12: note: supported: 'x', 'y' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Duplicate field @@ -1805,14 +1805,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x=1.5, .x=2.5, .x=3.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:4: error: duplicate struct field name \\input.zon:1:12: note: duplicate name here \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Ignore unknown fields @@ -1823,7 +1823,7 @@ test "std.zon structs" { .arena = arena, .source = ".{ .x = 1.0, .z = 3.0 }", .ignore_unknown_fields = true, - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec2{ .x = 1.0, .y = 2.0 }, parsed); } @@ -1837,14 +1837,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x=1.5, .z=2.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:4: error: unexpected field 'x' \\input.zon:1:4: note: none expected \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Missing field @@ -1856,13 +1856,13 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x=1.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: missing required field y \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Default field @@ -1872,7 +1872,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.2}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } @@ -1884,7 +1884,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.2}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); } @@ -1896,13 +1896,13 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{.x = 1.2, .y = 1.5}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\input.zon:1:18: error: cannot initialize comptime field \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Enum field (regression test, we were previously getting the field name in an @@ -1913,7 +1913,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{ .x = .x }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec0{ .x = .x }, parsed); } @@ -1925,7 +1925,7 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{ .@\"x x\" = .@\"x x\" }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec0{ .@"x x" = .@"x x" }, parsed); } @@ -1938,14 +1938,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = "Empty{}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Arrays @@ -1954,14 +1954,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = "[3]u8{1, 2, 3}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Slices @@ -1970,14 +1970,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = "[]u8{1, 2, 3}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Tuples @@ -1986,14 +1986,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = "Tuple{1, 2, 3}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\input.zon:1:1: error: types are not available in ZON \\input.zon:1:1: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Nested @@ -2002,14 +2002,14 @@ test "std.zon structs" { .gpa = gpa, .arena = arena, .source = ".{ .x = Tuple{1, 2, 3} }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\input.zon:1:9: error: types are not available in ZON \\input.zon:1:9: note: replace the type with '.' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } } @@ -2019,7 +2019,7 @@ test "std.zon tuples" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Structs (various sizes tested since they're parsed differently) { @@ -2032,7 +2032,7 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Tuple0{}, zero); @@ -2040,7 +2040,7 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{1.2}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Tuple1{1.2}, one); @@ -2048,7 +2048,7 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{1.2, true}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Tuple2{ 1.2, true }, two); @@ -2056,7 +2056,7 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{1.2, false, 3}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Tuple3{ 1.2, false, 3 }, three); } @@ -2068,7 +2068,7 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{\"hello\", \"world\"}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed); } @@ -2082,13 +2082,13 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{0.5, true, 123}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:14: error: index 2 outside of tuple length 2 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Extra field @@ -2100,13 +2100,13 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{0.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: missing tuple field with index 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Tuple with unexpected field names @@ -2118,13 +2118,13 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{.foo = 10.0}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: expected tuple \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Struct with missing field names @@ -2136,13 +2136,13 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{10.0}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: expected struct \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Comptime field @@ -2152,7 +2152,7 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{ 1.2 }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed); } @@ -2164,13 +2164,13 @@ test "std.zon tuples" { .gpa = gpa, .arena = arena, .source = ".{ 1.2, 1.5}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\input.zon:1:9: error: cannot initialize comptime field \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2180,7 +2180,7 @@ test "std.zon arrays and slices" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Literals { @@ -2190,7 +2190,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &@as([0]u8, .{}), &zero); @@ -2198,7 +2198,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a'}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &@as([1]u8, .{'a'}), &one); @@ -2206,7 +2206,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b'}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two); @@ -2214,7 +2214,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b',}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two_comma); @@ -2222,7 +2222,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, &three); @@ -2230,7 +2230,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = &errors, + .diagnostics = &diagnostics, }); const expected_sentinel: [3:'z']u8 = .{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, &expected_sentinel, &sentinel); @@ -2242,7 +2242,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero); @@ -2250,7 +2250,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a'}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &.{'a'}, one); @@ -2258,7 +2258,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b'}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two); @@ -2266,7 +2266,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b',}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma); @@ -2274,7 +2274,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three); @@ -2282,7 +2282,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = &errors, + .diagnostics = &diagnostics, }); const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel); @@ -2297,7 +2297,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{\"abc\"}", - .errors = &errors, + .diagnostics = &diagnostics, }); const expected: [1][]const u8 = .{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2309,7 +2309,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{\"abc\"}", - .errors = &errors, + .diagnostics = &diagnostics, }); const expected: []const []const u8 = &.{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -2324,7 +2324,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{1}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -2337,7 +2337,7 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{1}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -2353,13 +2353,13 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:3: error: index 0 outside of array of length 0 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Expect 1 find 2 @@ -2370,13 +2370,13 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b'}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:8: error: index 1 outside of array of length 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Expect 2 find 1 @@ -2387,13 +2387,13 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a'}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: expected 2 array elements; found 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Expect 3 find 0 @@ -2404,13 +2404,13 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: expected 3 array elements; found 0 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Wrong inner type @@ -2423,13 +2423,13 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:3: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Slice @@ -2440,13 +2440,13 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = ".{'a', 'b', 'c'}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:3: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2460,25 +2460,25 @@ test "std.zon arrays and slices" { .gpa = gpa, .arena = arena, .source = "'a'", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Slice { try std.testing.expectError( error.ParseZon, - fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "'a'", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2486,12 +2486,12 @@ test "std.zon arrays and slices" { { try std.testing.expectError( error.ParseZon, - fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = " &.{'a', 'b', 'c'}", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:3: error: pointers are not available in ZON \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2500,7 +2500,7 @@ test "std.zon string literal" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Basic string literal { @@ -2508,7 +2508,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"abc\"", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed); } @@ -2519,7 +2519,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"ab\\nc\"", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed); } @@ -2530,7 +2530,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"ab\\x00c\"", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed); } @@ -2540,23 +2540,23 @@ test "std.zon string literal" { { try std.testing.expectError( error.ParseZon, - fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\"abcd\"", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { try std.testing.expectError( error.ParseZon, - fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .errors = &errors }), + fromSlice([]u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\abcd", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2573,13 +2573,13 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"abcd\"", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -2589,13 +2589,13 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\\\\abcd", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2606,7 +2606,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"abc\"", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); @@ -2617,7 +2617,7 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\\\\abc", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings("abc", parsed); try std.testing.expectEqual(@as(u8, 0), parsed[3]); @@ -2629,23 +2629,23 @@ test "std.zon string literal" { { try std.testing.expectError( error.ParseZon, - fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .errors = &errors }), + fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\"foo\"", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { try std.testing.expectError( error.ParseZon, - fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .errors = &errors }), + fromSlice([:1]const u8, .{ .gpa = gpa, .arena = arena, .source = "\\\\foo", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2653,36 +2653,36 @@ test "std.zon string literal" { { try std.testing.expectError( error.ParseZon, - fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .errors = &errors }), + fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = "true", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected string \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Expecting string literal, getting an incompatible tuple { try std.testing.expectError( error.ParseZon, - fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .errors = &errors }), + fromSlice([]const u8, .{ .gpa = gpa, .arena = arena, .source = ".{false}", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:3: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Invalid string literal { try std.testing.expectError( error.ParseZon, - fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .errors = &errors }), + fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"\\a\"", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:3: error: invalid escape character: 'a' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Slice wrong child type @@ -2690,23 +2690,23 @@ test "std.zon string literal" { { try std.testing.expectError( error.ParseZon, - fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .errors = &errors }), + fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\"a\"", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { try std.testing.expectError( error.ParseZon, - fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .errors = &errors }), + fromSlice([]const i8, .{ .gpa = gpa, .arena = arena, .source = "\\\\a", .diagnostics = &diagnostics }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2719,13 +2719,13 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\"abc\"", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -2735,13 +2735,13 @@ test "std.zon string literal" { .gpa = gpa, .arena = arena, .source = "\\\\abc", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2775,7 +2775,7 @@ test "std.zon string literal" { \\ \\and this. \\} , - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings( "hello, world!\nthis is a multiline string!\n\n...", @@ -2792,7 +2792,7 @@ test "std.zon enum literals" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const Enum = enum { foo, @@ -2806,19 +2806,19 @@ test "std.zon enum literals" { .gpa = gpa, .arena = arena, .source = ".foo", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(Enum.bar, try fromSliceNoAlloc(Enum, .{ .gpa = gpa, .arena = arena, .source = ".bar", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(Enum.baz, try fromSliceNoAlloc(Enum, .{ .gpa = gpa, .arena = arena, .source = ".baz", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual( Enum.@"ab\nc", @@ -2826,7 +2826,7 @@ test "std.zon enum literals" { .gpa = gpa, .arena = arena, .source = ".@\"ab\\nc\"", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -2838,14 +2838,14 @@ test "std.zon enum literals" { .gpa = gpa, .arena = arena, .source = ".qux", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: unexpected enum literal 'qux' \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Bad tag that's too long for parser @@ -2856,14 +2856,14 @@ test "std.zon enum literals" { .gpa = gpa, .arena = arena, .source = ".@\"foobarbaz\"", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: unexpected enum literal 'foobarbaz' \\input.zon:1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Bad type @@ -2874,13 +2874,13 @@ test "std.zon enum literals" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Test embedded nulls in an identifier @@ -2891,13 +2891,13 @@ test "std.zon enum literals" { .gpa = gpa, .arena = arena, .source = ".@\"\\x00\"", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: identifier cannot contain null bytes \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2906,20 +2906,20 @@ test "std.zon parse bool" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Correct bools try std.testing.expectEqual(true, try fromSliceNoAlloc(bool, .{ .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(false, try fromSliceNoAlloc(bool, .{ .gpa = gpa, .arena = arena, .source = "false", - .errors = &errors, + .diagnostics = &diagnostics, })); // Errors @@ -2930,7 +2930,7 @@ test "std.zon parse bool" { .gpa = gpa, .arena = arena, .source = " foo", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( @@ -2938,19 +2938,19 @@ test "std.zon parse bool" { \\input.zon:1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\input.zon:1:2: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(bool, .{ .gpa = gpa, .arena = arena, .source = "123", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected type 'bool' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -2981,32 +2981,32 @@ test "std.zon parse int" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Test various numbers and types try std.testing.expectEqual(@as(u8, 10), try fromSliceNoAlloc(u8, .{ .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i16, 24), try fromSliceNoAlloc(i16, .{ .gpa = gpa, .arena = arena, .source = "24", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i14, -4), try fromSliceNoAlloc(i14, .{ .gpa = gpa, .arena = arena, .source = "-4", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i32, -123), try fromSliceNoAlloc(i32, .{ .gpa = gpa, .arena = arena, .source = "-123", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test limits @@ -3014,13 +3014,13 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "127", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i8, -128), try fromSliceNoAlloc(i8, .{ .gpa = gpa, .arena = arena, .source = "-128", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test characters @@ -3028,13 +3028,13 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "'a'", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(u8, 'z'), try fromSliceNoAlloc(u8, .{ .gpa = gpa, .arena = arena, .source = "'z'", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test big integers @@ -3044,7 +3044,7 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "36893488147419103231", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3053,7 +3053,7 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "368934_881_474191032_31", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3062,7 +3062,7 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "340282366920938463463374607431768211455", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -3073,7 +3073,7 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "36893488147419103231", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3082,7 +3082,7 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-36893488147419103232", - .errors = &errors, + .diagnostics = &diagnostics, }), ); { @@ -3090,24 +3090,24 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "36893488147419103232", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: type 'i66' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i66, .{ .gpa = gpa, .arena = arena, .source = "-36893488147419103233", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: type 'i66' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Test parsing whole number floats as integers @@ -3115,13 +3115,13 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-1.0", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i8, 123), try fromSliceNoAlloc(i8, .{ .gpa = gpa, .arena = arena, .source = "123.0", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test non-decimal integers @@ -3129,37 +3129,37 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "0xff", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i16, -0xff), try fromSliceNoAlloc(i16, .{ .gpa = gpa, .arena = arena, .source = "-0xff", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i16, 0o77), try fromSliceNoAlloc(i16, .{ .gpa = gpa, .arena = arena, .source = "0o77", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i16, -0o77), try fromSliceNoAlloc(i16, .{ .gpa = gpa, .arena = arena, .source = "-0o77", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i16, 0b11), try fromSliceNoAlloc(i16, .{ .gpa = gpa, .arena = arena, .source = "0b11", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i16, -0b11), try fromSliceNoAlloc(i16, .{ .gpa = gpa, .arena = arena, .source = "-0b11", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test non-decimal big integers @@ -3167,55 +3167,55 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "0x1ffffffffffffffff", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, .arena = arena, .source = "0x1ffffffffffffffff", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, .arena = arena, .source = "-0x1ffffffffffffffff", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSliceNoAlloc(u65, .{ .gpa = gpa, .arena = arena, .source = "0o3777777777777777777777", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, .arena = arena, .source = "0o3777777777777777777777", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, .arena = arena, .source = "-0o3777777777777777777777", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSliceNoAlloc(u65, .{ .gpa = gpa, .arena = arena, .source = "0b11111111111111111111111111111111111111111111111111111111111111111", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, .arena = arena, .source = "0b11111111111111111111111111111111111111111111111111111111111111111", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSliceNoAlloc(i66, .{ .gpa = gpa, .arena = arena, .source = "-0b11111111111111111111111111111111111111111111111111111111111111111", - .errors = &errors, + .diagnostics = &diagnostics, })); // Number with invalid character in the middle @@ -3224,12 +3224,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "32a32", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:3: error: invalid digit 'a' for decimal base \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Failing to parse as int @@ -3238,12 +3238,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Failing because an int is out of range @@ -3252,12 +3252,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "256", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Failing because a negative int is out of range @@ -3266,12 +3266,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-129", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: type 'i8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Failing because an unsigned int is negative @@ -3280,12 +3280,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-1", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Failing because a float is non-whole @@ -3294,12 +3294,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "1.5", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Failing because a float is negative @@ -3308,12 +3308,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-1.0", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: type 'u8' cannot represent value \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Negative integer zero @@ -3322,14 +3322,14 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-0", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:2: error: integer literal '-0' is ambiguous \\input.zon:1:2: note: use '0' for an integer zero \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Negative integer zero casted to float @@ -3338,14 +3338,14 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-0", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:2: error: integer literal '-0' is ambiguous \\input.zon:1:2: note: use '0' for an integer zero \\input.zon:1:2: note: use '-0.0' for a floating-point signed zero \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Negative float 0 is allowed @@ -3354,14 +3354,14 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "-0.0", - .errors = &errors, + .diagnostics = &diagnostics, })), ); try std.testing.expect(std.math.isPositiveZero(try fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "0.0", - .errors = &errors, + .diagnostics = &diagnostics, }))); // Double negation is not allowed @@ -3370,12 +3370,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "--2", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -3385,13 +3385,13 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "--2.0", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Invalid int literal @@ -3400,12 +3400,12 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "0xg", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:3: error: invalid digit 'g' for hex base \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Notes on invalid int literal @@ -3414,13 +3414,13 @@ test "std.zon parse int" { .gpa = gpa, .arena = arena, .source = "0123", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: number '0123' has leading zero \\input.zon:1:1: note: use '0o' prefix for octal literals \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -3429,31 +3429,31 @@ test "std.zon negative char" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "-'a'", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(i16, .{ .gpa = gpa, .arena = arena, .source = "-'a'", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -3464,14 +3464,14 @@ test "std.zon parse float" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Test decimals try std.testing.expectEqual(@as(f16, 0.5), try fromSliceNoAlloc(f16, .{ .gpa = gpa, .arena = arena, .source = "0.5", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual( @as(f32, 123.456), @@ -3479,7 +3479,7 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "123.456", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3488,14 +3488,14 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "-123.456", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual(@as(f128, 42.5), try fromSliceNoAlloc(f128, .{ .gpa = gpa, .arena = arena, .source = "42.5", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test whole numbers with and without decimals @@ -3503,25 +3503,25 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "5.0", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(f16, 5.0), try fromSliceNoAlloc(f16, .{ .gpa = gpa, .arena = arena, .source = "5", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(f32, -102), try fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "-102.0", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(f32, -102), try fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "-102", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test characters and negated characters @@ -3529,13 +3529,13 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "'a'", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(f32, 'z'), try fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "'z'", - .errors = &errors, + .diagnostics = &diagnostics, })); // Test big integers @@ -3545,7 +3545,7 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "36893488147419103231", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3554,20 +3554,20 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "-36893488147419103231", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSliceNoAlloc(f128, .{ .gpa = gpa, .arena = arena, .source = "0x1ffffffffffffffff", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "0x1ffffffffffffffff", - .errors = &errors, + .diagnostics = &diagnostics, })); // Exponents, underscores @@ -3577,7 +3577,7 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "12_3.0E+77", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -3588,7 +3588,7 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "0x103.70p-5", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3597,7 +3597,7 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "-0x103.70", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3606,7 +3606,7 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "0x1234_5678.9ABC_CDEFp-10", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -3615,19 +3615,19 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "inf", - .errors = &errors, + .diagnostics = &diagnostics, }))); try std.testing.expect(std.math.isNegativeInf(try fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "-inf", - .errors = &errors, + .diagnostics = &diagnostics, }))); try std.testing.expect(std.math.isNan(try fromSliceNoAlloc(f32, .{ .gpa = gpa, .arena = arena, .source = "nan", - .errors = &errors, + .diagnostics = &diagnostics, }))); // Negative nan not allowed @@ -3636,12 +3636,12 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "-nan", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // nan as int not allowed @@ -3650,12 +3650,12 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "nan", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // nan as int not allowed @@ -3664,12 +3664,12 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "nan", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // inf as int not allowed @@ -3678,12 +3678,12 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "inf", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // -inf as int not allowed @@ -3692,12 +3692,12 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "-inf", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected type 'i8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Bad identifier as float @@ -3706,14 +3706,14 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "foo", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: invalid expression \\input.zon:1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' \\input.zon:1:1: note: precede identifier with '.' for an enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -3721,12 +3721,12 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "-foo", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:1: error: expected number or 'inf' after '-' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Non float as float @@ -3737,13 +3737,13 @@ test "std.zon parse float" { .gpa = gpa, .arena = arena, .source = "\"foo\"", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected type 'f32' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -3752,7 +3752,7 @@ test "std.zon free on error" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Test freeing partially allocated structs { @@ -3771,7 +3771,7 @@ test "std.zon free on error" { \\ .z = "fail", \\} , - .errors = &errors, + .diagnostics = &diagnostics, })); } @@ -3792,7 +3792,7 @@ test "std.zon free on error" { \\ "fail", \\} , - .errors = &errors, + .diagnostics = &diagnostics, })); } @@ -3809,7 +3809,7 @@ test "std.zon free on error" { \\ .x = "hello", \\} , - .errors = &errors, + .diagnostics = &diagnostics, })); // Test freeing partially allocated arrays @@ -3823,7 +3823,7 @@ test "std.zon free on error" { \\ false, \\} , - .errors = &errors, + .diagnostics = &diagnostics, })); // Test freeing partially allocated slices @@ -3837,7 +3837,7 @@ test "std.zon free on error" { \\ false, \\} , - .errors = &errors, + .diagnostics = &diagnostics, })); // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged @@ -3848,7 +3848,7 @@ test "std.zon free on error" { .gpa = gpa, .arena = arena, .source = ".{ .x = 1.5 }", - .errors = &errors, + .diagnostics = &diagnostics, })).x, ); @@ -3859,7 +3859,7 @@ test "std.zon free on error" { .gpa = gpa, .arena = arena, .source = ".{ .x = \"foo\" }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualStrings("foo", result.x); } @@ -3873,7 +3873,7 @@ test "std.zon vector" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Passing cases try std.testing.expectEqual( @@ -3882,7 +3882,7 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3891,7 +3891,7 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{true, false, true}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -3901,7 +3901,7 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3910,7 +3910,7 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{1.5, 2.5, 3.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -3920,7 +3920,7 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -3929,7 +3929,7 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{2, 4, 6}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -3940,14 +3940,14 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); const pointers = try fromSlice(@Vector(3, *const u8), .{ .gpa = gpa, .arena = arena, .source = ".{2, 4, 6}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers); } @@ -3959,14 +3959,14 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); const pointers = try fromSlice(@Vector(3, ?*const u8), .{ .gpa = gpa, .arena = arena, .source = ".{2, null, 6}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers); } @@ -3979,13 +3979,13 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{0.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:2: error: expected 2 array elements; found 1 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Too many fields @@ -3996,13 +3996,13 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{0.5, 1.5, 2.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:13: error: index 2 outside of array of length 2 \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Wrong type fields @@ -4013,13 +4013,13 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{0.5, true, 2.5}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:8: error: expected type 'f32' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Wrong type @@ -4030,13 +4030,13 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected type '@Vector(3, u8)' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Elements should get freed on error @@ -4047,13 +4047,13 @@ test "std.zon vector" { .gpa = gpa, .arena = arena, .source = ".{1, true, 3}", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:6: error: expected type 'u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -4062,7 +4062,7 @@ test "std.zon add pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Primitive with varying levels of pointers { @@ -4070,7 +4070,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), result.*); } @@ -4080,7 +4080,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), result.*.*); } @@ -4090,7 +4090,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), result.*.*.*); } @@ -4101,7 +4101,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), some.?.*); @@ -4109,7 +4109,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(null, none); } @@ -4119,7 +4119,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), some.*.?); @@ -4127,7 +4127,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(null, none.*); } @@ -4137,7 +4137,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), some.?.*.*); @@ -4145,7 +4145,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(null, none); } @@ -4155,7 +4155,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), some.*.?.*); @@ -4163,7 +4163,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(null, none.*); } @@ -4173,7 +4173,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(@as(u32, 10), some.*.*.?); @@ -4181,7 +4181,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "null", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(null, none.*.*); } @@ -4192,7 +4192,7 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = ".{ 1, 2, 3 }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*); } @@ -4227,7 +4227,7 @@ test "std.zon add pointers" { \\ .f2 = null, \\} , - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(expected, found.?.*); @@ -4241,13 +4241,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected type '?u8' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4257,13 +4257,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected type '?f32' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4273,13 +4273,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected type '?@Vector(3, u8)' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4289,13 +4289,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "10", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected type '?bool' \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4305,13 +4305,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional struct \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4321,13 +4321,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional tuple \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4337,13 +4337,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional union \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4353,13 +4353,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4369,13 +4369,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4385,13 +4385,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4401,13 +4401,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional array \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4417,13 +4417,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional string \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } { @@ -4433,13 +4433,13 @@ test "std.zon add pointers" { .gpa = gpa, .arena = arena, .source = "true", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectFmt( \\input.zon:1:1: error: expected optional enum literal \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } } @@ -4448,7 +4448,7 @@ test "std.zon stop on node" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; { const Vec2 = struct { @@ -4466,7 +4466,7 @@ test "std.zon stop on node" { .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(result.y, 2.5); @@ -4484,7 +4484,7 @@ test "std.zon stop on node" { .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(&zoir)); } @@ -4495,7 +4495,7 @@ test "std.zon no alloc" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; try std.testing.expectEqual( [3]u8{ 1, 2, 3 }, @@ -4503,7 +4503,7 @@ test "std.zon no alloc" { .gpa = gpa, .arena = arena, .source = ".{ 1, 2, 3 }", - .errors = &errors, + .diagnostics = &diagnostics, }), ); @@ -4521,64 +4521,17 @@ test "std.zon no alloc" { .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = &errors, + .diagnostics = &diagnostics, }), ); } -test "std.zon errors without errorsnostics" { - const gpa = std.testing.allocator; - var arena_allocator: ArenaAllocator = .init(gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - var errors: Errors = .empty; - - const Enum = enum { - foo, - bar, - baz, - }; - try std.testing.expectError(error.ParseZon, fromSlice(Enum, .{ - .gpa = gpa, - .arena = arena, - .source = ".nothing", - .errors = &errors, - })); - - const Struct = struct { - name: []const u8, - }; - try std.testing.expectError(error.ParseZon, fromSlice(Struct, .{ - .gpa = gpa, - .arena = arena, - .source = ".{ .name = \"Alice\", .age = 25 }", - .errors = &errors, - })); - - const Union = union(enum) { - x, - y: u32, - }; - try std.testing.expectError(error.ParseZon, fromSlice(Union, .{ - .gpa = gpa, - .arena = arena, - .source = ".a", - .errors = &errors, - })); - try std.testing.expectError(error.ParseZon, fromSlice(Union, .{ - .gpa = gpa, - .arena = arena, - .source = ".{ .b = 8 }", - .errors = &errors, - })); -} - test "std.zon aligned pointers" { const gpa = std.testing.allocator; var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const n: u8 align(8) = 10; const Foo = struct { @@ -4592,7 +4545,7 @@ test "std.zon aligned pointers" { .gpa = gpa, .arena = arena, .source = ".{ .inner = 10 }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(expected.inner, found.inner); } @@ -4602,7 +4555,7 @@ test "std.zon update basic" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const Vector = struct { x: f32, y: f32, z: f32 }; const MyStruct = struct { @@ -4638,7 +4591,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{}", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqualDeep(expected, found); @@ -4646,7 +4599,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .bar = false }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.bar = false; try std.testing.expectEqualDeep(expected, found); @@ -4655,7 +4608,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .baz = .{ .a = .{ 2, 4 } } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.baz.a[0] = 2; expected.baz.a[1] = 4; @@ -4665,7 +4618,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .foo = 11, .baz = .{ .b = .d } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.foo = 11; expected.baz.b = .d; @@ -4675,7 +4628,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .optional = 10 }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.optional = 10; try std.testing.expectEqualDeep(expected, found); @@ -4684,7 +4637,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .optional = null }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.optional = null; try std.testing.expectEqualDeep(expected, found); @@ -4693,7 +4646,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.ptr.x = 10; expected.ptr.y = 20; @@ -4704,7 +4657,7 @@ test "std.zon update basic" { .gpa = gpa, .arena = arena, .source = ".{ .str = \"foo\" }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.str = "foo"; try std.testing.expectEqualDeep(expected, found); @@ -4715,7 +4668,7 @@ test "std.zon update optionals" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const MyStruct = struct { foo: ?struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, @@ -4729,7 +4682,7 @@ test "std.zon update optionals" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .qux = 3 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.foo = .{ .qux = 3 }; try std.testing.expectEqual(expected, found); @@ -4742,12 +4695,12 @@ test "std.zon update optionals" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{} }", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:12: error: missing required field qux \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Updating an optional that starts out non-null should preserve any values we leave off. It's @@ -4761,7 +4714,7 @@ test "std.zon update optionals" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .baz = 200 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.foo.?.baz = 200; try std.testing.expectEqual(expected, found); @@ -4773,7 +4726,7 @@ test "std.zon update optional pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const MyStruct = struct { foo: ?*const struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, @@ -4787,7 +4740,7 @@ test "std.zon update optional pointers" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .qux = 3 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.foo = &.{ .qux = 3 }; try std.testing.expectEqualDeep(expected, found); @@ -4799,7 +4752,7 @@ test "std.zon update pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; // Updating a pointer should leave fields we don't specify unchanged { @@ -4813,7 +4766,7 @@ test "std.zon update pointers" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .bar = 100 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.foo.bar = 100; try std.testing.expectEqualDeep(expected, found); @@ -4830,7 +4783,7 @@ test "std.zon update pointers" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .bar = 100 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.foo = &.{ .bar = 100, .baz = 20, .qux = 3 }; try std.testing.expectEqualDeep(expected, found); @@ -4842,7 +4795,7 @@ test "std.zon update unions" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const MyUnion = union(enum) { none: void, @@ -4857,7 +4810,7 @@ test "std.zon update unions" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .qux = 3 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected = .{ .foo = .{ .qux = 3 } }; try std.testing.expectEqual(expected, found); @@ -4870,12 +4823,12 @@ test "std.zon update unions" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{} }", - .errors = &errors, + .diagnostics = &diagnostics, })); try std.testing.expectFmt( \\input.zon:1:12: error: missing required field qux \\ - , "{f}", .{errors.fmt("input.zon")}); + , "{f}", .{diagnostics.fmt("input.zon")}); } // Updating a union should preseve any sub-fields that we left off. It's also okay to leave off @@ -4889,7 +4842,7 @@ test "std.zon update unions" { .gpa = gpa, .arena = arena, .source = ".{ .foo = .{ .baz = 200 } }", - .errors = &errors, + .diagnostics = &diagnostics, }); expected.foo.baz = 200; try std.testing.expectEqual(expected, found); @@ -4904,7 +4857,7 @@ test "std.zon variants" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var errors: Errors = .empty; + var diagnostics: Diagnostics = .empty; const Struct = struct { a: u32, b: u32 }; const start: Struct = .{ .a = 10, .b = 20 }; @@ -4922,7 +4875,7 @@ test "std.zon variants" { .gpa = gpa, .arena = arena, .source = ".{ .a = 100 }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(end, curr); @@ -4931,7 +4884,7 @@ test "std.zon variants" { .gpa = gpa, .arena = arena, .source = ".{ .a = 100 }", - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(end, curr); @@ -4940,7 +4893,7 @@ test "std.zon variants" { .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(end, curr); @@ -4949,7 +4902,7 @@ test "std.zon variants" { .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = &errors, + .diagnostics = &diagnostics, }); try std.testing.expectEqual(end, curr); } @@ -4967,7 +4920,7 @@ test "std.zon variants" { .gpa = gpa, .arena = arena, .source = ".{ .a = 100, .b = 20 }", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -4976,7 +4929,7 @@ test "std.zon variants" { .gpa = gpa, .arena = arena, .source = ".{ .a = 100, .b = 20 }", - .errors = &errors, + .diagnostics = &diagnostics, }), ); try std.testing.expectEqual( @@ -4984,7 +4937,7 @@ test "std.zon variants" { .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = &errors, + .diagnostics = &diagnostics, }), end, ); @@ -4993,7 +4946,7 @@ test "std.zon variants" { .arena = arena, .ast = &ast, .zoir = &zoir, - .errors = &errors, + .diagnostics = &diagnostics, }), end, ); @@ -5001,5 +4954,5 @@ test "std.zon variants" { } test { - _ = Errors; + _ = Diagnostics; } -- 2.54.0 From aa0504ac574cafd3a6ff3cf716bac537fe3689d7 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 28 Sep 2026 00:54:40 -0700 Subject: [PATCH 26/26] Remove `Diagnostics.empty`, initialized in callee --- lib/std/zon/parse.zig | 76 +++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index c2422539c3e2bd8005c6f95bcfafc2ac8bf6e9e1..3f4eadddaabe3e442e112d3a76c02e9fcbe0439c 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -27,8 +27,6 @@ const valid_types = {}; pub const Diagnostics = struct { errors: []const Error, - pub const empty: Diagnostics = .{ .errors = &.{} }; - /// Log the failure with `std.log.err`. pub fn log(self: *const Diagnostics, path: []const u8) void { std.log.err("{f}", .{self.fmt(path)}); @@ -39,7 +37,7 @@ pub const Diagnostics = struct { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const MyType = struct { foo: u32, @@ -71,7 +69,7 @@ pub const Diagnostics = struct { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const MyType = struct { foo: u32, @@ -198,8 +196,8 @@ pub const Options = struct { arena: Allocator, /// The ZON source to parse. source: [:0]const u8, - /// When an error is returned from the parser, a human readable error description of the failure - /// is stored here. + /// Initialized by the callee with a human readable description of any parse errors that + /// occured. diagnostics: *Diagnostics, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, @@ -232,7 +230,7 @@ test fromSlice { \\} ; - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const options = fromSlice(TextureOptions, .{ .gpa = gpa, .arena = arena, @@ -307,7 +305,7 @@ test updateFromSlice { .theme = "default", }; - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; updateFromSlice(MyTextEditorConfig, &config, .{ .gpa = gpa, .arena = arena, @@ -359,7 +357,7 @@ fn fromSliceInner( options: Options, ) error{ OutOfMemory, ParseZon }!void { var errors: std.ArrayList(Diagnostics.Error) = .empty; - defer options.diagnostics.errors = errors.items; + defer options.diagnostics.* = .{ .errors = errors.items }; var ast = try std.zig.Ast.parse(options.gpa, options.source, .{ .mode = .zon }); defer ast.deinit(options.gpa); @@ -386,8 +384,8 @@ pub const FromZoirOptions = struct { zoir: *const Zoir, /// The node to start parsing at. node: Zoir.Node.Index = .root, - /// When an error is returned from the parser, a human readable error description of the failure - /// is stored here. + /// Initialized by the callee with a human readable description of any parse errors that + /// occured. diagnostics: *Diagnostics, /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, @@ -432,7 +430,7 @@ fn fromZoirInner( options: FromZoirOptions, ) error{ OutOfMemory, ParseZon }!void { var errors: std.ArrayList(Diagnostics.Error) = .empty; - defer options.diagnostics.errors = errors.items; + defer options.diagnostics.* = .{ .errors = errors.items }; return inner(T, value, initialized, .{ .arena = options.arena, .ast = options.ast, @@ -1384,7 +1382,7 @@ test "std.zon ast errors" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; try std.testing.expectError( error.ParseZon, fromSliceNoAlloc(struct {}, .{ @@ -1405,7 +1403,7 @@ test "std.zon comments" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; try std.testing.expectEqual(@as(u8, 10), fromSliceNoAlloc(u8, .{ .gpa = gpa, @@ -1441,7 +1439,7 @@ test "std.zon failure/oom formatting" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; try std.testing.expectError(error.OutOfMemory, fromSlice([]const u8, .{ .gpa = .failing, @@ -1459,7 +1457,7 @@ test "std.zon fromSlice syntax error" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; try std.testing.expectError( error.ParseZon, fromSliceNoAlloc(u8, .{ @@ -1480,7 +1478,7 @@ test "std.zon optional" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Basic usage { @@ -1524,7 +1522,7 @@ test "std.zon unions" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Unions { @@ -1722,7 +1720,7 @@ test "std.zon structs" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Structs (various sizes tested since they're parsed differently) { @@ -2019,7 +2017,7 @@ test "std.zon tuples" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Structs (various sizes tested since they're parsed differently) { @@ -2180,7 +2178,7 @@ test "std.zon arrays and slices" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Literals { @@ -2500,7 +2498,7 @@ test "std.zon string literal" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Basic string literal { @@ -2792,7 +2790,7 @@ test "std.zon enum literals" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const Enum = enum { foo, @@ -2906,7 +2904,7 @@ test "std.zon parse bool" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Correct bools try std.testing.expectEqual(true, try fromSliceNoAlloc(bool, .{ @@ -2981,7 +2979,7 @@ test "std.zon parse int" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Test various numbers and types try std.testing.expectEqual(@as(u8, 10), try fromSliceNoAlloc(u8, .{ @@ -3429,7 +3427,7 @@ test "std.zon negative char" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; { try std.testing.expectError(error.ParseZon, fromSliceNoAlloc(f32, .{ @@ -3464,7 +3462,7 @@ test "std.zon parse float" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Test decimals try std.testing.expectEqual(@as(f16, 0.5), try fromSliceNoAlloc(f16, .{ @@ -3752,7 +3750,7 @@ test "std.zon free on error" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Test freeing partially allocated structs { @@ -3873,7 +3871,7 @@ test "std.zon vector" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Passing cases try std.testing.expectEqual( @@ -4062,7 +4060,7 @@ test "std.zon add pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Primitive with varying levels of pointers { @@ -4448,7 +4446,7 @@ test "std.zon stop on node" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; { const Vec2 = struct { @@ -4495,7 +4493,7 @@ test "std.zon no alloc" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; try std.testing.expectEqual( [3]u8{ 1, 2, 3 }, @@ -4531,7 +4529,7 @@ test "std.zon aligned pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const n: u8 align(8) = 10; const Foo = struct { @@ -4555,7 +4553,7 @@ test "std.zon update basic" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const Vector = struct { x: f32, y: f32, z: f32 }; const MyStruct = struct { @@ -4668,7 +4666,7 @@ test "std.zon update optionals" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const MyStruct = struct { foo: ?struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, @@ -4726,7 +4724,7 @@ test "std.zon update optional pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const MyStruct = struct { foo: ?*const struct { bar: u32 = 1, baz: u32 = 2, qux: u32 }, @@ -4752,7 +4750,7 @@ test "std.zon update pointers" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; // Updating a pointer should leave fields we don't specify unchanged { @@ -4795,7 +4793,7 @@ test "std.zon update unions" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const MyUnion = union(enum) { none: void, @@ -4857,7 +4855,7 @@ test "std.zon variants" { var arena_allocator: ArenaAllocator = .init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var diagnostics: Diagnostics = .empty; + var diagnostics: Diagnostics = undefined; const Struct = struct { a: u32, b: u32 }; const start: Struct = .{ .a = 10, .b = 20 }; -- 2.54.0