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>
This commit is contained in:
2026-02-10 12:18:40 +00:00
parent 1bb921b8ca
commit 0433771b3e
2 changed files with 97 additions and 9 deletions

View File

@@ -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);
}

View File

@@ -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;
\\ },
\\};
\\
);
}