| author | |
| committer | |
| log | e21ced91e30d8bc943ebe0a1f1f6d3bc6821740e |
| tree | 4655fd293313e5cab24cdefe7b95fc498f92a29a |
| parent | ac6930544f812582df10181210e80675ce6a42ee |
| signature |
Currently Parse.zig gives a parse error when the number of for loop
inputs does not match the number of for loop captures. This property
is however both overly complex to specify in the formal grammar and not
necessary to make further parsing possible.
This eliminates yet another discrepancy between the formal grammar
and Parse.zig implementation that has been discovered through fuzzing
with AFL++.4 files changed, 13 insertions(+), 21 deletions(-)
lib/std/zig/Ast.zig-8| ... | @@ -559,12 +559,6 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void | ... | @@ -559,12 +559,6 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void |
| 559 | .var_const_decl => { | 559 | .var_const_decl => { |
| 560 | return w.writeAll("use 'var' or 'const' to declare variable"); | 560 | return w.writeAll("use 'var' or 'const' to declare variable"); |
| 561 | }, | 561 | }, |
| 562 | .extra_for_capture => { | ||
| 563 | return w.writeAll("extra capture in for loop"); | ||
| 564 | }, | ||
| 565 | .for_input_not_captured => { | ||
| 566 | return w.writeAll("for input is not captured"); | ||
| 567 | }, | ||
| 568 | 562 | ||
| 569 | .invalid_byte => { | 563 | .invalid_byte => { |
| 570 | const tok_slice = tree.source[tree.tokens.items(.start)[parse_error.token]..]; | 564 | const tok_slice = tree.source[tree.tokens.items(.start)[parse_error.token]..]; |
| ... | @@ -2889,8 +2883,6 @@ pub const Error = struct { | ... | @@ -2889,8 +2883,6 @@ pub const Error = struct { |
| 2889 | expected_var_const, | 2883 | expected_var_const, |
| 2890 | wrong_equal_var_decl, | 2884 | wrong_equal_var_decl, |
| 2891 | var_const_decl, | 2885 | var_const_decl, |
| 2892 | extra_for_capture, | ||
| 2893 | for_input_not_captured, | ||
| 2894 | 2886 | ||
| 2895 | zig_style_container, | 2887 | zig_style_container, |
| 2896 | previous_field, | 2888 | previous_field, |
lib/std/zig/AstGen.zig+7| ... | @@ -6708,6 +6708,9 @@ fn forExpr( | ... | @@ -6708,6 +6708,9 @@ fn forExpr( |
| 6708 | for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_refs| { | 6708 | for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_refs| { |
| 6709 | const capture_is_ref = tree.tokenTag(capture_token) == .asterisk; | 6709 | const capture_is_ref = tree.tokenTag(capture_token) == .asterisk; |
| 6710 | const ident_tok = capture_token + @intFromBool(capture_is_ref); | 6710 | const ident_tok = capture_token + @intFromBool(capture_is_ref); |
| 6711 | if (tree.tokenTag(ident_tok) != .identifier) { | ||
| 6712 | return astgen.failNode(input, "for input is not captured", .{}); | ||
| 6713 | } | ||
| 6711 | const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_"); | 6714 | const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_"); |
| 6712 | 6715 | ||
| 6713 | if (is_discard and capture_is_ref) { | 6716 | if (is_discard and capture_is_ref) { |
| ... | @@ -6750,6 +6753,10 @@ fn forExpr( | ... | @@ -6750,6 +6753,10 @@ fn forExpr( |
| 6750 | len_refs.* = .{ indexable, .none }; | 6753 | len_refs.* = .{ indexable, .none }; |
| 6751 | } | 6754 | } |
| 6752 | } | 6755 | } |
| 6756 | // There may or may not be a trailing comma after the final capture | ||
| 6757 | if (tree.tokenTag(capture_token) != .pipe and tree.tokenTag(capture_token - 1) != .pipe) { | ||
| 6758 | return astgen.failTok(capture_token, "extra capture in for loop", .{}); | ||
| 6759 | } | ||
| 6753 | } | 6760 | } |
| 6754 | 6761 | ||
| 6755 | if (!any_len_checks) { | 6762 | if (!any_len_checks) { |
lib/std/zig/Parse.zig+1-13| ... | @@ -2106,16 +2106,9 @@ fn forPrefix(p: *Parse) Error!usize { | ... | @@ -2106,16 +2106,9 @@ fn forPrefix(p: *Parse) Error!usize { |
| 2106 | return inputs; | 2106 | return inputs; |
| 2107 | }; | 2107 | }; |
| 2108 | 2108 | ||
| 2109 | var warned_excess = false; | ||
| 2110 | var captures: u32 = 0; | ||
| 2111 | while (true) { | 2109 | while (true) { |
| 2112 | _ = p.eatToken(.asterisk); | 2110 | _ = p.eatToken(.asterisk); |
| 2113 | const identifier = try p.expectToken(.identifier); | 2111 | _ = try p.expectToken(.identifier); |
| 2114 | captures += 1; | ||
| 2115 | if (captures > inputs and !warned_excess) { | ||
| 2116 | try p.warnMsg(.{ .tag = .extra_for_capture, .token = identifier }); | ||
| 2117 | warned_excess = true; | ||
| 2118 | } | ||
| 2119 | switch (p.tokenTag(p.tok_i)) { | 2112 | switch (p.tokenTag(p.tok_i)) { |
| 2120 | .comma => p.tok_i += 1, | 2113 | .comma => p.tok_i += 1, |
| 2121 | .pipe => { | 2114 | .pipe => { |
| ... | @@ -2128,11 +2121,6 @@ fn forPrefix(p: *Parse) Error!usize { | ... | @@ -2128,11 +2121,6 @@ fn forPrefix(p: *Parse) Error!usize { |
| 2128 | if (p.eatToken(.pipe)) |_| break; | 2121 | if (p.eatToken(.pipe)) |_| break; |
| 2129 | } | 2122 | } |
| 2130 | 2123 | ||
| 2131 | if (captures < inputs) { | ||
| 2132 | const index = p.scratch.items.len - captures; | ||
| 2133 | const input = p.nodeMainToken(p.scratch.items[index]); | ||
| 2134 | try p.warnMsg(.{ .tag = .for_input_not_captured, .token = input }); | ||
| 2135 | } | ||
| 2136 | return inputs; | 2124 | return inputs; |
| 2137 | } | 2125 | } |
| 2138 | 2126 |
lib/std/zig/parser_fuzz.zig+5| ... | @@ -52,6 +52,11 @@ test "newline required before doc comment not at start of file" { | ... | @@ -52,6 +52,11 @@ test "newline required before doc comment not at start of file" { |
| 52 | try checkAgainstOracle("///\ntest {}"); | 52 | try checkAgainstOracle("///\ntest {}"); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | // Found using AFL++ | ||
| 56 | test "extra capture in for loop" { | ||
| 57 | try checkAgainstOracle("for(0)|t,r|0"); | ||
| 58 | } | ||
| 59 | |||
| 55 | fn checkAgainstOracle(source: [:0]const u8) !void { | 60 | fn checkAgainstOracle(source: [:0]const u8) !void { |
| 56 | var fba_buf: [1 << 18]u8 = undefined; | 61 | var fba_buf: [1 << 18]u8 = undefined; |
| 57 | var fba: std.heap.FixedBufferAllocator = .init(&fba_buf); | 62 | var fba: std.heap.FixedBufferAllocator = .init(&fba_buf); |