commit ab237855b8df3f91f232c7e63eba7d21f166f75f (tree)
parent c818a1e61f702e1990af3e0773b3954d0e29d0ca
Author: Kendall Condon <goon.pri.low@gmail.com>
Date: Sun, 22 Mar 2026 17:34:56 -0400
zig fmt: render asm colons with trailing comments
Previously, the comments would be lost with the colons.
This required a substantial rewrite of renderAsm to determine how many
colons should be rendered.
Diffstat:
2 files changed, 102 insertions(+), 69 deletions(-)
diff --git a/lib/std/zig/Ast/Render.zig b/lib/std/zig/Ast/Render.zig
@@ -2756,8 +2756,47 @@ fn renderAsm(
try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen
}
+ const render_colons: [3]?Ast.TokenIndex = colons: {
+ var colons: [3]Ast.TokenIndex = undefined;
+ var render: u2 = 0;
+
+ const rparen = asm_node.ast.rparen;
+ filled: {
+ colons[0] = tree.lastToken(asm_node.ast.template) + 1;
+ if (colons[0] == rparen) break :filled;
+
+ if (asm_node.outputs.len != 0) {
+ colons[1] = tree.lastToken(asm_node.outputs[asm_node.outputs.len - 1]) + 1;
+ colons[1] += @intFromBool(tree.tokenTag(colons[1]) == .comma);
+ render = 1;
+ } else {
+ colons[1] = colons[0] + 1;
+ if (hasComment(tree, colons[0], colons[1])) render = 1;
+ }
+ if (colons[1] == rparen) break :filled;
+
+ // Next colon is not checked for here since it cannot present without clobbers
+ if (asm_node.inputs.len != 0) {
+ render = 2;
+ } else {
+ const colon_or_rparen = colons[1] + 1;
+ if (hasComment(tree, colons[1], colon_or_rparen)) render = 2;
+ }
+
+ if (asm_node.ast.clobbers.unwrap()) |clobbers| {
+ colons[2] = tree.firstToken(clobbers) - 1;
+ render = 3;
+ }
+ }
+
+ var opt_colons: [3]?Ast.TokenIndex = @splat(null);
+ for (0..render) |i| opt_colons[i] = colons[i];
+ break :colons opt_colons;
+ };
+
+ try ais.forcePushIndent(.normal);
+
if (asm_node.ast.items.len == 0) {
- try ais.forcePushIndent(.normal);
if (asm_node.ast.clobbers.unwrap()) |clobbers| {
// asm ("foo" ::: clobbers)
try renderExpression(r, asm_node.ast.template, .space);
@@ -2771,13 +2810,14 @@ fn renderAsm(
return renderToken(r, asm_node.ast.rparen, space); // rparen
}
- // asm ("foo")
- try renderExpression(r, asm_node.ast.template, .none);
- ais.popIndent();
- return renderToken(r, asm_node.ast.rparen, space); // rparen
+ if (render_colons[0] == null) {
+ // asm ("foo")
+ try renderExpression(r, asm_node.ast.template, .none);
+ ais.popIndent();
+ return renderToken(r, asm_node.ast.rparen, space); // rparen
+ }
}
- try ais.forcePushIndent(.normal);
try renderExpression(r, asm_node.ast.template, .newline);
ais.forceLastIndent(); // Might have been dedented by a multiline string literal
assert(ais.current_line_empty);
@@ -2785,86 +2825,62 @@ fn renderAsm(
const prev_indent_delta = ais.indent_delta; // May be part of another asm expression
// so indent_delta can't be unconditionally used
ais.setIndentDelta(asm_indent_delta);
- const colon1 = tree.lastToken(asm_node.ast.template) + 1;
- const colon2 = if (asm_node.outputs.len == 0) colon2: {
- try renderToken(r, colon1, .newline); // :
- break :colon2 colon1 + 1;
- } else colon2: {
- try renderToken(r, colon1, .space); // :
+ rendered: {
+ if (render_colons[0]) |colon1| {
+ if (asm_node.outputs.len != 0) {
+ try renderToken(r, colon1, .space);
+ try ais.forcePushIndent(.normal);
- try ais.forcePushIndent(.normal);
- for (asm_node.outputs, 0..) |asm_output, i| {
- if (i + 1 < asm_node.outputs.len) {
- const next_asm_output = asm_node.outputs[i + 1];
- try renderAsmOutput(r, asm_output, .none);
+ const final = asm_node.outputs.len - 1;
+ for (asm_node.outputs[0..final], 0..) |asm_output, i| {
+ try renderAsmOutput(r, asm_output, .none);
+
+ const next_start = tree.firstToken(asm_node.outputs[i + 1]);
+ try renderToken(r, next_start - 1, .newline); // ,
+ try renderExtraNewlineToken(r, next_start);
+ }
- const comma = tree.firstToken(next_asm_output) - 1;
- try renderToken(r, comma, .newline); // ,
- try renderExtraNewlineToken(r, tree.firstToken(next_asm_output));
- } else if (asm_node.inputs.len == 0 and asm_node.ast.clobbers == .none) {
try ais.pushSpace(.comma);
- try renderAsmOutput(r, asm_output, .comma);
+ try renderAsmOutput(r, asm_node.outputs[final], .comma);
ais.popSpace();
ais.popIndent();
- ais.setIndentDelta(indent_delta);
- ais.popIndent();
- return renderToken(r, asm_node.ast.rparen, space); // rparen
} else {
- try ais.pushSpace(.comma);
- try renderAsmOutput(r, asm_output, .comma);
- ais.popSpace();
- const comma_or_colon = tree.lastToken(asm_output) + 1;
- ais.popIndent();
- break :colon2 switch (tree.tokenTag(comma_or_colon)) {
- .comma => comma_or_colon + 1,
- else => comma_or_colon,
- };
+ try renderToken(r, colon1, .newline);
}
} else unreachable;
- };
- const colon3 = if (asm_node.inputs.len == 0) colon3: {
- try renderToken(r, colon2, .newline); // :
- break :colon3 colon2 + 1;
- } else colon3: {
- try renderToken(r, colon2, .space); // :
- try ais.forcePushIndent(.normal);
- for (asm_node.inputs, 0..) |asm_input, i| {
- if (i + 1 < asm_node.inputs.len) {
- const next_asm_input = asm_node.inputs[i + 1];
- try renderAsmInput(r, asm_input, .none);
-
- const first_token = tree.firstToken(next_asm_input);
- try renderToken(r, first_token - 1, .newline); // ,
- try renderExtraNewlineToken(r, first_token);
- } else if (asm_node.ast.clobbers == .none) {
+ if (render_colons[1]) |colon2| {
+ if (asm_node.inputs.len != 0) {
+ try renderToken(r, colon2, .space);
+ try ais.forcePushIndent(.normal);
+
+ const final = asm_node.inputs.len - 1;
+ for (asm_node.inputs[0..final], 0..) |asm_input, i| {
+ try renderAsmInput(r, asm_input, .none);
+
+ const next_start = tree.firstToken(asm_node.inputs[i + 1]);
+ try renderToken(r, next_start - 1, .newline); // ,
+ try renderExtraNewlineToken(r, next_start);
+ }
+
try ais.pushSpace(.comma);
- try renderAsmInput(r, asm_input, .comma);
+ try renderAsmInput(r, asm_node.inputs[final], .comma);
ais.popSpace();
ais.popIndent();
- ais.setIndentDelta(indent_delta);
- ais.popIndent();
- return renderToken(r, asm_node.ast.rparen, space); // rparen
} else {
- try ais.pushSpace(.comma);
- try renderAsmInput(r, asm_input, .comma);
- ais.popSpace();
- const comma_or_colon = tree.lastToken(asm_input) + 1;
- ais.popIndent();
- break :colon3 switch (tree.tokenTag(comma_or_colon)) {
- .comma => comma_or_colon + 1,
- else => comma_or_colon,
- };
+ try renderToken(r, colon2, .newline);
}
+ } else break :rendered;
+
+ if (render_colons[2]) |colon3| {
+ const clobbers = asm_node.ast.clobbers.unwrap().?;
+ try renderToken(r, colon3, .maybe_space);
+ try renderExpression(r, clobbers, .none);
+ ais.forceLastIndent(); // Might have been dedented by a multiline string literal
}
- unreachable;
- };
+ }
- try renderToken(r, colon3, .maybe_space); // :
- const clobbers = asm_node.ast.clobbers.unwrap().?;
- try renderExpression(r, clobbers, .none);
- ais.forceLastIndent(); // Might have been dedented by a multiline string literal
ais.setIndentDelta(prev_indent_delta);
ais.popIndent();
return renderToken(r, asm_node.ast.rparen, space); // rparen
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
@@ -6892,6 +6892,23 @@ test "zig fmt: array init with multiline string literal with fmt on/off" {
);
}
+test "zig fmt: render extra colons with comments" {
+ try testCanonical(
+ \\const a = asm (""
+ \\ : // testing
+ \\);
+ \\const b = asm (""
+ \\ : // testing
+ \\ : // testing
+ \\);
+ \\const c = asm (""
+ \\ :
+ \\ : // testing
+ \\);
+ \\
+ );
+}
+
test "recovery: top level" {
try testError(
\\test "" {inline}