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 {...@@ -204,6 +204,8 @@ pub const Diagnostics = struct {
204 cursor_in_current_input: usize = undefined,204 cursor_in_current_input: usize = undefined,
205 current_input: []const u8 = undefined,205 current_input: []const u8 = undefined,
206206
207 // updated setMessage().
208 message: []const u8 = "",
207 // updated by recordContext().209 // updated by recordContext().
208 context_stack: BoundedArray([]const u8, 8) = .{},210 context_stack: BoundedArray([]const u8, 8) = .{},
209211
...@@ -220,6 +222,13 @@ pub const Diagnostics = struct {...@@ -220,6 +222,13 @@ pub const Diagnostics = struct {
220 return self.total_bytes_before_current_input + self.cursor_in_current_input;222 return self.total_bytes_before_current_input + self.cursor_in_current_input;
221 }223 }
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 }
223 /// Attemps to push a human-readable string onto the context stack.232 /// Attemps to push a human-readable string onto the context stack.
224 /// Only works up to a maximum number of times, after which this does nothing.233 /// Only works up to a maximum number of times, after which this does nothing.
225 pub fn recordContext(self: *@This(), context: []const u8) void {234 pub fn recordContext(self: *@This(), context: []const u8) void {
...@@ -270,11 +279,11 @@ pub const Diagnostics = struct {...@@ -270,11 +279,11 @@ pub const Diagnostics = struct {
270 try writer.writeByteNTimes(' ', start_elipsis.len + self.cursor_in_current_input - start);279 try writer.writeByteNTimes(' ', start_elipsis.len + self.cursor_in_current_input - start);
271 try writer.writeAll("^\n");280 try writer.writeAll("^\n");
272281
273 if (self.context_stack.len > 0) {282 if (self.message.len > 0) {
274 try writer.print("{s}\n", .{self.context_stack.slice()[0]});283 try writer.print("{s}\n", .{self.message});
275 for (self.context_stack.slice()[1..]) |item| {284 }
276 try writer.print(" in {s}\n", .{item});285 for (self.context_stack.slice()) |item| {
277 }286 try writer.print(" in {s}\n", .{item});
278 }287 }
279 }288 }
280};289};
lib/std/json/static.zig+46-18
...@@ -287,7 +287,7 @@ pub fn innerParse(...@@ -287,7 +287,7 @@ pub fn innerParse(
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 => {289 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");
291 return error.MissingField;291 return error.MissingField;
292 },292 },
293 };293 };
...@@ -305,7 +305,7 @@ pub fn innerParse(...@@ -305,7 +305,7 @@ pub fn innerParse(
305 else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"),305 else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"),
306 }306 }
307 if (.object_end != try source.next()) {307 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");
309 return error.UnknownField;309 return error.UnknownField;
310 }310 }
311 result = @unionInit(T, u_field.name, {});311 result = @unionInit(T, u_field.name, {});
...@@ -317,12 +317,12 @@ pub fn innerParse(...@@ -317,12 +317,12 @@ pub fn innerParse(
317 }317 }
318 } else {318 } else {
319 // Didn't match anything.319 // Didn't match anything.
320 if (options.diagnostics) |diag| diag.recordContext("unrecognized tag name");320 if (options.diagnostics) |diag| diag.setMessage("unrecognized tag name");
321 return error.UnknownField;321 return error.UnknownField;
322 }322 }
323323
324 if (.object_end != try source.next()) {324 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");
326 return error.UnknownField;326 return error.UnknownField;
327 }327 }
328328
...@@ -339,7 +339,7 @@ pub fn innerParse(...@@ -339,7 +339,7 @@ pub fn innerParse(
339 var r: T = undefined;339 var r: T = undefined;
340 inline for (0..structInfo.fields.len) |i| {340 inline for (0..structInfo.fields.len) |i| {
341 if (.array_end == try source.peekNextTokenType()) {341 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(
343 "tuple too short. expected length: {}",343 "tuple too short. expected length: {}",
344 .{structInfo.fields.len},344 .{structInfo.fields.len},
345 ));345 ));
...@@ -349,7 +349,7 @@ pub fn innerParse(...@@ -349,7 +349,7 @@ pub fn innerParse(
349 }349 }
350350
351 if (.array_end != try source.next()) {351 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(
353 "tuple too long. expected length: {}",353 "tuple too long. expected length: {}",
354 .{structInfo.fields.len},354 .{structInfo.fields.len},
355 ));355 ));
...@@ -398,7 +398,7 @@ pub fn innerParse(...@@ -398,7 +398,7 @@ pub fn innerParse(
398 break;398 break;
399 },399 },
400 .@"error" => {400 .@"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);
402 return error.DuplicateField;402 return error.DuplicateField;
403 },403 },
404 .use_last => {},404 .use_last => {},
...@@ -414,7 +414,7 @@ pub fn innerParse(...@@ -414,7 +414,7 @@ pub fn innerParse(
414 if (options.ignore_unknown_fields) {414 if (options.ignore_unknown_fields) {
415 try source.skipValue();415 try source.skipValue();
416 } else {416 } else {
417 if (options.diagnostics) |diag| diag.recordContext("unrecognized field name");417 if (options.diagnostics) |diag| diag.setMessage("unrecognized field name");
418 return error.UnknownField;418 return error.UnknownField;
419 }419 }
420 }420 }
...@@ -438,32 +438,54 @@ pub fn innerParse(...@@ -438,32 +438,54 @@ pub fn innerParse(
438 while (true) {438 while (true) {
439 switch (try source.next()) {439 switch (try source.next()) {
440 .string => |slice| {440 .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 }
442 @memcpy(r[i..][0..slice.len], slice);449 @memcpy(r[i..][0..slice.len], slice);
443 break;450 break;
444 },451 },
445 .partial_string => |slice| {452 .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 }
447 @memcpy(r[i..][0..slice.len], slice);457 @memcpy(r[i..][0..slice.len], slice);
448 i += slice.len;458 i += slice.len;
449 },459 },
450 .partial_string_escaped_1 => |arr| {460 .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 }
452 @memcpy(r[i..][0..arr.len], arr[0..]);465 @memcpy(r[i..][0..arr.len], arr[0..]);
453 i += arr.len;466 i += arr.len;
454 },467 },
455 .partial_string_escaped_2 => |arr| {468 .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 }
457 @memcpy(r[i..][0..arr.len], arr[0..]);473 @memcpy(r[i..][0..arr.len], arr[0..]);
458 i += arr.len;474 i += arr.len;
459 },475 },
460 .partial_string_escaped_3 => |arr| {476 .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 }
462 @memcpy(r[i..][0..arr.len], arr[0..]);481 @memcpy(r[i..][0..arr.len], arr[0..]);
463 i += arr.len;482 i += arr.len;
464 },483 },
465 .partial_string_escaped_4 => |arr| {484 .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 }
467 @memcpy(r[i..][0..arr.len], arr[0..]);489 @memcpy(r[i..][0..arr.len], arr[0..]);
468 i += arr.len;490 i += arr.len;
469 },491 },
...@@ -568,11 +590,17 @@ fn internalParseArray(...@@ -568,11 +590,17 @@ fn internalParseArray(
568 var r: T = undefined;590 var r: T = undefined;
569 var i: usize = 0;591 var i: usize = 0;
570 while (i < len) : (i += 1) {592 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 }
572 r[i] = try innerParse(Child, allocator, source, options);597 r[i] = try innerParse(Child, allocator, source, options);
573 }598 }
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
577 return r;605 return r;
578}606}
...@@ -622,7 +650,7 @@ fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []co...@@ -622,7 +650,7 @@ fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []co
622 .array_end => unreachable, // type errors happen at the start of a value.650 .array_end => unreachable, // type errors happen at the start of a value.
623 .end_of_document => unreachable, // type errors happen at the start of a value.651 .end_of_document => unreachable, // type errors happen at the start of a value.
624 };652 };
625 diag.recordContext(s);653 diag.setMessage(s);
626 }654 }
627 return error.UnexpectedToken;655 return error.UnexpectedToken;
628}656}
...@@ -880,7 +908,7 @@ fn fillDefaultStructValues(comptime T: type, options: ParseOptions, r: *T, field...@@ -880,7 +908,7 @@ fn fillDefaultStructValues(comptime T: type, options: ParseOptions, r: *T, field
880 const default = @as(*align(1) const field.type, @ptrCast(default_ptr)).*;908 const default = @as(*align(1) const field.type, @ptrCast(default_ptr)).*;
881 @field(r, field.name) = default;909 @field(r, field.name) = default;
882 } else {910 } 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);
884 return error.MissingField;912 return error.MissingField;
885 }913 }
886 }914 }