commit e8dd9a5736233bf638ee1c6dfbfbc2b9022e6ae2 (tree)
parent a88a2e7428a29f25f22e92803c7ff6e26b96a51c
Author: Motiejus Jakštys <motiejus.jakstys@chronosphere.io>
Date: Wed, 11 Feb 2026 07:05:33 +0000
parser: port pointer/slice attributes tests
Port tests:
- "pointer attributes"
- "slice attributes"
Fix ** pointer type to parse modifiers per upstream (no sentinel,
modifiers on inner pointer only).
Fix ptr_type selection when both sentinel and align are present
(use ptr_type with extra data instead of ptr_type_sentinel which
can't store alignment).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
| M | parser.c | | | 93 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------- |
| M | parser_test.zig | | | 24 | ++++++++++++++++++++++-- |
2 files changed, 98 insertions(+), 19 deletions(-)
diff --git a/parser.c b/parser.c
@@ -897,7 +897,7 @@ static AstNodeIndex parsePtrModifiersAndType(
},
});
}
- if (addrspace_expr != 0) {
+ if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) {
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_PTR_TYPE,
@@ -905,7 +905,7 @@ static AstNodeIndex parsePtrModifiersAndType(
.data = {
.lhs = addExtra(p,
(AstNodeIndex[]) { OPT(sentinel), OPT(align_expr),
- addrspace_expr },
+ OPT(addrspace_expr) },
3),
.rhs = child_type,
},
@@ -946,17 +946,74 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
return parsePtrModifiersAndType(p, asterisk);
}
case TOKEN_ASTERISK_ASTERISK: {
- // ** is two nested pointer types sharing the same token
+ // ** is two nested pointer types sharing the same token.
+ // Inner pointer gets modifiers, outer wraps it with none.
+ // (Matches upstream Parse.zig asterisk_asterisk case)
const AstTokenIndex asterisk = nextToken(p);
- // Inner pointer: parse modifiers and child type
- const AstNodeIndex inner_child = parseTypeExpr(p);
- const AstNodeIndex inner = addNode(&p->nodes,
- (AstNodeItem) {
- .tag = AST_NODE_PTR_TYPE_ALIGNED,
- .main_token = asterisk,
- .data = { .lhs = 0, .rhs = inner_child },
- });
- // Outer pointer wraps the inner
+
+ // Parse inner pointer modifiers (no sentinel for **)
+ 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++;
+ AstNodeIndex align_expr = 0;
+ AstNodeIndex bit_range_start = 0;
+ AstNodeIndex bit_range_end = 0;
+ if (eatToken(p, TOKEN_KEYWORD_ALIGN) != null_token) {
+ expectToken(p, TOKEN_L_PAREN);
+ align_expr = expectExpr(p);
+ if (eatToken(p, TOKEN_COLON) != null_token) {
+ bit_range_start = expectExpr(p);
+ expectToken(p, TOKEN_COLON);
+ bit_range_end = expectExpr(p);
+ }
+ expectToken(p, TOKEN_R_PAREN);
+ }
+ const AstNodeIndex addrspace_expr = parseAddrSpace(p);
+ 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);
+ assert(elem_type != 0);
+
+ AstNodeIndex inner;
+ if (bit_range_start != 0) {
+ inner = addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_PTR_TYPE_BIT_RANGE,
+ .main_token = asterisk,
+ .data = {
+ .lhs = addExtra(p,
+ (AstNodeIndex[]) { OPT(0), align_expr,
+ OPT(addrspace_expr), bit_range_start,
+ bit_range_end },
+ 5),
+ .rhs = elem_type,
+ },
+ });
+ } else if (addrspace_expr != 0) {
+ inner = addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_PTR_TYPE,
+ .main_token = asterisk,
+ .data = {
+ .lhs = addExtra(p,
+ (AstNodeIndex[]) { OPT(0), OPT(align_expr),
+ addrspace_expr },
+ 3),
+ .rhs = elem_type,
+ },
+ });
+ } else {
+ inner = addNode(&p->nodes,
+ (AstNodeItem) {
+ .tag = AST_NODE_PTR_TYPE_ALIGNED,
+ .main_token = asterisk,
+ .data = { .lhs = align_expr, .rhs = elem_type },
+ });
+ }
+
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_PTR_TYPE_ALIGNED,
@@ -1034,7 +1091,7 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
},
});
}
- if (addrspace_expr != 0) {
+ if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) {
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_PTR_TYPE,
@@ -1042,7 +1099,8 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
.data = {
.lhs = addExtra(p,
(AstNodeIndex[]) { OPT(sentinel),
- OPT(align_expr), addrspace_expr },
+ OPT(align_expr),
+ OPT(addrspace_expr) },
3),
.rhs = elem_type,
},
@@ -1081,7 +1139,7 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
p->tok_i++;
const AstNodeIndex elem_type = parseTypeExpr(p);
- if (addrspace_expr != 0) {
+ if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) {
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_PTR_TYPE,
@@ -1089,13 +1147,14 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
.data = {
.lhs = addExtra(p,
(AstNodeIndex[]) { OPT(sentinel),
- OPT(align_expr), addrspace_expr },
+ OPT(align_expr),
+ OPT(addrspace_expr) },
3),
.rhs = elem_type,
},
});
}
- if (sentinel != 0 && align_expr == 0) {
+ if (sentinel != 0) {
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_PTR_TYPE_SENTINEL,
diff --git a/parser_test.zig b/parser_test.zig
@@ -2850,6 +2850,28 @@ test "zig fmt: nested pointers with ** tokens" {
);
}
+test "zig fmt: pointer attributes" {
+ try testCanonical(
+ \\extern fn f1(s: *align(*u8) u8) c_int;
+ \\extern fn f2(s: **align(1) *const *volatile u8) c_int;
+ \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int;
+ \\extern fn f4(s: *align(1) const volatile u8) c_int;
+ \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int;
+ \\
+ );
+}
+
+test "zig fmt: slice attributes" {
+ try testCanonical(
+ \\extern fn f1(s: []align(*u8) u8) c_int;
+ \\extern fn f2(s: []align(1) []const []volatile u8) c_int;
+ \\extern fn f3(s: []align(1) const [:0]align(1) volatile []const volatile u8) c_int;
+ \\extern fn f4(s: []align(1) const volatile u8) c_int;
+ \\extern fn f5(s: [:0]align(1) const volatile u8) c_int;
+ \\
+ );
+}
+
test "zig fmt: test declaration" {
try testCanonical(
\\test "test name" {
@@ -5550,5 +5572,3 @@ fn zigAst(gpa: Allocator, c_ast: c.Ast) !Ast {
.errors = errors,
};
}
-
-// copy-past from parser_test.zig