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>
This commit is contained in:
2026-02-10 22:57:15 +00:00
parent 571fb20bb7
commit fdaeca84fe
2 changed files with 205 additions and 2 deletions

View File

@@ -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,

View File

@@ -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 {