From 0433771b3e766e5d80eea86b6836e5ea1d2ff3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 10 Feb 2026 12:18:40 +0000 Subject: [PATCH] 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) --- parser.c | 81 +++++++++++++++++++++++++++++++++++++++++++------ parser_test.zig | 25 +++++++++++++++ 2 files changed, 97 insertions(+), 9 deletions(-) diff --git a/parser.c b/parser.c index f3d24657f2..95e10c67ce 100644 --- 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 index e588ab1ff2..d8429c1dd6 100644 --- 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; + \\ }, + \\}; + \\ + ); +}