| ... | @@ -1454,6 +1454,8 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: | ... | @@ -1454,6 +1454,8 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: |
| 1454 | // Parsing some types won't have OutOfMemory in their | 1454 | // Parsing some types won't have OutOfMemory in their |
| 1455 | // error-sets, for the condition to be valid, merge it in. | 1455 | // error-sets, for the condition to be valid, merge it in. |
| 1456 | if (@as(@TypeOf(err) || error{OutOfMemory}, err) == error.OutOfMemory) return err; | 1456 | if (@as(@TypeOf(err) || error{OutOfMemory}, err) == error.OutOfMemory) return err; |
| | 1457 | // Bubble up AllocatorRequired, as it indicates missing option |
| | 1458 | if (@as(@TypeOf(err) || error{AllocatorRequired}, err) == error.AllocatorRequired) return err; |
| 1457 | // otherwise continue through the `inline for` | 1459 | // otherwise continue through the `inline for` |
| 1458 | } | 1460 | } |
| 1459 | } | 1461 | } |
| ... | @@ -1741,18 +1743,6 @@ test "parse into tagged union" { | ... | @@ -1741,18 +1743,6 @@ test "parse into tagged union" { |
| 1741 | testing.expectEqual(T{ .float = 1.5 }, try parse(T, &TokenStream.init("1.5"), ParseOptions{})); | 1743 | testing.expectEqual(T{ .float = 1.5 }, try parse(T, &TokenStream.init("1.5"), ParseOptions{})); |
| 1742 | } | 1744 | } |
| 1743 | | 1745 | |
| 1744 | { // if union matches string member, fails with NoUnionMembersMatched rather than AllocatorRequired | | |
| 1745 | // Note that this behaviour wasn't necessarily by design, but was | | |
| 1746 | // what fell out of the implementation and may result in interesting | | |
| 1747 | // API breakage if changed | | |
| 1748 | const T = union(enum) { | | |
| 1749 | int: i32, | | |
| 1750 | float: f64, | | |
| 1751 | string: []const u8, | | |
| 1752 | }; | | |
| 1753 | testing.expectError(error.NoUnionMembersMatched, parse(T, &TokenStream.init("\"foo\""), ParseOptions{})); | | |
| 1754 | } | | |
| 1755 | | | |
| 1756 | { // failing allocations should be bubbled up instantly without trying next member | 1746 | { // failing allocations should be bubbled up instantly without trying next member |
| 1757 | var fail_alloc = testing.FailingAllocator.init(testing.allocator, 0); | 1747 | var fail_alloc = testing.FailingAllocator.init(testing.allocator, 0); |
| 1758 | const options = ParseOptions{ .allocator = &fail_alloc.allocator }; | 1748 | const options = ParseOptions{ .allocator = &fail_alloc.allocator }; |
| ... | @@ -1782,6 +1772,25 @@ test "parse into tagged union" { | ... | @@ -1782,6 +1772,25 @@ test "parse into tagged union" { |
| 1782 | } | 1772 | } |
| 1783 | } | 1773 | } |
| 1784 | | 1774 | |
| | 1775 | test "parse union bubbles up AllocatorRequired" { |
| | 1776 | { // string member first in union (and not matching) |
| | 1777 | const T = union(enum) { |
| | 1778 | string: []const u8, |
| | 1779 | int: i32, |
| | 1780 | }; |
| | 1781 | testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("42"), ParseOptions{})); |
| | 1782 | } |
| | 1783 | |
| | 1784 | { // string member not first in union (and matching) |
| | 1785 | const T = union(enum) { |
| | 1786 | int: i32, |
| | 1787 | float: f64, |
| | 1788 | string: []const u8, |
| | 1789 | }; |
| | 1790 | testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("\"foo\""), ParseOptions{})); |
| | 1791 | } |
| | 1792 | } |
| | 1793 | |
| 1785 | test "parseFree descends into tagged union" { | 1794 | test "parseFree descends into tagged union" { |
| 1786 | var fail_alloc = testing.FailingAllocator.init(testing.allocator, 1); | 1795 | var fail_alloc = testing.FailingAllocator.init(testing.allocator, 1); |
| 1787 | const options = ParseOptions{ .allocator = &fail_alloc.allocator }; | 1796 | const options = ParseOptions{ .allocator = &fail_alloc.allocator }; |