parser: add container declaration and test decl tests

Port tests from upstream parser_test.zig:
- "container declaration, one item, multi line trailing comma"
- "container declaration, no trailing comma on separate line"
- "container declaration, line break, no trailing comma"
- "container declaration, transform trailing comma"
- "container declaration, comment, add trailing comma"
- "container declaration, multiline string, add trailing comma"
- "container declaration, doc comment on member, add trailing comma"
- "remove empty lines at start/end of container decl"

Implement in parser.c:
- Test declarations in parseContainerMembers
- Comptime block/var statements in expectStatement
- Variable declaration with initializer in expectVarDeclExprStatement
- Regular assignment expressions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-02-10 12:25:25 +00:00
parent 8d193346e8
commit b186471328
2 changed files with 203 additions and 4 deletions

View File

@@ -1281,9 +1281,34 @@ static AstNodeIndex expectVarDeclExprStatement(Parser* p) {
case TOKEN_SEMICOLON:
p->tok_i++;
return lhs;
case TOKEN_EQUAL: {
// Check if lhs is a var decl that needs initialization
const AstNodeTag lhs_tag = p->nodes.tags[lhs];
if (lhs_tag == AST_NODE_SIMPLE_VAR_DECL
|| lhs_tag == AST_NODE_ALIGNED_VAR_DECL
|| lhs_tag == AST_NODE_LOCAL_VAR_DECL
|| lhs_tag == AST_NODE_GLOBAL_VAR_DECL) {
p->tok_i++;
p->nodes.datas[lhs].rhs = expectExpr(p);
expectSemicolon(p);
return lhs;
}
// Regular assignment expression
const AstTokenIndex eq_token = nextToken(p);
const AstNodeIndex rhs = expectExpr(p);
expectSemicolon(p);
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_ASSIGN,
.main_token = eq_token,
.data = { .lhs = lhs, .rhs = rhs },
});
}
default:
fprintf(stderr,
"expectVarDeclExprStatement: assignment not implemented\n");
"expectVarDeclExprStatement: assignment not implemented "
"for token %s\n",
tokenizerGetTagString(p->token_tags[p->tok_i]));
exit(1);
}
}
@@ -1295,8 +1320,24 @@ static AstNodeIndex expectVarDeclExprStatement(Parser* p) {
}
static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
if (eatToken(p, TOKEN_KEYWORD_COMPTIME) != null_token) {
fprintf(stderr, "expectStatement: comptime keyword not supported\n");
const AstTokenIndex comptime_token = eatToken(p, TOKEN_KEYWORD_COMPTIME);
if (comptime_token != null_token) {
// comptime followed by block => comptime block statement
const AstNodeIndex block = parseBlock(p);
if (block != 0) {
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_COMPTIME,
.main_token = comptime_token,
.data = { .lhs = block, .rhs = 0 },
});
}
// comptime var decl or expression
if (allow_defer_var) {
return expectVarDeclExprStatement(p);
}
fprintf(
stderr, "expectStatement: comptime keyword not supported here\n");
exit(1);
}
@@ -1561,7 +1602,26 @@ static Members parseContainerMembers(Parser* p) {
while (1) {
eatDocComments(p);
switch (p->token_tags[p->tok_i]) {
case TOKEN_KEYWORD_TEST:
case TOKEN_KEYWORD_TEST: {
const AstTokenIndex test_token = nextToken(p);
// test name can be a string literal or identifier, or omitted
const AstTokenIndex test_name
= (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL
|| p->token_tags[p->tok_i] == TOKEN_IDENTIFIER)
? nextToken(p)
: 0;
const AstNodeIndex body = parseBlock(p);
assert(body != 0);
const AstNodeIndex test_decl = addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_TEST_DECL,
.main_token = test_token,
.data = { .lhs = test_name, .rhs = body },
});
SLICE_APPEND(AstNodeIndex, &p->scratch, test_decl);
trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE;
break;
}
case TOKEN_KEYWORD_USINGNAMESPACE:;
const char* str = tokenizerGetTagString(p->token_tags[p->tok_i]);
fprintf(

View File

@@ -734,3 +734,142 @@ test "zig fmt: container declaration, single line" {
\\
);
}
test "zig fmt: container declaration, one item, multi line trailing comma" {
try testCanonical(
\\test "" {
\\ comptime {
\\ const X = struct {
\\ x: i32,
\\ };
\\ }
\\}
\\
);
}
test "zig fmt: container declaration, no trailing comma on separate line" {
try testTransform(
\\test "" {
\\ comptime {
\\ const X = struct {
\\ x: i32
\\ };
\\ }
\\}
\\
,
\\test "" {
\\ comptime {
\\ const X = struct { x: i32 };
\\ }
\\}
\\
);
}
test "zig fmt: container declaration, line break, no trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: i32, bar: i8 };
,
\\const X = struct { foo: i32, bar: i8 };
\\
);
}
test "zig fmt: container declaration, transform trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: i32, bar: i8, };
,
\\const X = struct {
\\ foo: i32,
\\ bar: i8,
\\};
\\
);
}
test "zig fmt: container declaration, comment, add trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: i32, // foo
\\ bar: i8
\\};
,
\\const X = struct {
\\ foo: i32, // foo
\\ bar: i8,
\\};
\\
);
try testTransform(
\\const X = struct {
\\ foo: i32 // foo
\\};
,
\\const X = struct {
\\ foo: i32, // foo
\\};
\\
);
}
test "zig fmt: container declaration, multiline string, add trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: []const u8 =
\\ \\ foo
\\ ,
\\ bar: i8
\\};
,
\\const X = struct {
\\ foo: []const u8 =
\\ \\ foo
\\ ,
\\ bar: i8,
\\};
\\
);
}
test "zig fmt: container declaration, doc comment on member, add trailing comma" {
try testTransform(
\\pub const Pos = struct {
\\ /// X-axis.
\\ x: u32,
\\ /// Y-axis.
\\ y: u32
\\};
,
\\pub const Pos = struct {
\\ /// X-axis.
\\ x: u32,
\\ /// Y-axis.
\\ y: u32,
\\};
\\
);
}
test "zig fmt: remove empty lines at start/end of container decl" {
try testTransform(
\\const X = struct {
\\
\\ foo: i32,
\\
\\ bar: i8,
\\
\\};
\\
,
\\const X = struct {
\\ foo: i32,
\\
\\ bar: i8,
\\};
\\
);
}