commit fb51ce64637fc7a5b66cd6ce13e1547f9b2dec55 (tree)
parent e5642d1eb1c164ec9d59900c78e43d0531ea621c
Author: Motiejus Jakštys <motiejus.jakstys@chronosphere.io>
Date: Tue, 10 Feb 2026 12:25:25 +0000
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>
Diffstat:
| M | parser.c | | | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
| M | parser_test.zig | | | 139 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 203 insertions(+), 4 deletions(-)
diff --git a/parser.c b/parser.c
@@ -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(
diff --git a/parser_test.zig b/parser_test.zig
@@ -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,
+ \\};
+ \\
+ );
+}