From b18647132874332288101df9550ed6d71cc6a763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 10 Feb 2026 12:25:25 +0000 Subject: [PATCH] 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) --- parser.c | 68 +++++++++++++++++++++-- parser_test.zig | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 4 deletions(-) diff --git a/parser.c b/parser.c index b832beaf38..bf476553dc 100644 --- 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 index 75f6446d93..cfbc4946ad 100644 --- 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, + \\}; + \\ + ); +}