zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 383fe836264ec99e499e8a538d181c5462be0d5d (tree)
parent c5915c06fb0bd42c94525052abc0b0092e3ecb98
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date:   Tue, 10 Feb 2026 22:41:53 +0000

parser: implement asm_legacy, port inline asm tests

Add AST_NODE_ASM_LEGACY for legacy string clobber format.
When asm clobbers use string literals ("clobber1", "clobber2"),
produce asm_legacy node instead of asm node.

Port tests:
- "preserves clobbers in inline asm with stray comma"
- "remove trailing comma at the end of assembly clobber"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Diffstat:
Mast.h | 3+++
Mparser.c | 52++++++++++++++++++++++++++++++++++++++++++----------
Mparser_test.zig | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 10 deletions(-)

diff --git a/ast.h b/ast.h @@ -484,6 +484,9 @@ typedef enum { AST_NODE_BLOCK_SEMICOLON, /// `asm(lhs)`. rhs is the token index of the rparen. AST_NODE_ASM_SIMPLE, + /// Legacy asm with string clobbers. `asm(lhs, a)`. + /// `AsmLegacy[rhs]`. + AST_NODE_ASM_LEGACY, /// `asm(lhs, a)`. `Asm[rhs]`. AST_NODE_ASM, /// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen. diff --git a/parser.c b/parser.c @@ -2127,28 +2127,60 @@ static AstNodeIndex parseAsmExpr(Parser* p) { } // Parse clobbers (after third colon) - // Legacy format: "str1", "str2", ... - // New format: .{ .clobber = true } - AstNodeIndex clobbers = 0; if (eatToken(p, TOKEN_COLON) != null_token) { if (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) { - // Legacy clobber format — skip all string literals and commas + // Legacy clobber format: "str1", "str2", ... + // Produces asm_legacy node while (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) { p->tok_i++; if (eatToken(p, TOKEN_COMMA) == null_token) break; } - } else if (p->token_tags[p->tok_i] != TOKEN_R_PAREN) { - clobbers = expectExpr(p); + const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN); + const uint32_t items_len = p->scratch.len - scratch_top.old_len; + const AstSubRange items_span = listToSpan( + p, &p->scratch.arr[scratch_top.old_len], items_len); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ASM_LEGACY, + .main_token = asm_token, + .data = { + .lhs = template, + .rhs = addExtra(p, + (AstNodeIndex[]) { items_span.start, + items_span.end, rparen }, + 3), + }, + }); } + // New clobber format: expression (e.g. .{ .clobber = true }) + AstNodeIndex clobbers = 0; + if (p->token_tags[p->tok_i] != TOKEN_R_PAREN) + clobbers = expectExpr(p); + + const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN); + const uint32_t items_len = p->scratch.len - scratch_top.old_len; + const AstSubRange items_span + = listToSpan(p, &p->scratch.arr[scratch_top.old_len], items_len); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ASM, + .main_token = asm_token, + .data = { + .lhs = template, + .rhs = addExtra(p, + (AstNodeIndex[]) { items_span.start, + items_span.end, OPT(clobbers), rparen }, + 4), + }, + }); } + // No clobbers const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN); - const uint32_t items_len = p->scratch.len - scratch_top.old_len; const AstSubRange items_span = listToSpan(p, &p->scratch.arr[scratch_top.old_len], items_len); - return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_ASM, @@ -2156,8 +2188,8 @@ static AstNodeIndex parseAsmExpr(Parser* p) { .data = { .lhs = template, .rhs = addExtra(p, - (AstNodeIndex[]) { items_span.start, - items_span.end, OPT(clobbers), rparen }, + (AstNodeIndex[]) { items_span.start, items_span.end, + OPT((AstNodeIndex)0), rparen }, 4), }, }); diff --git a/parser_test.zig b/parser_test.zig @@ -171,6 +171,7 @@ fn zigNode(token: c_uint) Ast.Node.Tag { c.AST_NODE_BLOCK => .block, c.AST_NODE_BLOCK_SEMICOLON => .block_semicolon, c.AST_NODE_ASM_SIMPLE => .asm_simple, + c.AST_NODE_ASM_LEGACY => .asm_legacy, c.AST_NODE_ASM => .@"asm", c.AST_NODE_ASM_OUTPUT => .asm_output, c.AST_NODE_ASM_INPUT => .asm_input, @@ -569,6 +570,60 @@ test "zig fmt: tuple struct" { ); } +test "zig fmt: preserves clobbers in inline asm with stray comma" { + try testTransform( + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type), + \\ : + \\ : "clobber" + \\ ); + \\ asm volatile ("" + \\ : + \\ : [_] "" (type), + \\ : "clobber" + \\ ); + \\} + \\ + , + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type), + \\ : + \\ : .{ .clobber = true } + \\ ); + \\ asm volatile ("" + \\ : + \\ : [_] "" (type), + \\ : .{ .clobber = true } + \\ ); + \\} + \\ + ); +} + +test "zig fmt: remove trailing comma at the end of assembly clobber" { + try testTransform( + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type), + \\ : + \\ : "clobber1", "clobber2", + \\ ); + \\} + \\ + , + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type), + \\ : + \\ : .{ .clobber1 = true, .clobber2 = true } + \\ ); + \\} + \\ + ); +} + test "zig fmt: respect line breaks in struct field value declaration" { try testCanonical( \\const Foo = struct {