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 {...@@ -270,8 +270,11 @@ pub const Diagnostics = struct {
270 try writer.writeByteNTimes(' ', start_elipsis.len + self.cursor_in_current_input - start);270 try writer.writeByteNTimes(' ', start_elipsis.len + self.cursor_in_current_input - start);
271 try writer.writeAll("^\n");271 try writer.writeAll("^\n");
272272
273 for (self.context_stack.slice()) |item| {273 if (self.context_stack.len > 0) {
274 try writer.print(" in {s}\n", .{item});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 }
275 }278 }
276 }279 }
277};280};
lib/std/json/static.zig+37-9
...@@ -286,7 +286,10 @@ pub fn innerParse(...@@ -286,7 +286,10 @@ pub fn innerParse(
286 var name_token: ?Token = try source.nextAllocMax(allocator, .alloc_if_needed, options.max_value_len.?);286 var name_token: ?Token = try source.nextAllocMax(allocator, .alloc_if_needed, options.max_value_len.?);
287 const field_name = switch (name_token.?) {287 const field_name = switch (name_token.?) {
288 inline .string, .allocated_string => |slice| slice,288 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 },
290 };293 };
291294
292 inline for (unionInfo.fields) |u_field| {295 inline for (unionInfo.fields) |u_field| {
...@@ -301,7 +304,10 @@ pub fn innerParse(...@@ -301,7 +304,10 @@ pub fn innerParse(
301 .object_begin => {},304 .object_begin => {},
302 else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"),305 else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"),
303 }306 }
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 }
305 result = @unionInit(T, u_field.name, {});311 result = @unionInit(T, u_field.name, {});
306 } else {312 } else {
307 // Recurse.313 // Recurse.
...@@ -311,10 +317,14 @@ pub fn innerParse(...@@ -311,10 +317,14 @@ pub fn innerParse(
311 }317 }
312 } else {318 } else {
313 // Didn't match anything.319 // Didn't match anything.
320 if (options.diagnostics) |diag| diag.recordContext("unrecognized tag name");
314 return error.UnknownField;321 return error.UnknownField;
315 }322 }
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
319 return result.?;329 return result.?;
320 },330 },
...@@ -328,11 +338,23 @@ pub fn innerParse(...@@ -328,11 +338,23 @@ pub fn innerParse(
328338
329 var r: T = undefined;339 var r: T = undefined;
330 inline for (0..structInfo.fields.len) |i| {340 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 }
332 r[i] = try innerParse(structInfo.fields[i].type, allocator, source, options);348 r[i] = try innerParse(structInfo.fields[i].type, allocator, source, options);
333 }349 }
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
337 return r;359 return r;
338 }360 }
...@@ -375,7 +397,10 @@ pub fn innerParse(...@@ -375,7 +397,10 @@ pub fn innerParse(
375 _ = try innerParse(field.type, allocator, source, options);397 _ = try innerParse(field.type, allocator, source, options);
376 break;398 break;
377 },399 },
378 .@"error" => return error.DuplicateField,400 .@"error" => {
401 if (options.diagnostics) |diag| diag.recordContext("duplicate field name: " ++ field.name);
402 return error.DuplicateField;
403 },
379 .use_last => {},404 .use_last => {},
380 }405 }
381 }406 }
...@@ -389,11 +414,12 @@ pub fn innerParse(...@@ -389,11 +414,12 @@ pub fn innerParse(
389 if (options.ignore_unknown_fields) {414 if (options.ignore_unknown_fields) {
390 try source.skipValue();415 try source.skipValue();
391 } else {416 } else {
417 if (options.diagnostics) |diag| diag.recordContext("unrecognized field name");
392 return error.UnknownField;418 return error.UnknownField;
393 }419 }
394 }420 }
395 }421 }
396 try fillDefaultStructValues(T, &r, &fields_seen);422 try fillDefaultStructValues(T, options, &r, &fields_seen);
397 return r;423 return r;
398 },424 },
399425
...@@ -591,6 +617,7 @@ fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []co...@@ -591,6 +617,7 @@ fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []co
591 .null => prefix ++ "null",617 .null => prefix ++ "null",
592 .number => prefix ++ "number",618 .number => prefix ++ "number",
593 .string => prefix ++ "string",619 .string => prefix ++ "string",
620
594 .object_end => unreachable, // type errors happen at the start of a value.621 .object_end => unreachable, // type errors happen at the start of a value.
595 .array_end => unreachable, // type errors happen at the start of a value.622 .array_end => unreachable, // type errors happen at the start of a value.
596 .end_of_document => unreachable, // type errors happen at the start of a value.623 .end_of_document => unreachable, // type errors happen at the start of a value.
...@@ -732,7 +759,7 @@ pub fn innerParseFromValue(...@@ -732,7 +759,7 @@ pub fn innerParseFromValue(
732 if (!options.ignore_unknown_fields) return error.UnknownField;759 if (!options.ignore_unknown_fields) return error.UnknownField;
733 }760 }
734 }761 }
735 try fillDefaultStructValues(T, &r, &fields_seen);762 try fillDefaultStructValues(T, options, &r, &fields_seen);
736 return r;763 return r;
737 },764 },
738765
...@@ -846,13 +873,14 @@ fn sliceToEnum(comptime T: type, slice: []const u8) !T {...@@ -846,13 +873,14 @@ fn sliceToEnum(comptime T: type, slice: []const u8) !T {
846 return std.meta.intToEnum(T, n);873 return std.meta.intToEnum(T, n);
847}874}
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 {
850 inline for (@typeInfo(T).Struct.fields, 0..) |field, i| {877 inline for (@typeInfo(T).Struct.fields, 0..) |field, i| {
851 if (!fields_seen[i]) {878 if (!fields_seen[i]) {
852 if (field.default_value) |default_ptr| {879 if (field.default_value) |default_ptr| {
853 const default = @as(*align(1) const field.type, @ptrCast(default_ptr)).*;880 const default = @as(*align(1) const field.type, @ptrCast(default_ptr)).*;
854 @field(r, field.name) = default;881 @field(r, field.name) = default;
855 } else {882 } else {
883 if (options.diagnostics) |diag| diag.recordContext("missing field: " ++ @typeName(T) ++ "." ++ field.name);
856 return error.MissingField;884 return error.MissingField;
857 }885 }
858 }886 }