authorgravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2024-06-02 11:07:55-04:00
committergravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2024-06-02 11:07:55-04:00
log44a35b9c50601cc5b52d8691c4d192e2671be0c2
tree292ae6081f1f2b5620c9bfa02557e96e7db08cdb
parent896e36f935b6c69b2f330d106f4bf648ad3cf212

error messages for all parse from source errors


2 files changed, 60 insertions(+), 23 deletions(-)

lib/std/json/scanner.zig+14-5
......@@ -204,6 +204,8 @@ pub const Diagnostics = struct {
204204 cursor_in_current_input: usize = undefined,
205205 current_input: []const u8 = undefined,
206206
207 // updated setMessage().
208 message: []const u8 = "",
207209 // updated by recordContext().
208210 context_stack: BoundedArray([]const u8, 8) = .{},
209211
......@@ -220,6 +222,13 @@ pub const Diagnostics = struct {
220222 return self.total_bytes_before_current_input + self.cursor_in_current_input;
221223 }
222224
225 /// Set the headline message of the problem.
226 /// Must not be called multiple times.
227 pub fn setMessage(self: *@This(), message: []const u8) void {
228 assert(message.len != 0);
229 assert(self.message.len == 0); // already set message.
230 self.message = message;
231 }
223232 /// Attemps to push a human-readable string onto the context stack.
224233 /// Only works up to a maximum number of times, after which this does nothing.
225234 pub fn recordContext(self: *@This(), context: []const u8) void {
......@@ -270,11 +279,11 @@ pub const Diagnostics = struct {
270279 try writer.writeByteNTimes(' ', start_elipsis.len + self.cursor_in_current_input - start);
271280 try writer.writeAll("^\n");
272281
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 }
282 if (self.message.len > 0) {
283 try writer.print("{s}\n", .{self.message});
284 }
285 for (self.context_stack.slice()) |item| {
286 try writer.print(" in {s}\n", .{item});
278287 }
279288 }
280289};
lib/std/json/static.zig+46-18
......@@ -287,7 +287,7 @@ pub fn innerParse(
287287 const field_name = switch (name_token.?) {
288288 inline .string, .allocated_string => |slice| slice,
289289 else => {
290 if (options.diagnostics) |diag| diag.recordContext("tagged union requires a field to identify the active tag");
290 if (options.diagnostics) |diag| diag.setMessage("tagged union requires a field to identify the active tag");
291291 return error.MissingField;
292292 },
293293 };
......@@ -305,7 +305,7 @@ pub fn innerParse(
305305 else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"),
306306 }
307307 if (.object_end != try source.next()) {
308 if (options.diagnostics) |diag| diag.recordContext("void payload '{}' should have no fields");
308 if (options.diagnostics) |diag| diag.setMessage("void payload '{}' should have no fields");
309309 return error.UnknownField;
310310 }
311311 result = @unionInit(T, u_field.name, {});
......@@ -317,12 +317,12 @@ pub fn innerParse(
317317 }
318318 } else {
319319 // Didn't match anything.
320 if (options.diagnostics) |diag| diag.recordContext("unrecognized tag name");
320 if (options.diagnostics) |diag| diag.setMessage("unrecognized tag name");
321321 return error.UnknownField;
322322 }
323323
324324 if (.object_end != try source.next()) {
325 if (options.diagnostics) |diag| diag.recordContext("tagged union requires only one field");
325 if (options.diagnostics) |diag| diag.setMessage("tagged union requires only one field");
326326 return error.UnknownField;
327327 }
328328
......@@ -339,7 +339,7 @@ pub fn innerParse(
339339 var r: T = undefined;
340340 inline for (0..structInfo.fields.len) |i| {
341341 if (.array_end == try source.peekNextTokenType()) {
342 if (options.diagnostics) |diag| diag.recordContext(std.fmt.comptimePrint(
342 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint(
343343 "tuple too short. expected length: {}",
344344 .{structInfo.fields.len},
345345 ));
......@@ -349,7 +349,7 @@ pub fn innerParse(
349349 }
350350
351351 if (.array_end != try source.next()) {
352 if (options.diagnostics) |diag| diag.recordContext(std.fmt.comptimePrint(
352 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint(
353353 "tuple too long. expected length: {}",
354354 .{structInfo.fields.len},
355355 ));
......@@ -398,7 +398,7 @@ pub fn innerParse(
398398 break;
399399 },
400400 .@"error" => {
401 if (options.diagnostics) |diag| diag.recordContext("duplicate field name: " ++ field.name);
401 if (options.diagnostics) |diag| diag.setMessage("duplicate field name: " ++ field.name);
402402 return error.DuplicateField;
403403 },
404404 .use_last => {},
......@@ -414,7 +414,7 @@ pub fn innerParse(
414414 if (options.ignore_unknown_fields) {
415415 try source.skipValue();
416416 } else {
417 if (options.diagnostics) |diag| diag.recordContext("unrecognized field name");
417 if (options.diagnostics) |diag| diag.setMessage("unrecognized field name");
418418 return error.UnknownField;
419419 }
420420 }
......@@ -438,32 +438,54 @@ pub fn innerParse(
438438 while (true) {
439439 switch (try source.next()) {
440440 .string => |slice| {
441 if (i + slice.len != r.len) return error.LengthMismatch;
441 if (i + slice.len > r.len) {
442 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("string too long for fixed length array. expected length: {}", .{r.len}));
443 return error.LengthMismatch;
444 }
445 if (i + slice.len < r.len) {
446 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("string too short for fixed length array. expected length: {}", .{r.len}));
447 return error.LengthMismatch;
448 }
442449 @memcpy(r[i..][0..slice.len], slice);
443450 break;
444451 },
445452 .partial_string => |slice| {
446 if (i + slice.len > r.len) return error.LengthMismatch;
453 if (i + slice.len > r.len) {
454 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("string too long for fixed length array. expected length: {}", .{r.len}));
455 return error.LengthMismatch;
456 }
447457 @memcpy(r[i..][0..slice.len], slice);
448458 i += slice.len;
449459 },
450460 .partial_string_escaped_1 => |arr| {
451 if (i + arr.len > r.len) return error.LengthMismatch;
461 if (i + arr.len > r.len) {
462 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("string too long for fixed length array. expected length: {}", .{r.len}));
463 return error.LengthMismatch;
464 }
452465 @memcpy(r[i..][0..arr.len], arr[0..]);
453466 i += arr.len;
454467 },
455468 .partial_string_escaped_2 => |arr| {
456 if (i + arr.len > r.len) return error.LengthMismatch;
469 if (i + arr.len > r.len) {
470 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("string too long for fixed length array. expected length: {}", .{r.len}));
471 return error.LengthMismatch;
472 }
457473 @memcpy(r[i..][0..arr.len], arr[0..]);
458474 i += arr.len;
459475 },
460476 .partial_string_escaped_3 => |arr| {
461 if (i + arr.len > r.len) return error.LengthMismatch;
477 if (i + arr.len > r.len) {
478 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("string too long for fixed length array. expected length: {}", .{r.len}));
479 return error.LengthMismatch;
480 }
462481 @memcpy(r[i..][0..arr.len], arr[0..]);
463482 i += arr.len;
464483 },
465484 .partial_string_escaped_4 => |arr| {
466 if (i + arr.len > r.len) return error.LengthMismatch;
485 if (i + arr.len > r.len) {
486 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("string too long for fixed length array. expected length: {}", .{r.len}));
487 return error.LengthMismatch;
488 }
467489 @memcpy(r[i..][0..arr.len], arr[0..]);
468490 i += arr.len;
469491 },
......@@ -568,11 +590,17 @@ fn internalParseArray(
568590 var r: T = undefined;
569591 var i: usize = 0;
570592 while (i < len) : (i += 1) {
571 if (.array_end == try source.peekNextTokenType()) return error.LengthMismatch;
593 if (.array_end == try source.peekNextTokenType()) {
594 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("not enough items for fixed length array. expected length: {}", .{len}));
595 return error.LengthMismatch;
596 }
572597 r[i] = try innerParse(Child, allocator, source, options);
573598 }
574599
575 if (.array_end != try source.next()) return error.LengthMismatch;
600 if (.array_end != try source.next()) {
601 if (options.diagnostics) |diag| diag.setMessage(std.fmt.comptimePrint("too many items for fixed length array. expected length: {}", .{len}));
602 return error.LengthMismatch;
603 }
576604
577605 return r;
578606}
......@@ -622,7 +650,7 @@ fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []co
622650 .array_end => unreachable, // type errors happen at the start of a value.
623651 .end_of_document => unreachable, // type errors happen at the start of a value.
624652 };
625 diag.recordContext(s);
653 diag.setMessage(s);
626654 }
627655 return error.UnexpectedToken;
628656}
......@@ -880,7 +908,7 @@ fn fillDefaultStructValues(comptime T: type, options: ParseOptions, r: *T, field
880908 const default = @as(*align(1) const field.type, @ptrCast(default_ptr)).*;
881909 @field(r, field.name) = default;
882910 } else {
883 if (options.diagnostics) |diag| diag.recordContext("missing field: " ++ @typeName(T) ++ "." ++ field.name);
911 if (options.diagnostics) |diag| diag.setMessage("missing field: " ++ @typeName(T) ++ "." ++ field.name);
884912 return error.MissingField;
885913 }
886914 }