| ... | @@ -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 | }; |
| 291 | | 294 | |
| 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 | } |
| 316 | | 323 | |
| 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 | } |
| 318 | | 328 | |
| 319 | return result.?; | 329 | return result.?; |
| 320 | }, | 330 | }, |
| ... | @@ -328,11 +338,23 @@ pub fn innerParse( | ... | @@ -328,11 +338,23 @@ pub fn innerParse( |
| 328 | | 338 | |
| 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 | } |
| 334 | | 350 | |
| 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 | } |
| 336 | | 358 | |
| 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 | }, |
| 399 | | 425 | |
| ... | @@ -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 | }, |
| 738 | | 765 | |
| ... | @@ -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 | } |
| 848 | | 875 | |
| 849 | fn fillDefaultStructValues(comptime T: type, r: *T, fields_seen: *[@typeInfo(T).Struct.fields.len]bool) !void { | 876 | fn 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 | } |