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>
This commit is contained in:
2026-02-11 07:05:33 +00:00
parent 6bd0bdd7ed
commit 4c35471d46
2 changed files with 98 additions and 19 deletions

View File

@@ -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,

View File

@@ -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