commit 0433771b3e766e5d80eea86b6836e5ea1d2ff3c3 (tree)
parent 1bb921b8cacbdf3a5d6c5104081c0e554a299938
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Tue, 10 Feb 2026 12:18:40 +0000
parser: add struct field value declaration test
Port "zig fmt: respect line breaks in struct field value declaration"
test from upstream parser_test.zig.
Implement in parser.c:
- Slice types ([]T, [:s]T) in parseTypeExpr
- Array types ([N]T, [N:s]T) in parseTypeExpr
- Multiline string literals in parsePrimaryTypeExpr
- Add comments explaining why const/volatile/allowzero pointer
modifiers are consumed (not stored in AST; renderer re-derives
them from token positions)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
| M | parser.c | | | 81 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
| M | parser_test.zig | | | 25 | +++++++++++++++++++++++++ |
2 files changed, 97 insertions(+), 9 deletions(-)
diff --git a/parser.c b/parser.c
@@ -483,10 +483,18 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) {
.main_token = nextToken(p),
.data = { .lhs = parseTypeExpr(p), .rhs = 0 },
});
- case TOKEN_MULTILINE_STRING_LITERAL_LINE:
- fprintf(stderr, "parsePrimaryTypeExpr does not support %s\n",
- tokenizerGetTagString(tok));
- exit(1);
+ case TOKEN_MULTILINE_STRING_LITERAL_LINE: {
+ const AstTokenIndex first = nextToken(p);
+ AstTokenIndex last = first;
+ while (p->token_tags[p->tok_i] == TOKEN_MULTILINE_STRING_LITERAL_LINE)
+ last = nextToken(p);
+ return addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_MULTILINE_STRING_LITERAL,
+ .main_token = first,
+ .data = { .lhs = first, .rhs = last },
+ });
+ }
case TOKEN_IDENTIFIER:
if (p->token_tags[p->tok_i + 1] == TOKEN_COLON) {
fprintf(stderr,
@@ -670,7 +678,9 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
const AstNodeIndex align_expr = parseByteAlign(p);
const AstNodeIndex sentinel
= eatToken(p, TOKEN_COLON) != null_token ? parseExpr(p) : 0;
- // skip const/volatile/allowzero modifiers
+ // const/volatile/allowzero are pointer modifiers consumed here.
+ // They are not stored in the AST node; the renderer re-derives
+ // them from token positions.
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
@@ -703,10 +713,63 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
fprintf(stderr, "parseTypeExpr not supported for %s\n",
tokenizerGetTagString(tok));
exit(1);
- case TOKEN_L_BRACKET:
- fprintf(stderr, "parseTypeExpr not supported for %s\n",
- tokenizerGetTagString(tok));
- exit(1);
+ case TOKEN_L_BRACKET: {
+ const AstTokenIndex lbracket = nextToken(p);
+ if (p->token_tags[p->tok_i] == TOKEN_ASTERISK) {
+ fprintf(
+ stderr, "parseTypeExpr: [*] pointer types not implemented\n");
+ exit(1);
+ }
+ const AstNodeIndex len_expr = parseExpr(p);
+ const AstNodeIndex sentinel
+ = eatToken(p, TOKEN_COLON) != null_token ? expectExpr(p) : 0;
+ expectToken(p, TOKEN_R_BRACKET);
+ if (len_expr == 0) {
+ // Slice type: []T or [:s]T
+ // const/volatile/allowzero are pointer modifiers consumed here.
+ // They are not stored in the AST node; the renderer re-derives
+ // them from token positions.
+ while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
+ || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
+ || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
+ p->tok_i++;
+ const AstNodeIndex elem_type = parseTypeExpr(p);
+ if (sentinel != 0) {
+ return addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_PTR_TYPE_SENTINEL,
+ .main_token = lbracket,
+ .data = { .lhs = sentinel, .rhs = elem_type },
+ });
+ }
+ return addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_PTR_TYPE_ALIGNED,
+ .main_token = lbracket,
+ .data = { .lhs = 0, .rhs = elem_type },
+ });
+ }
+ // Array type: [N]T or [N:s]T
+ const AstNodeIndex elem_type = parseTypeExpr(p);
+ if (sentinel == 0) {
+ return addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_ARRAY_TYPE,
+ .main_token = lbracket,
+ .data = { .lhs = len_expr, .rhs = elem_type },
+ });
+ }
+ return addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_ARRAY_TYPE_SENTINEL,
+ .main_token = lbracket,
+ .data = {
+ .lhs = len_expr,
+ .rhs = addExtra(p,
+ (AstNodeIndex[]) { sentinel, elem_type }, 2),
+ },
+ });
+ }
default:
return parseErrorUnionExpr(p);
}
diff --git a/parser_test.zig b/parser_test.zig
@@ -574,3 +574,28 @@ test "zig fmt: tuple struct" {
\\
);
}
+
+test "zig fmt: respect line breaks in struct field value declaration" {
+ try testCanonical(
+ \\const Foo = struct {
+ \\ bar: u32 =
+ \\ 42,
+ \\ bar: u32 =
+ \\ // a comment
+ \\ 42,
+ \\ bar: u32 =
+ \\ 42,
+ \\ // a comment
+ \\ bar: []const u8 =
+ \\ \\ foo
+ \\ \\ bar
+ \\ \\ baz
+ \\ ,
+ \\ bar: u32 =
+ \\ blk: {
+ \\ break :blk 42;
+ \\ },
+ \\};
+ \\
+ );
+}