authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-06-15 13:00:01+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-08 11:48:58+02:00
loge539f7bbebc8016ea05b9203c8be371db8b2e90d
treed98fcc99e470cd871d8f7b66ee4c44a475080b26
parent153fcc4e9c7dfea91472a9456d95ff5abb3b8649
signaturelock-open Commit is signed but in an unrecognized format.

grammar: forbid && as op without whitespace between

This matches the Parse.zig implementation, which has a special parse error for this case: > ambiguous use of '&&'; use 'and' for logical AND, or change whitespace > to ' & &' for bitwise AND

3 files changed, 20 insertions(+), 2 deletions(-)

doc/langref/grammar.peg+1-1
...@@ -272,7 +272,7 @@ CompareOpTok...@@ -272,7 +272,7 @@ CompareOpTok
272BitwiseOp <- pre_op_white BitwiseOpTok post_op_white272BitwiseOp <- pre_op_white BitwiseOpTok post_op_white
273 / !pre_op_white BitwiseOpTok !post_op_white273 / !pre_op_white BitwiseOpTok !post_op_white
274BitwiseOpTok274BitwiseOpTok
275 <- AMPERSAND275 <- AMPERSAND ![&]
276 / CARET276 / CARET
277 / PIPE277 / PIPE
278 / KEYWORD_orelse278 / KEYWORD_orelse
lib/std/zig/parser_fuzz.zig+6
...@@ -30,6 +30,12 @@ test "doc comment or division operator" {...@@ -30,6 +30,12 @@ test "doc comment or division operator" {
30 try checkAgainstOracle("0=0///\n0");30 try checkAgainstOracle("0=0///\n0");
31}31}
3232
33// Found using AFL++
34test "double ampersand" {
35 try checkAgainstOracle("0=0&&0"); // error
36 try checkAgainstOracle("test{&&0;}"); // ok
37}
38
33fn checkAgainstOracle(source: [:0]const u8) !void {39fn checkAgainstOracle(source: [:0]const u8) !void {
34 var fba_buf: [1 << 18]u8 = undefined;40 var fba_buf: [1 << 18]u8 = undefined;
35 var fba: std.heap.FixedBufferAllocator = .init(&fba_buf);41 var fba: std.heap.FixedBufferAllocator = .init(&fba_buf);
lib/std/zig/parser_generated_oracle.zig+13-1
...@@ -1528,7 +1528,19 @@ const Parser = struct {...@@ -1528,7 +1528,19 @@ const Parser = struct {
1528 pub fn parseBitwiseOpTok(p: *Parser) bool {1528 pub fn parseBitwiseOpTok(p: *Parser) bool {
1529 return blk_0: {1529 return blk_0: {
1530 const pos_0 = p.i;1530 const pos_0 = p.i;
1531 if (p.parseAMPERSAND()) break :blk_0 true;1531 if (p.parseAMPERSAND() and blk_1: {
1532 const pos_1 = p.i;
1533 const match_1 = (p.i < p.source.len and switch (p.source[p.i]) {
1534 '&'...'&',
1535 => blk_2: {
1536 p.i += 1;
1537 break :blk_2 true;
1538 },
1539 else => false,
1540 });
1541 p.i = pos_1;
1542 break :blk_1 !match_1;
1543 }) break :blk_0 true;
1532 p.i = pos_0;1544 p.i = pos_0;
1533 if (p.parseCARET()) break :blk_0 true;1545 if (p.parseCARET()) break :blk_0 true;
1534 p.i = pos_0;1546 p.i = pos_0;