authorgravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2024-06-02 01:09:11-04:00
committergravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2024-06-02 01:09:11-04:00
log896e36f935b6c69b2f330d106f4bf648ad3cf212
tree446a8683f701bd42e4faa565e168765a53ff4cfc
parentce828bdc8947d0d4817acea1bf7624ccf5455ab3

comptime string error messages are limited


2 files changed, 42 insertions(+), 11 deletions(-)

lib/std/json/scanner.zig+5-2
......@@ -270,8 +270,11 @@ pub const Diagnostics = struct {
270270 try writer.writeByteNTimes(' ', start_elipsis.len + self.cursor_in_current_input - start);
271271 try writer.writeAll("^\n");
272272
273 for (self.context_stack.slice()) |item| {
274 try writer.print(" in {s}\n", .{item});
273 if (self.context_stack.len > 0) {
274 try writer.print("{s}\n", .{self.context_stack.slice()[0]});
275 for (self.context_stack.slice()[1..]) |item| {
276 try writer.print(" in {s}\n", .{item});
277 }
275278 }
276279 }
277280};
lib/std/json/static.zig+37-9
......@@ -286,7 +286,10 @@ pub fn innerParse(
286286 var name_token: ?Token = try source.nextAllocMax(allocator, .alloc_if_needed, options.max_value_len.?);
287287 const field_name = switch (name_token.?) {
288288 inline .string, .allocated_string => |slice| slice,
289 else => return error.MissingField,
289 else => {
290 if (options.diagnostics) |diag| diag.recordContext("tagged union requires a field to identify the active tag");
291 return error.MissingField;
292 },
290293 };
291294
292295 inline for (unionInfo.fields) |u_field| {
......@@ -301,7 +304,10 @@ pub fn innerParse(
301304 .object_begin => {},
302305 else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"),
303306 }
304 if (.object_end != try source.next()) return error.UnknownField;
307 if (.object_end != try source.next()) {
308 if (options.diagnostics) |diag| diag.recordContext("void payload '{}' should have no fields");
309 return error.UnknownField;
310 }
305311 result = @unionInit(T, u_field.name, {});
306312 } else {
307313 // Recurse.
......@@ -311,10 +317,14 @@ pub fn innerParse(
311317 }
312318 } else {
313319 // Didn't match anything.
320 if (options.diagnostics) |diag| diag.recordContext("unrecognized tag name");
314321 return error.UnknownField;
315322 }
316323
317 if (.object_end != try source.next()) return error.UnknownField;
324 if (.object_end != try source.next()) {
325 if (options.diagnostics) |diag| diag.recordContext("tagged union requires only one field");
326 return error.UnknownField;
327 }
318328
319329 return result.?;
320330 },
......@@ -328,11 +338,23 @@ pub fn innerParse(
328338
329339 var r: T = undefined;
330340 inline for (0..structInfo.fields.len) |i| {
331 if (.array_end == try source.peekNextTokenType()) return error.LengthMismatch;
341 if (.array_end == try source.peekNextTokenType()) {
342 if (options.diagnostics) |diag| diag.recordContext(std.fmt.comptimePrint(
343 "tuple too short. expected length: {}",
344 .{structInfo.fields.len},
345 ));
346 return error.LengthMismatch;
347 }
332348 r[i] = try innerParse(structInfo.fields[i].type, allocator, source, options);
333349 }
334350
335 if (.array_end != try source.next()) return error.LengthMismatch;
351 if (.array_end != try source.next()) {
352 if (options.diagnostics) |diag| diag.recordContext(std.fmt.comptimePrint(
353 "tuple too long. expected length: {}",
354 .{structInfo.fields.len},
355 ));
356 return error.LengthMismatch;
357 }
336358
337359 return r;
338360 }
......@@ -375,7 +397,10 @@ pub fn innerParse(
375397 _ = try innerParse(field.type, allocator, source, options);
376398 break;
377399 },
378 .@"error" => return error.DuplicateField,
400 .@"error" => {
401 if (options.diagnostics) |diag| diag.recordContext("duplicate field name: " ++ field.name);
402 return error.DuplicateField;
403 },
379404 .use_last => {},
380405 }
381406 }
......@@ -389,11 +414,12 @@ pub fn innerParse(
389414 if (options.ignore_unknown_fields) {
390415 try source.skipValue();
391416 } else {
417 if (options.diagnostics) |diag| diag.recordContext("unrecognized field name");
392418 return error.UnknownField;
393419 }
394420 }
395421 }
396 try fillDefaultStructValues(T, &r, &fields_seen);
422 try fillDefaultStructValues(T, options, &r, &fields_seen);
397423 return r;
398424 },
399425
......@@ -591,6 +617,7 @@ fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []co
591617 .null => prefix ++ "null",
592618 .number => prefix ++ "number",
593619 .string => prefix ++ "string",
620
594621 .object_end => unreachable, // type errors happen at the start of a value.
595622 .array_end => unreachable, // type errors happen at the start of a value.
596623 .end_of_document => unreachable, // type errors happen at the start of a value.
......@@ -732,7 +759,7 @@ pub fn innerParseFromValue(
732759 if (!options.ignore_unknown_fields) return error.UnknownField;
733760 }
734761 }
735 try fillDefaultStructValues(T, &r, &fields_seen);
762 try fillDefaultStructValues(T, options, &r, &fields_seen);
736763 return r;
737764 },
738765
......@@ -846,13 +873,14 @@ fn sliceToEnum(comptime T: type, slice: []const u8) !T {
846873 return std.meta.intToEnum(T, n);
847874}
848875
849fn fillDefaultStructValues(comptime T: type, r: *T, fields_seen: *[@typeInfo(T).Struct.fields.len]bool) !void {
876fn fillDefaultStructValues(comptime T: type, options: ParseOptions, r: *T, fields_seen: *[@typeInfo(T).Struct.fields.len]bool) !void {
850877 inline for (@typeInfo(T).Struct.fields, 0..) |field, i| {
851878 if (!fields_seen[i]) {
852879 if (field.default_value) |default_ptr| {
853880 const default = @as(*align(1) const field.type, @ptrCast(default_ptr)).*;
854881 @field(r, field.name) = default;
855882 } else {
883 if (options.diagnostics) |diag| diag.recordContext("missing field: " ++ @typeName(T) ++ "." ++ field.name);
856884 return error.MissingField;
857885 }
858886 }