| author | |
| committer | |
| log | d45de6d4bd0052fa0187b0ea4e6c274fded7bdc0 |
| tree | 324244b3924c22dfe1871f7ff1d1171925dd83bb |
| parent | f2328d520860049f542539e4e28c401970870f84 |
| signature |
Consider the new test case (found by AFL++):
test{for(0)|t|0,const w=0;}
Currently the grammar backtracks after failing to parse this with the
ForStatement rule and instead finds that the VarAssignStatement rule
is a match. This behavior is not LL(k) and inconsistent with the parser.
Add the required negative lookahead to eliminate this case and similar
cases.4 files changed, 57 insertions(+), 25 deletions(-)
doc/langref/grammar.peg+8-2| ... | @@ -77,7 +77,7 @@ BlockStatement | ... | @@ -77,7 +77,7 @@ BlockStatement |
| 77 | <- Statement | 77 | <- Statement |
| 78 | / KEYWORD_defer BlockExprStatement | 78 | / KEYWORD_defer BlockExprStatement |
| 79 | / KEYWORD_errdefer BlockExprStatement | 79 | / KEYWORD_errdefer BlockExprStatement |
| 80 | / !KEYWORD_nosuspend (KEYWORD_comptime !BlockExprPrefix)? VarAssignStatement | 80 | / !StatementPrefix KEYWORD_comptime? VarAssignStatement |
| 81 | 81 | ||
| 82 | Statement | 82 | Statement |
| 83 | <- IfStatement | 83 | <- IfStatement |
| ... | @@ -85,7 +85,13 @@ Statement | ... | @@ -85,7 +85,13 @@ Statement |
| 85 | / KEYWORD_nosuspend BlockExprStatement | 85 | / KEYWORD_nosuspend BlockExprStatement |
| 86 | / KEYWORD_comptime BlockExpr | 86 | / KEYWORD_comptime BlockExpr |
| 87 | / KEYWORD_suspend BlockExprStatement | 87 | / KEYWORD_suspend BlockExprStatement |
| 88 | / (KEYWORD_comptime !BlockExprPrefix)? AssignExpr SEMICOLON | 88 | / !StatementPrefix KEYWORD_comptime? AssignExpr SEMICOLON |
| 89 | |||
| 90 | StatementPrefix | ||
| 91 | <- KEYWORD_if | ||
| 92 | / BlockLabel? (LBRACE / KEYWORD_inline? (KEYWORD_for / KEYWORD_while) / KEYWORD_switch) | ||
| 93 | / KEYWORD_nosuspend | ||
| 94 | / KEYWORD_comptime BlockExprPrefix | ||
| 89 | 95 | ||
| 90 | IfStatement | 96 | IfStatement |
| 91 | <- IfPrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else) | 97 | <- IfPrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else) |
lib/std/zig/Parse.zig+8-2| ... | @@ -956,7 +956,7 @@ fn expectContainerField(p: *Parse) !Node.Index { | ... | @@ -956,7 +956,7 @@ fn expectContainerField(p: *Parse) !Node.Index { |
| 956 | /// <- Statement | 956 | /// <- Statement |
| 957 | /// / KEYWORD_defer BlockExprStatement | 957 | /// / KEYWORD_defer BlockExprStatement |
| 958 | /// / KEYWORD_errdefer BlockExprStatement | 958 | /// / KEYWORD_errdefer BlockExprStatement |
| 959 | /// / (KEYWORD_comptime !BlockExprPrefix)? VarAssignStatement | 959 | /// / !StatementPrefix KEYWORD_comptime? VarAssignStatement |
| 960 | /// | 960 | /// |
| 961 | /// Statement | 961 | /// Statement |
| 962 | /// <- IfStatement | 962 | /// <- IfStatement |
| ... | @@ -964,7 +964,13 @@ fn expectContainerField(p: *Parse) !Node.Index { | ... | @@ -964,7 +964,13 @@ fn expectContainerField(p: *Parse) !Node.Index { |
| 964 | /// / KEYWORD_nosuspend BlockExprStatement | 964 | /// / KEYWORD_nosuspend BlockExprStatement |
| 965 | /// / KEYWORD_comptime BlockExpr | 965 | /// / KEYWORD_comptime BlockExpr |
| 966 | /// / KEYWORD_suspend BlockExprStatement | 966 | /// / KEYWORD_suspend BlockExprStatement |
| 967 | /// / (KEYWORD_comptime !BlockExprPrefix)? AssignExpr SEMICOLON | 967 | /// / !StatementPrefix KEYWORD_comptime? AssignExpr SEMICOLON |
| 968 | /// | ||
| 969 | /// StatementPrefix | ||
| 970 | /// <- KEYWORD_if | ||
| 971 | /// / BlockLabel? (LBRACE / KEYWORD_inline? (KEYWORD_for / KEYWORD_while) / KEYWORD_switch) | ||
| 972 | /// / KEYWORD_nosuspend | ||
| 973 | /// / KEYWORD_comptime BlockExprPrefix | ||
| 968 | fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { | 974 | fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { |
| 969 | if (p.eatToken(.keyword_comptime)) |comptime_token| { | 975 | if (p.eatToken(.keyword_comptime)) |comptime_token| { |
| 970 | const opt_block_expr = try p.parseBlockExpr(); | 976 | const opt_block_expr = try p.parseBlockExpr(); |
lib/std/zig/parser_fuzz.zig+5| ... | @@ -153,6 +153,11 @@ test "resume block chained compare ops" { | ... | @@ -153,6 +153,11 @@ test "resume block chained compare ops" { |
| 153 | try checkAgainstOracle("test{resume{0 > 0;} > 0 > 0;}"); | 153 | try checkAgainstOracle("test{resume{0 > 0;} > 0 > 0;}"); |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | // Found using AFL++ | ||
| 157 | test "for multiassign" { | ||
| 158 | try checkAgainstOracle("test{for(0)|t|0,const w=0;}"); | ||
| 159 | } | ||
| 160 | |||
| 156 | fn checkAgainstOracle(source: [:0]const u8) !void { | 161 | fn checkAgainstOracle(source: [:0]const u8) !void { |
| 157 | var fba_buf: [1 << 18]u8 = undefined; | 162 | var fba_buf: [1 << 18]u8 = undefined; |
| 158 | var fba: std.heap.FixedBufferAllocator = .init(&fba_buf); | 163 | var fba: std.heap.FixedBufferAllocator = .init(&fba_buf); |
lib/std/zig/parser_generated_oracle.zig+36-21| ... | @@ -270,20 +270,10 @@ const Parser = struct { | ... | @@ -270,20 +270,10 @@ const Parser = struct { |
| 270 | p.i = pos_0; | 270 | p.i = pos_0; |
| 271 | if (blk_1: { | 271 | if (blk_1: { |
| 272 | const pos_1 = p.i; | 272 | const pos_1 = p.i; |
| 273 | const match_1 = try p.parseKEYWORD_nosuspend(); | 273 | const match_1 = try p.parseStatementPrefix(); |
| 274 | p.i = pos_1; | 274 | p.i = pos_1; |
| 275 | break :blk_1 !match_1; | 275 | break :blk_1 !match_1; |
| 276 | } and (blk_3: { | 276 | } and (try p.parseKEYWORD_comptime() or true) and try p.parseVarAssignStatement()) break :blk_0 true; |
| 277 | const pos_3 = p.i; | ||
| 278 | if (try p.parseKEYWORD_comptime() and blk_4: { | ||
| 279 | const pos_4 = p.i; | ||
| 280 | const match_4 = try p.parseBlockExprPrefix(); | ||
| 281 | p.i = pos_4; | ||
| 282 | break :blk_4 !match_4; | ||
| 283 | }) break :blk_3 true; | ||
| 284 | p.i = pos_3; | ||
| 285 | break :blk_3 false; | ||
| 286 | } or true) and try p.parseVarAssignStatement()) break :blk_0 true; | ||
| 287 | p.i = pos_0; | 277 | p.i = pos_0; |
| 288 | break :blk_0 false; | 278 | break :blk_0 false; |
| 289 | }; | 279 | }; |
| ... | @@ -301,17 +291,42 @@ const Parser = struct { | ... | @@ -301,17 +291,42 @@ const Parser = struct { |
| 301 | p.i = pos_0; | 291 | p.i = pos_0; |
| 302 | if (try p.parseKEYWORD_suspend() and try p.parseBlockExprStatement()) break :blk_0 true; | 292 | if (try p.parseKEYWORD_suspend() and try p.parseBlockExprStatement()) break :blk_0 true; |
| 303 | p.i = pos_0; | 293 | p.i = pos_0; |
| 304 | if ((blk_3: { | 294 | if (blk_1: { |
| 305 | const pos_3 = p.i; | 295 | const pos_1 = p.i; |
| 306 | if (try p.parseKEYWORD_comptime() and blk_4: { | 296 | const match_1 = try p.parseStatementPrefix(); |
| 297 | p.i = pos_1; | ||
| 298 | break :blk_1 !match_1; | ||
| 299 | } and (try p.parseKEYWORD_comptime() or true) and try p.parseAssignExpr() and try p.parseSEMICOLON()) break :blk_0 true; | ||
| 300 | p.i = pos_0; | ||
| 301 | break :blk_0 false; | ||
| 302 | }; | ||
| 303 | } | ||
| 304 | pub fn parseStatementPrefix(p: *Parser) Error!bool { | ||
| 305 | return blk_0: { | ||
| 306 | const pos_0 = p.i; | ||
| 307 | if (try p.parseKEYWORD_if()) break :blk_0 true; | ||
| 308 | p.i = pos_0; | ||
| 309 | if ((try p.parseBlockLabel() or true) and blk_2: { | ||
| 310 | const pos_2 = p.i; | ||
| 311 | if (try p.parseLBRACE()) break :blk_2 true; | ||
| 312 | p.i = pos_2; | ||
| 313 | if ((try p.parseKEYWORD_inline() or true) and blk_4: { | ||
| 307 | const pos_4 = p.i; | 314 | const pos_4 = p.i; |
| 308 | const match_4 = try p.parseBlockExprPrefix(); | 315 | if (try p.parseKEYWORD_for()) break :blk_4 true; |
| 309 | p.i = pos_4; | 316 | p.i = pos_4; |
| 310 | break :blk_4 !match_4; | 317 | if (try p.parseKEYWORD_while()) break :blk_4 true; |
| 311 | }) break :blk_3 true; | 318 | p.i = pos_4; |
| 312 | p.i = pos_3; | 319 | break :blk_4 false; |
| 313 | break :blk_3 false; | 320 | }) break :blk_2 true; |
| 314 | } or true) and try p.parseAssignExpr() and try p.parseSEMICOLON()) break :blk_0 true; | 321 | p.i = pos_2; |
| 322 | if (try p.parseKEYWORD_switch()) break :blk_2 true; | ||
| 323 | p.i = pos_2; | ||
| 324 | break :blk_2 false; | ||
| 325 | }) break :blk_0 true; | ||
| 326 | p.i = pos_0; | ||
| 327 | if (try p.parseKEYWORD_nosuspend()) break :blk_0 true; | ||
| 328 | p.i = pos_0; | ||
| 329 | if (try p.parseKEYWORD_comptime() and try p.parseBlockExprPrefix()) break :blk_0 true; | ||
| 315 | p.i = pos_0; | 330 | p.i = pos_0; |
| 316 | break :blk_0 false; | 331 | break :blk_0 false; |
| 317 | }; | 332 | }; |