diff --git a/ast.h b/ast.h index 201eb42028..e6a8527f8d 100644 --- 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 index a0c5ad7c89..65f8fd648f 100644 --- 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 index 14f388f098..297411536e 100644 --- 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 {