commit fdaeca84fe70cce739b241fcc15ed6dda6fb4905 (tree)
parent 571fb20bb723308254e191b8c32b18272886e559
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Tue, 10 Feb 2026 22:57:15 +0000
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) <noreply@anthropic.com>
Diffstat:
| M | parser.c | | | 19 | +++++++++++++++++-- |
| M | parser_test.zig | | | 188 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 205 insertions(+), 2 deletions(-)
diff --git 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
@@ -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 {