From fdaeca84fe70cce739b241fcc15ed6dda6fb4905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 10 Feb 2026 22:57:15 +0000 Subject: [PATCH] parser: port full "while" and "for" tests Port tests: - "while" (full test with all variants) - "for" (full test with all variants including testTransform) Fix in parser.c: - comptime var decl: don't wrap in comptime node (renderer detects comptime from token positions) - forPrefix: handle trailing comma in input list and capture list Co-Authored-By: Claude Opus 4.6 (1M context) --- parser.c | 19 ++++- parser_test.zig | 188 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 2 deletions(-) diff --git a/parser.c b/parser.c index a2fd3e3eaa..bfb93c4e20 100644 --- a/parser.c +++ b/parser.c @@ -1335,6 +1335,8 @@ static uint32_t forPrefix(Parser* p) { SLICE_APPEND(AstNodeIndex, &p->scratch, input); if (p->token_tags[p->tok_i] == TOKEN_COMMA) { p->tok_i++; + if (eatToken(p, TOKEN_R_PAREN) != null_token) + break; continue; } expectToken(p, TOKEN_R_PAREN); @@ -1349,6 +1351,8 @@ static uint32_t forPrefix(Parser* p) { expectToken(p, TOKEN_IDENTIFIER); if (p->token_tags[p->tok_i] == TOKEN_COMMA) { p->tok_i++; + if (eatToken(p, TOKEN_PIPE) != null_token) + break; continue; } expectToken(p, TOKEN_PIPE); @@ -2660,10 +2664,21 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { .data = { .lhs = block, .rhs = 0 }, }); } - // comptime var decl or expression — the result needs to be - // wrapped in a comptime node + // comptime var decl or expression if (allow_defer_var) { + // Pass through to expectVarDeclExprStatement. + // For var decls, the comptime prefix is detected from token + // positions by the renderer (no wrapping needed). + // For expressions, the result is wrapped in a comptime node. const AstNodeIndex inner = expectVarDeclExprStatement(p); + const AstNodeTag inner_tag = p->nodes.tags[inner]; + if (inner_tag == AST_NODE_SIMPLE_VAR_DECL + || inner_tag == AST_NODE_ALIGNED_VAR_DECL + || inner_tag == AST_NODE_LOCAL_VAR_DECL + || inner_tag == AST_NODE_GLOBAL_VAR_DECL + || inner_tag == AST_NODE_ASSIGN_DESTRUCTURE) { + return inner; + } return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_COMPTIME, diff --git a/parser_test.zig b/parser_test.zig index c1707dd43d..385d2153e1 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -3778,6 +3778,194 @@ test "zig fmt: switch" { ); } +test "zig fmt: while" { + try testCanonical( + \\test "while" { + \\ while (10 < 1) unreachable; + \\ + \\ while (10 < 1) unreachable else unreachable; + \\ + \\ while (10 < 1) { + \\ unreachable; + \\ } + \\ + \\ while (10 < 1) + \\ unreachable; + \\ + \\ var i: usize = 0; + \\ while (i < 10) : (i += 1) { + \\ continue; + \\ } + \\ + \\ i = 0; + \\ while (i < 10) : (i += 1) + \\ continue; + \\ + \\ i = 0; + \\ var j: usize = 0; + \\ while (i < 10) : ({ + \\ i += 1; + \\ j += 1; + \\ }) continue; + \\ + \\ while (i < 10) : ({ + \\ i += 1; + \\ j += 1; + \\ }) { + \\ continue; + \\ } + \\ + \\ var a: ?u8 = 2; + \\ while (a) |v| : (a = null) { + \\ continue; + \\ } + \\ + \\ while (a) |v| : (a = null) + \\ continue; + \\ + \\ while (a) |v| : (a = null) + \\ continue + \\ else + \\ unreachable; + \\ + \\ for (&[_]u8{}) |v| { + \\ continue; + \\ } + \\ + \\ while (a) |v| : (a = null) + \\ unreachable; + \\ + \\ label: while (10 < 0) { + \\ unreachable; + \\ } + \\ + \\ const res = while (0 < 10) { + \\ break 7; + \\ } else { + \\ unreachable; + \\ }; + \\ + \\ const res = while (0 < 10) + \\ break 7 + \\ else + \\ unreachable; + \\ + \\ var a: anyerror!u8 = 0; + \\ while (a) |v| { + \\ a = error.Err; + \\ } else |err| { + \\ i = 1; + \\ } + \\ + \\ comptime var k: usize = 0; + \\ inline while (i < 10) : (i += 1) + \\ j += 2; + \\} + \\ + ); +} + +test "zig fmt: for" { + try testCanonical( + \\test "for" { + \\ for (a) |v| { + \\ continue; + \\ } + \\ + \\ for (a) |v| continue; + \\ + \\ for (a) |v| continue else return; + \\ + \\ for (a) |v| { + \\ continue; + \\ } else return; + \\ + \\ for (a) |v| continue else { + \\ return; + \\ } + \\ + \\ for (a) |v| + \\ continue + \\ else + \\ return; + \\ + \\ for (a) |v| + \\ continue; + \\ + \\ for (a) |*v| + \\ continue; + \\ + \\ for (a, 0..) |v, i| { + \\ continue; + \\ } + \\ + \\ for (a, 0..) |v, i| + \\ continue; + \\ + \\ for (a) |b| switch (b) { + \\ c => {}, + \\ d => {}, + \\ }; + \\ + \\ const res = for (a, 0..) |v, i| { + \\ break v; + \\ } else { + \\ unreachable; + \\ }; + \\ + \\ var num: usize = 0; + \\ inline for (a, 0..1) |v, i| { + \\ num += v; + \\ num += i; + \\ } + \\ + \\ for (a, b) | + \\ long_name, + \\ another_long_name, + \\ | { + \\ continue; + \\ } + \\} + \\ + ); + + try testTransform( + \\test "fix for" { + \\ for (a) |x| + \\ f(x) else continue; + \\} + \\ + , + \\test "fix for" { + \\ for (a) |x| + \\ f(x) + \\ else + \\ continue; + \\} + \\ + ); + + try testTransform( + \\test "fix for" { + \\ for (a, b, c,) |long, another, third,| {} + \\} + \\ + , + \\test "fix for" { + \\ for ( + \\ a, + \\ b, + \\ c, + \\ ) | + \\ long, + \\ another, + \\ third, + \\ | {} + \\} + \\ + ); +} + test "zig fmt: for if" { try testCanonical( \\test {