commit b15aa0540d03c92c7b822fbebc373bf4e76e3425 (tree)
parent 2c61deaae26ee492fa5a7b68726801c9414305a5
Author: Andrew Kelley <andrew@ziglang.org>
Date: Fri, 13 Mar 2026 08:43:19 +0100
Merge pull request 'add a fuzz test for zig fmt' (#30082) from gooncreeper/zig:fmt-fuzzing into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30082
Reviewed-by: Andrew Kelley <andrew@ziglang.org>
Diffstat:
11 files changed, 2071 insertions(+), 640 deletions(-)
diff --git a/lib/compiler/aro/aro/Compilation.zig b/lib/compiler/aro/aro/Compilation.zig
@@ -1707,7 +1707,8 @@ pub fn initSearchPath(comp: *Compilation, includes: []const Include, verbose: bo
std.debug.print("#include <...> search starts here:\n", .{});
}
std.debug.print(" {s}{s}\n", .{
- include.path, if (include.kind.isFramework())
+ include.path,
+ if (include.kind.isFramework())
" (framework directory)"
else
"",
diff --git a/lib/compiler/translate-c/ast.zig b/lib/compiler/translate-c/ast.zig
@@ -1213,7 +1213,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
.tag = .slice,
.main_token = l_bracket,
.data = .{ .node_and_extra = .{
- string, try c.addExtra(std.zig.Ast.Node.Slice{
+ string,
+ try c.addExtra(std.zig.Ast.Node.Slice{
.start = start,
.end = end,
}),
@@ -2720,7 +2721,8 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
.tag = .call,
.main_token = lparen,
.data = .{ .node_and_extra = .{
- lhs, try c.addExtra(NodeSubRange{
+ lhs,
+ try c.addExtra(NodeSubRange{
.start = span.start,
.end = span.end,
}),
diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig
@@ -8,8 +8,7 @@ const has_armaes = builtin.cpu.has(.aarch64, .aes);
// C backend doesn't currently support passing vectors to inline asm.
const impl = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c and has_aesni and has_avx) impl: {
break :impl @import("aes/aesni.zig");
-} else if (builtin.cpu.arch == .aarch64 and builtin.zig_backend != .stage2_c and has_armaes)
-impl: {
+} else if (builtin.cpu.arch == .aarch64 and builtin.zig_backend != .stage2_c and has_armaes) impl: {
break :impl @import("aes/armcrypto.zig");
} else impl: {
break :impl @import("aes/soft.zig");
diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig
@@ -1245,14 +1245,16 @@ test utf8ToUtf16Le {
const length = try utf8ToUtf16Le(utf16le[0..], "This string has been designed to test the vectorized implementat" ++
"ion by beginning with one hundred twenty-seven ASCII characters¡");
try testing.expectEqualSlices(u8, &.{
- 'T', 0, 'h', 0, 'i', 0, 's', 0, ' ', 0, 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'h', 0, 'a', 0, 's', 0, ' ', 0,
- 'b', 0, 'e', 0, 'e', 0, 'n', 0, ' ', 0, 'd', 0, 'e', 0, 's', 0, 'i', 0, 'g', 0, 'n', 0, 'e', 0, 'd', 0, ' ', 0, 't', 0, 'o', 0,
- ' ', 0, 't', 0, 'e', 0, 's', 0, 't', 0, ' ', 0, 't', 0, 'h', 0, 'e', 0, ' ', 0, 'v', 0, 'e', 0, 'c', 0, 't', 0, 'o', 0, 'r', 0,
- 'i', 0, 'z', 0, 'e', 0, 'd', 0, ' ', 0, 'i', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 'a', 0, 't', 0,
- 'i', 0, 'o', 0, 'n', 0, ' ', 0, 'b', 0, 'y', 0, ' ', 0, 'b', 0, 'e', 0, 'g', 0, 'i', 0, 'n', 0, 'n', 0, 'i', 0, 'n', 0, 'g', 0,
- ' ', 0, 'w', 0, 'i', 0, 't', 0, 'h', 0, ' ', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'h', 0, 'u', 0, 'n', 0, 'd', 0, 'r', 0, 'e', 0,
- 'd', 0, ' ', 0, 't', 0, 'w', 0, 'e', 0, 'n', 0, 't', 0, 'y', 0, '-', 0, 's', 0, 'e', 0, 'v', 0, 'e', 0, 'n', 0, ' ', 0, 'A', 0,
- 'S', 0, 'C', 0, 'I', 0, 'I', 0, ' ', 0, 'c', 0, 'h', 0, 'a', 0, 'r', 0, 'a', 0, 'c', 0, 't', 0, 'e', 0, 'r', 0, 's', 0, '¡', 0,
+ 'T', 0, 'h', 0, 'i', 0, 's', 0, ' ', 0, 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'h', 0, 'a', 0, 's', 0, ' ', 0,
+ 'b', 0, 'e', 0, 'e', 0, 'n', 0, ' ', 0, 'd', 0, 'e', 0, 's', 0, 'i', 0, 'g', 0, 'n', 0, 'e', 0, 'd', 0, ' ', 0, 't', 0, 'o', 0,
+ ' ', 0, 't', 0, 'e', 0, 's', 0, 't', 0, ' ', 0, 't', 0, 'h', 0, 'e', 0, ' ', 0, 'v', 0, 'e', 0, 'c', 0, 't', 0, 'o', 0, 'r', 0,
+ 'i', 0, 'z', 0, 'e', 0, 'd', 0, ' ', 0, 'i', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 'a', 0, 't', 0,
+ 'i', 0, 'o', 0, 'n', 0, ' ', 0, 'b', 0, 'y', 0, ' ', 0, 'b', 0, 'e', 0, 'g', 0, 'i', 0, 'n', 0, 'n', 0, 'i', 0, 'n', 0, 'g', 0,
+ ' ', 0, 'w', 0, 'i', 0, 't', 0, 'h', 0, ' ', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'h', 0, 'u', 0, 'n', 0, 'd', 0, 'r', 0, 'e', 0,
+ 'd', 0, ' ', 0, 't', 0, 'w', 0, 'e', 0, 'n', 0, 't', 0, 'y', 0, '-', 0, 's', 0, 'e', 0, 'v', 0, 'e', 0, 'n', 0, ' ', 0, 'A', 0,
+ 'S', 0, 'C', 0, 'I', 0, 'I', 0, ' ', 0, 'c', 0, 'h', 0, 'a', 0, 'r', 0, 'a', 0, 'c', 0, 't', 0, 'e', 0, 'r', 0, 's', 0,
+ '¡',
+ 0,
}, mem.sliceAsBytes(utf16le[0..length]));
}
}
@@ -1317,14 +1319,16 @@ test utf8ToUtf16LeAllocZ {
"ion by beginning with one hundred twenty-seven ASCII characters¡");
defer testing.allocator.free(utf16);
try testing.expectEqualSlices(u8, &.{
- 'T', 0, 'h', 0, 'i', 0, 's', 0, ' ', 0, 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'h', 0, 'a', 0, 's', 0, ' ', 0,
- 'b', 0, 'e', 0, 'e', 0, 'n', 0, ' ', 0, 'd', 0, 'e', 0, 's', 0, 'i', 0, 'g', 0, 'n', 0, 'e', 0, 'd', 0, ' ', 0, 't', 0, 'o', 0,
- ' ', 0, 't', 0, 'e', 0, 's', 0, 't', 0, ' ', 0, 't', 0, 'h', 0, 'e', 0, ' ', 0, 'v', 0, 'e', 0, 'c', 0, 't', 0, 'o', 0, 'r', 0,
- 'i', 0, 'z', 0, 'e', 0, 'd', 0, ' ', 0, 'i', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 'a', 0, 't', 0,
- 'i', 0, 'o', 0, 'n', 0, ' ', 0, 'b', 0, 'y', 0, ' ', 0, 'b', 0, 'e', 0, 'g', 0, 'i', 0, 'n', 0, 'n', 0, 'i', 0, 'n', 0, 'g', 0,
- ' ', 0, 'w', 0, 'i', 0, 't', 0, 'h', 0, ' ', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'h', 0, 'u', 0, 'n', 0, 'd', 0, 'r', 0, 'e', 0,
- 'd', 0, ' ', 0, 't', 0, 'w', 0, 'e', 0, 'n', 0, 't', 0, 'y', 0, '-', 0, 's', 0, 'e', 0, 'v', 0, 'e', 0, 'n', 0, ' ', 0, 'A', 0,
- 'S', 0, 'C', 0, 'I', 0, 'I', 0, ' ', 0, 'c', 0, 'h', 0, 'a', 0, 'r', 0, 'a', 0, 'c', 0, 't', 0, 'e', 0, 'r', 0, 's', 0, '¡', 0,
+ 'T', 0, 'h', 0, 'i', 0, 's', 0, ' ', 0, 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'h', 0, 'a', 0, 's', 0, ' ', 0,
+ 'b', 0, 'e', 0, 'e', 0, 'n', 0, ' ', 0, 'd', 0, 'e', 0, 's', 0, 'i', 0, 'g', 0, 'n', 0, 'e', 0, 'd', 0, ' ', 0, 't', 0, 'o', 0,
+ ' ', 0, 't', 0, 'e', 0, 's', 0, 't', 0, ' ', 0, 't', 0, 'h', 0, 'e', 0, ' ', 0, 'v', 0, 'e', 0, 'c', 0, 't', 0, 'o', 0, 'r', 0,
+ 'i', 0, 'z', 0, 'e', 0, 'd', 0, ' ', 0, 'i', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 'a', 0, 't', 0,
+ 'i', 0, 'o', 0, 'n', 0, ' ', 0, 'b', 0, 'y', 0, ' ', 0, 'b', 0, 'e', 0, 'g', 0, 'i', 0, 'n', 0, 'n', 0, 'i', 0, 'n', 0, 'g', 0,
+ ' ', 0, 'w', 0, 'i', 0, 't', 0, 'h', 0, ' ', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'h', 0, 'u', 0, 'n', 0, 'd', 0, 'r', 0, 'e', 0,
+ 'd', 0, ' ', 0, 't', 0, 'w', 0, 'e', 0, 'n', 0, 't', 0, 'y', 0, '-', 0, 's', 0, 'e', 0, 'v', 0, 'e', 0, 'n', 0, ' ', 0, 'A', 0,
+ 'S', 0, 'C', 0, 'I', 0, 'I', 0, ' ', 0, 'c', 0, 'h', 0, 'a', 0, 'r', 0, 'a', 0, 'c', 0, 't', 0, 'e', 0, 'r', 0, 's', 0,
+ '¡',
+ 0,
}, mem.sliceAsBytes(utf16));
}
}
diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig
@@ -2136,10 +2136,12 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType
// here while looking for modifiers as that could result in false
// positives. Therefore, start after a sentinel if there is one and
// skip over any align node and bit range nodes.
- var i = if (info.sentinel.unwrap()) |sentinel| tree.lastToken(sentinel) + 1 else switch (size) {
- .many, .c => info.main_token + 1,
- else => info.main_token,
- };
+ var i = (if (info.sentinel.unwrap()) |sentinel| tree.lastToken(sentinel) + 1 else switch (size) {
+ .one => info.main_token,
+ .slice => info.main_token + 1,
+ .many => info.main_token + 2,
+ .c => info.main_token + 3,
+ }) + 1;
const end = tree.firstToken(info.child_type);
while (i < end) : (i += 1) {
switch (tree.tokenTag(i)) {
@@ -2147,15 +2149,15 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType
.keyword_const => result.const_token = i,
.keyword_volatile => result.volatile_token = i,
.keyword_align => {
- const align_node = info.align_node.unwrap().?;
if (info.bit_range_end.unwrap()) |bit_range_end| {
assert(info.bit_range_start != .none);
i = tree.lastToken(bit_range_end) + 1;
- } else {
+ } else if (info.align_node.unwrap()) |align_node| {
i = tree.lastToken(align_node) + 1;
- }
+ } else unreachable;
},
- else => {},
+ .keyword_addrspace => i = tree.lastToken(info.addrspace_node.unwrap().?) + 1,
+ else => unreachable,
}
}
return result;
diff --git a/lib/std/zig/Ast/Render.zig b/lib/std/zig/Ast/Render.zig
@@ -346,20 +346,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
const next_token_tag = tree.tokenTag(next_token);
// dedent the next thing that comes after a multiline string literal
- if (!ais.indentStackEmpty() and
- next_token_tag != .colon and
- ((next_token_tag != .semicolon and next_token_tag != .comma) or
- ais.lastSpaceModeIndent() < ais.currentIndent()))
+ if (next_token_tag != .colon and
+ !ais.indentStackEmpty() and
+ ais.lastSpaceModeIndent() < ais.currentIndent())
{
- ais.popIndent();
- try ais.pushIndent(.normal);
+ const indent_top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
+ if (indent_top.realized) {
+ indent_top.realized = false;
+ ais.indent_count -= 1;
+ }
}
switch (space) {
- .none, .space, .newline, .skip => {},
- .semicolon => if (next_token_tag == .semicolon) try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
- .comma => if (next_token_tag == .comma) try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
- .comma_space => if (next_token_tag == .comma) try renderToken(r, next_token, .space),
+ .none, .space, .newline, .maybe_space, .skip => {},
+ .semicolon => if (next_token_tag == .semicolon)
+ try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
+ .comma => if (next_token_tag == .comma)
+ try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
+ .comma_space => if (next_token_tag == .comma)
+ try renderToken(r, next_token, .space),
+ .comma_maybe_space => if (next_token_tag == .comma)
+ try renderToken(r, next_token, .maybe_space),
}
},
@@ -384,11 +391,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
const defer_token = tree.nodeMainToken(node);
const maybe_payload_token, const expr = tree.nodeData(node).opt_token_and_node;
- try renderToken(r, defer_token, .space);
+ try renderToken(r, defer_token, .maybe_space);
if (maybe_payload_token.unwrap()) |payload_token| {
try renderToken(r, payload_token - 1, .none); // |
try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier
- try renderToken(r, payload_token + 1, .space); // |
+ try renderToken(r, payload_token + 1, .maybe_space); // |
}
return renderExpression(r, expr, space);
},
@@ -400,7 +407,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
=> {
const main_token = tree.nodeMainToken(node);
const item = tree.nodeData(node).node;
- try renderToken(r, main_token, .space);
+ try renderToken(r, main_token, .maybe_space);
return renderExpression(r, item, space);
},
@@ -409,8 +416,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
const lhs, const rhs = tree.nodeData(node).node_and_node;
const fallback_first = tree.firstToken(rhs);
- const same_line = tree.tokensOnSameLine(main_token, fallback_first);
- const after_op_space = if (same_line) Space.space else Space.newline;
+ const seperate_line = !tree.tokensOnSameLine(main_token, fallback_first) or
+ tree.tokenTag(fallback_first) == .multiline_string_literal_line;
+ const after_op_space: Space = if (seperate_line) .newline else .space;
try renderExpression(r, lhs, .space); // target
@@ -433,13 +441,18 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
const dot_token = name_token - 1;
try ais.pushIndent(.field_access);
- try renderExpression(r, lhs, .none);
- // Allow a line break between the lhs and the dot if the lhs and rhs
- // are on different lines.
const lhs_last_token = tree.lastToken(lhs);
const same_line = tree.tokensOnSameLine(lhs_last_token, name_token);
- if (!same_line and !hasComment(tree, lhs_last_token, dot_token)) try ais.insertNewline();
+ // Keeping a space after the number ensures it will not turn into a decimal number
+ // (e.g. "0xF .A").
+ const number_space: Space = if (tree.tokenTag(lhs_last_token) == .number_literal and same_line) .space else .none;
+ try renderExpression(r, lhs, number_space);
+
+ // Allow a line break between the lhs and the dot if the lhs and rhs
+ // are on different lines.
+ if (!same_line and !hasComment(tree, lhs_last_token, dot_token))
+ try ais.insertNewline();
try renderToken(r, dot_token, .none);
@@ -489,11 +502,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
try renderExpression(r, lhs, .space);
const op_token = tree.nodeMainToken(node);
try ais.pushIndent(.after_equals);
- if (tree.tokensOnSameLine(op_token, op_token + 1)) {
- try renderToken(r, op_token, .space);
- } else {
- try renderToken(r, op_token, .newline);
- }
+ const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
+ tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
+ try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
try renderExpression(r, rhs, space);
ais.popIndent();
},
@@ -532,11 +543,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
try renderExpression(r, lhs, .space);
const op_token = tree.nodeMainToken(node);
try ais.pushIndent(.binop);
- if (tree.tokensOnSameLine(op_token, op_token + 1)) {
- try renderToken(r, op_token, .space);
- } else {
- try renderToken(r, op_token, .newline);
- }
+ const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
+ tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
+ try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
try renderExpression(r, rhs, space);
ais.popIndent();
},
@@ -544,11 +553,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
.assign_destructure => {
const full = tree.assignDestructure(node);
if (full.comptime_token) |comptime_token| {
- try renderToken(r, comptime_token, .space);
+ try renderToken(r, comptime_token, .maybe_space);
}
for (full.ast.variables, 0..) |variable_node, i| {
- const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;
+ const variable_space: Space = if (i == full.ast.variables.len - 1) .maybe_space else .comma_maybe_space;
switch (tree.nodeTag(variable_node)) {
.global_var_decl,
.local_var_decl,
@@ -561,11 +570,10 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
}
}
try ais.pushIndent(.after_equals);
- if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {
- try renderToken(r, full.ast.equal_token, .space);
- } else {
- try renderToken(r, full.ast.equal_token, .newline);
- }
+ const expr_seperate_line =
+ !tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1) or
+ tree.tokenTag(full.ast.equal_token + 1) == .multiline_string_literal_line;
+ try renderToken(r, full.ast.equal_token, if (expr_seperate_line) .newline else .space);
try renderExpression(r, full.ast.value_expr, space);
ais.popIndent();
},
@@ -584,7 +592,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
.@"try",
.@"resume",
=> {
- try renderToken(r, tree.nodeMainToken(node), .space);
+ try renderToken(r, tree.nodeMainToken(node), .maybe_space);
return renderExpression(r, tree.nodeData(node).node, space);
},
@@ -637,7 +645,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
const lhs, const rhs = tree.nodeData(node).node_and_node;
const lbracket = tree.firstToken(rhs) - 1;
const rbracket = tree.lastToken(rhs) + 1;
- const one_line = tree.tokensOnSameLine(lbracket, rbracket);
+ const one_line = tree.tokensOnSameLine(lbracket, rbracket) and
+ !becomesMultilineExpr(tree, rhs);
const inner_space = if (one_line) Space.none else Space.newline;
try renderExpression(r, lhs, .none);
try ais.pushIndent(.normal);
@@ -668,30 +677,23 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
.@"break", .@"continue" => {
const main_token = tree.nodeMainToken(node);
const opt_label_token, const opt_target = tree.nodeData(node).opt_token_and_opt_node;
- if (opt_label_token == .none and opt_target == .none) {
- try renderToken(r, main_token, space); // break/continue
- } else if (opt_label_token == .none and opt_target != .none) {
- const target = opt_target.unwrap().?;
- try renderToken(r, main_token, .space); // break/continue
- try renderExpression(r, target, space);
- } else if (opt_label_token != .none and opt_target == .none) {
- const label_token = opt_label_token.unwrap().?;
- try renderToken(r, main_token, .space); // break/continue
- try renderToken(r, label_token - 1, .none); // :
- try renderIdentifier(r, label_token, space, .eagerly_unquote); // identifier
- } else if (opt_label_token != .none and opt_target != .none) {
- const label_token = opt_label_token.unwrap().?;
- const target = opt_target.unwrap().?;
- try renderToken(r, main_token, .space); // break/continue
+
+ const before_target_space: Space = if (opt_target != .none) .maybe_space else space;
+ const before_label_space: Space = if (opt_label_token != .none) .space else before_target_space;
+
+ try renderToken(r, main_token, before_label_space);
+ if (opt_label_token.unwrap()) |label_token| {
try renderToken(r, label_token - 1, .none); // :
- try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier
+ try renderIdentifier(r, label_token, before_target_space, .eagerly_unquote); // identifier
+ }
+ if (opt_target.unwrap()) |target| {
try renderExpression(r, target, space);
- } else unreachable;
+ }
},
.@"return" => {
if (tree.nodeData(node).opt_node.unwrap()) |expr| {
- try renderToken(r, tree.nodeMainToken(node), .space);
+ try renderToken(r, tree.nodeMainToken(node), .maybe_space);
try renderExpression(r, expr, space);
} else {
try renderToken(r, tree.nodeMainToken(node), space);
@@ -740,33 +742,36 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
try renderToken(r, lbrace, .none);
try renderIdentifier(r, lbrace + 1, .none, .eagerly_unquote); // identifier
return renderToken(r, rbrace, space);
- } else if (tree.tokenTag(rbrace - 1) == .comma) {
- // There is a trailing comma so render each member on a new line.
+ } else if (!isOneLineErrorSetDecl(tree, lbrace, rbrace)) {
+ // Render each member on a new line.
try ais.pushIndent(.normal);
try renderToken(r, lbrace, .newline);
var i = lbrace + 1;
while (i < rbrace) : (i += 1) {
+ const tag = tree.tokenTag(i);
+ if (tag == .comma) {
+ assert(tree.tokenTag(i - 1) == .identifier);
+ continue;
+ }
if (i > lbrace + 1) try renderExtraNewlineToken(r, i);
- switch (tree.tokenTag(i)) {
+ switch (tag) {
.doc_comment => try renderToken(r, i, .newline),
.identifier => {
try ais.pushSpace(.comma);
try renderIdentifier(r, i, .comma, .eagerly_unquote);
ais.popSpace();
},
- .comma => {},
else => unreachable,
}
}
ais.popIndent();
return renderToken(r, rbrace, space);
} else {
- // There is no trailing comma so render everything on one line.
+ // Render each member on one line.
try renderToken(r, lbrace, .space);
var i = lbrace + 1;
while (i < rbrace) : (i += 1) {
switch (tree.tokenTag(i)) {
- .doc_comment => unreachable, // TODO
.identifier => try renderIdentifier(r, i, .comma_space, .eagerly_unquote),
.comma => {},
else => unreachable,
@@ -926,6 +931,382 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v
}
}
+/// Same as becomesMultilineExpr, but returns false when `node == .none`
+fn optBecomesMultilineExpr(tree: Ast, node: Ast.Node.OptionalIndex) bool {
+ return if (node.unwrap()) |payload| becomesMultilineExpr(tree, payload) else false;
+}
+
+/// May return false if `node` is already multiline
+fn becomesMultilineExpr(tree: Ast, node: Ast.Node.Index) bool {
+ // Conditions related to comments, doc comments, and multiline string literals are ignored
+ // since they always go to the end of the line, which already make them a multi-line
+ // expression (since they contain a newline).
+ switch (tree.nodeTag(node)) {
+ .identifier,
+ .number_literal,
+ .char_literal,
+ .unreachable_literal,
+ .anyframe_literal,
+ .string_literal,
+ .multiline_string_literal,
+ .error_value,
+ .enum_literal,
+ => return false,
+ .container_decl_trailing,
+ .container_decl_arg_trailing,
+ .container_decl_two_trailing,
+ .tagged_union_trailing,
+ .tagged_union_enum_tag_trailing,
+ .tagged_union_two_trailing,
+ .switch_comma,
+ .builtin_call_two_comma,
+ .builtin_call_comma,
+ .call_one_comma,
+ .call_comma,
+ .struct_init_one_comma,
+ .struct_init_dot_two_comma,
+ .struct_init_dot_comma,
+ .struct_init_comma,
+ .array_init_one_comma,
+ .array_init_dot_two_comma,
+ .array_init_dot_comma,
+ .array_init_comma,
+ // The following always have a non-zero amount of members
+ // which is also the condition for them to be multi-line.
+ .block,
+ .block_semicolon,
+ => return true,
+ .block_two,
+ .block_two_semicolon,
+ => return tree.nodeData(node).opt_node_and_opt_node[0] != .none,
+ .container_decl,
+ .container_decl_arg,
+ .container_decl_two,
+ .tagged_union,
+ .tagged_union_enum_tag,
+ .tagged_union_two,
+ => {
+ var buf: [2]Ast.Node.Index = undefined;
+ const full = tree.fullContainerDecl(&buf, node).?;
+ if (full.ast.arg.unwrap()) |arg| {
+ if (becomesMultilineExpr(tree, arg))
+ return true;
+ }
+ // This does the same checks as `isOneLineContainerDecl`, however it avoids unnecessary
+ // checks related to comments and multiline strings, which would mean the container is
+ // already multiple lines.
+ for (full.ast.members) |member| {
+ if (tree.fullContainerField(member)) |field_full| {
+ for ([_]Ast.Node.OptionalIndex{
+ field_full.ast.type_expr,
+ field_full.ast.align_expr,
+ field_full.ast.value_expr,
+ }) |opt_expr| {
+ if (opt_expr.unwrap()) |expr| {
+ if (becomesMultilineExpr(tree, expr))
+ return true;
+ }
+ }
+ } else return true;
+ }
+ return false;
+ },
+ .error_set_decl => {
+ const lbrace, const rbrace = tree.nodeData(node).token_and_token;
+ return !isOneLineErrorSetDecl(tree, lbrace, rbrace);
+ },
+ .@"switch" => {
+ const op, const extra_index = tree.nodeData(node).node_and_extra;
+ const case_range = tree.extraData(extra_index, Ast.Node.SubRange);
+ return @intFromEnum(case_range.end) - @intFromEnum(case_range.start) != 0 or
+ becomesMultilineExpr(tree, op);
+ },
+ .for_simple, .@"for" => {
+ const full = tree.fullFor(node).?;
+ if (becomesMultilineExpr(tree, full.ast.then_expr) or
+ optBecomesMultilineExpr(tree, full.ast.else_expr))
+ return true;
+
+ for (full.ast.inputs) |expr| {
+ if (if (tree.nodeTag(expr) == .for_range) blk: {
+ const lhs, const rhs = tree.nodeData(expr).node_and_opt_node;
+ break :blk becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, rhs);
+ } else becomesMultilineExpr(tree, expr))
+ return true;
+ }
+ const final_input_expr = full.ast.inputs[full.ast.inputs.len - 1];
+ if (tree.tokenTag(tree.lastToken(final_input_expr) + 1) == .comma)
+ return true;
+
+ const token_tags = tree.tokens.items(.tag);
+ const payload = full.payload_token;
+ const pipe = std.mem.indexOfScalarPos(Token.Tag, token_tags, payload, .pipe).?;
+ return token_tags[@intCast(pipe - 1)] == .comma;
+ },
+ .while_simple,
+ .while_cont,
+ .@"while",
+ => {
+ const full = tree.fullWhile(node).?;
+ return becomesMultilineExpr(tree, full.ast.cond_expr) or
+ becomesMultilineExpr(tree, full.ast.then_expr) or
+ optBecomesMultilineExpr(tree, full.ast.cont_expr) or
+ optBecomesMultilineExpr(tree, full.ast.else_expr);
+ },
+ .if_simple,
+ .@"if",
+ => {
+ const full = tree.fullIf(node).?;
+ return becomesMultilineExpr(tree, full.ast.cond_expr) or
+ becomesMultilineExpr(tree, full.ast.then_expr) or
+ optBecomesMultilineExpr(tree, full.ast.else_expr);
+ },
+ .fn_proto_simple,
+ .fn_proto_multi,
+ .fn_proto_one,
+ .fn_proto,
+ => {
+ var buf: [1]Ast.Node.Index = undefined;
+ const fn_proto = tree.fullFnProto(&buf, node).?;
+
+ for ([_]Ast.Node.OptionalIndex{
+ fn_proto.ast.return_type,
+ fn_proto.ast.align_expr,
+ fn_proto.ast.addrspace_expr,
+ fn_proto.ast.section_expr,
+ fn_proto.ast.callconv_expr,
+ }) |opt_expr| {
+ if (opt_expr.unwrap()) |expr| {
+ if (becomesMultilineExpr(tree, expr))
+ return true;
+ }
+ }
+ for (fn_proto.ast.params) |expr| {
+ if (becomesMultilineExpr(tree, expr))
+ return true;
+ }
+
+ const lparen = fn_proto.ast.fn_token + 1;
+ const return_type = fn_proto.ast.return_type.unwrap().?;
+ const maybe_bang = tree.firstToken(return_type) - 1;
+ const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
+ return !isOneLineFnProto(tree, fn_proto, lparen, rparen);
+ },
+ .asm_simple,
+ => {
+ const lhs = tree.nodeData(node).node_and_token[0];
+ return becomesMultilineExpr(tree, lhs);
+ },
+ .@"asm",
+ => {
+ const lhs, const extra_index = tree.nodeData(node).node_and_extra;
+ const asm_extra = tree.extraData(extra_index, Ast.Node.Asm);
+ return @intFromEnum(asm_extra.items_end) - @intFromEnum(asm_extra.items_start) != 0 or
+ becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, asm_extra.clobbers);
+ },
+ .array_type, .array_type_sentinel => {
+ const array_type = tree.fullArrayType(node).?;
+ const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
+ return !isOneLineArrayType(tree, array_type, rbracket) or
+ becomesMultilineExpr(tree, array_type.ast.elem_type);
+ },
+ .array_access => {
+ const lhs, const rhs = tree.nodeData(node).node_and_node;
+ const lbracket = tree.firstToken(rhs) - 1;
+ const rbracket = tree.lastToken(rhs) + 1;
+ return !tree.tokensOnSameLine(lbracket, rbracket) or
+ becomesMultilineExpr(tree, lhs) or
+ becomesMultilineExpr(tree, rhs);
+ },
+ .call_one,
+ .call,
+ .builtin_call_two,
+ .builtin_call,
+ .array_init_one,
+ .array_init_dot_two,
+ .array_init_dot,
+ .array_init,
+ .struct_init_one,
+ .struct_init_dot_two,
+ .struct_init_dot,
+ .struct_init,
+ => |tag| {
+ var buf: [2]Ast.Node.Index = undefined;
+ const opt_lhs: Ast.Node.OptionalIndex, const items = switch (tag) {
+ .call_one, .call => blk: {
+ const full = tree.fullCall(buf[0..1], node).?;
+ break :blk .{ full.ast.fn_expr.toOptional(), full.ast.params };
+ },
+ .builtin_call_two, .builtin_call => .{ .none, tree.builtinCallParams(&buf, node).? },
+ .array_init_one,
+ .array_init_dot_two,
+ .array_init_dot,
+ .array_init,
+ => blk: {
+ const full = tree.fullArrayInit(&buf, node).?;
+ break :blk .{ full.ast.type_expr, full.ast.elements };
+ },
+ .struct_init_one,
+ .struct_init_dot_two,
+ .struct_init_dot,
+ .struct_init,
+ => blk: {
+ const full = tree.fullStructInit(&buf, node).?;
+ break :blk .{ full.ast.type_expr, full.ast.fields };
+ },
+ else => unreachable,
+ };
+ if (opt_lhs.unwrap()) |lhs| {
+ if (becomesMultilineExpr(tree, lhs))
+ return true;
+ }
+ for (items) |expr| {
+ if (becomesMultilineExpr(tree, expr))
+ return true;
+ }
+ return false;
+ },
+ .assign_destructure => {
+ const full = tree.assignDestructure(node);
+ for (full.ast.variables) |expr| {
+ if (becomesMultilineExpr(tree, expr))
+ return true;
+ }
+ return becomesMultilineExpr(tree, full.ast.value_expr);
+ },
+ .ptr_type_aligned,
+ .ptr_type_sentinel,
+ .ptr_type,
+ .ptr_type_bit_range,
+ => {
+ const full = tree.fullPtrType(node).?;
+ return becomesMultilineExpr(tree, full.ast.child_type) or
+ optBecomesMultilineExpr(tree, full.ast.sentinel) or
+ optBecomesMultilineExpr(tree, full.ast.align_node) or
+ optBecomesMultilineExpr(tree, full.ast.addrspace_node) or
+ optBecomesMultilineExpr(tree, full.ast.bit_range_start) or
+ optBecomesMultilineExpr(tree, full.ast.bit_range_end);
+ },
+ .slice_open,
+ .slice,
+ .slice_sentinel,
+ => {
+ const full = tree.fullSlice(node).?;
+ return becomesMultilineExpr(tree, full.ast.sliced) or
+ becomesMultilineExpr(tree, full.ast.start) or
+ optBecomesMultilineExpr(tree, full.ast.end) or
+ optBecomesMultilineExpr(tree, full.ast.sentinel);
+ },
+ .@"comptime",
+ .@"nosuspend",
+ .@"suspend",
+ .@"resume",
+ .bit_not,
+ .bool_not,
+ .negation,
+ .negation_wrap,
+ .optional_type,
+ .address_of,
+ .deref,
+ .@"try",
+ => return becomesMultilineExpr(tree, tree.nodeData(node).node),
+ .@"return" => return optBecomesMultilineExpr(tree, tree.nodeData(node).opt_node),
+ .field_access,
+ .unwrap_optional,
+ .grouped_expression,
+ => return becomesMultilineExpr(tree, tree.nodeData(node).node_and_token[0]),
+ .add,
+ .add_wrap,
+ .add_sat,
+ .array_cat,
+ .array_mult,
+ .bang_equal,
+ .bit_and,
+ .bit_or,
+ .shl,
+ .shl_sat,
+ .shr,
+ .bit_xor,
+ .bool_and,
+ .bool_or,
+ .div,
+ .equal_equal,
+ .greater_or_equal,
+ .greater_than,
+ .less_or_equal,
+ .less_than,
+ .merge_error_sets,
+ .mod,
+ .mul,
+ .mul_wrap,
+ .mul_sat,
+ .sub,
+ .sub_wrap,
+ .sub_sat,
+ .@"orelse",
+ .@"catch",
+ .error_union,
+ .assign,
+ .assign_bit_and,
+ .assign_bit_or,
+ .assign_shl,
+ .assign_shl_sat,
+ .assign_shr,
+ .assign_bit_xor,
+ .assign_div,
+ .assign_sub,
+ .assign_sub_wrap,
+ .assign_sub_sat,
+ .assign_mod,
+ .assign_add,
+ .assign_add_wrap,
+ .assign_add_sat,
+ .assign_mul,
+ .assign_mul_wrap,
+ .assign_mul_sat,
+ => {
+ const lhs, const rhs = tree.nodeData(node).node_and_node;
+ return becomesMultilineExpr(tree, lhs) or becomesMultilineExpr(tree, rhs);
+ },
+ .@"break", .@"continue" => {
+ const opt_expr = tree.nodeData(node).opt_token_and_opt_node[1];
+ return optBecomesMultilineExpr(tree, opt_expr);
+ },
+ .anyframe_type => return becomesMultilineExpr(tree, tree.nodeData(node).token_and_node[1]),
+ .@"errdefer",
+ .@"defer",
+ .for_range,
+ .switch_range,
+ .switch_case_one,
+ .switch_case_inline_one,
+ .switch_case,
+ .switch_case_inline,
+ .asm_output,
+ .asm_input,
+ .fn_decl,
+ .container_field,
+ .container_field_init,
+ .container_field_align,
+ .root,
+ .global_var_decl,
+ .local_var_decl,
+ .simple_var_decl,
+ .aligned_var_decl,
+ .test_decl,
+ => unreachable,
+ }
+}
+
+fn isOneLineArrayType(
+ tree: Ast,
+ array_type: Ast.full.ArrayType,
+ rbracket: Ast.TokenIndex,
+) bool {
+ return tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and
+ !becomesMultilineExpr(tree, array_type.ast.elem_count) and
+ !optBecomesMultilineExpr(tree, array_type.ast.sentinel);
+}
+
fn renderArrayType(
r: *Render,
array_type: Ast.full.ArrayType,
@@ -934,7 +1315,7 @@ fn renderArrayType(
const tree = r.tree;
const ais = r.ais;
const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
- const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);
+ const one_line = isOneLineArrayType(tree, array_type, rbracket);
const inner_space = if (one_line) Space.none else Space.newline;
try ais.pushIndent(.normal);
try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
@@ -951,6 +1332,7 @@ fn renderArrayType(
fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
const tree = r.tree;
const main_token = ptr_type.ast.main_token;
+
switch (ptr_type.size) {
.one => {
// Since ** tokens exist and the same token is shared by two
@@ -997,11 +1379,36 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
},
}
+ // .maybe_space cannot be used at the end of each qualifier since they may be reordered
+ const final_qual: enum {
+ @"volatile",
+ @"const",
+ @"addrspace",
+ @"align",
+ @"allowzero",
+ none,
+ } = if (ptr_type.volatile_token != null)
+ .@"volatile"
+ else if (ptr_type.const_token != null)
+ .@"const"
+ else if (ptr_type.ast.addrspace_node != .none)
+ .@"addrspace"
+ else if (ptr_type.ast.align_node != .none)
+ .@"align"
+ else if (ptr_type.allowzero_token != null)
+ .@"allowzero"
+ else
+ .none;
+ const final_qual_space: Space = if (tree.tokenTag(tree.firstToken(ptr_type.ast.child_type)) !=
+ .multiline_string_literal_line) .space else .none;
+
if (ptr_type.allowzero_token) |allowzero_token| {
- try renderToken(r, allowzero_token, .space);
+ const this_space: Space = if (final_qual == .@"allowzero") final_qual_space else .space;
+ try renderToken(r, allowzero_token, this_space);
}
if (ptr_type.ast.align_node.unwrap()) |align_node| {
+ const this_space: Space = if (final_qual == .@"align") final_qual_space else .space;
const align_first = tree.firstToken(align_node);
try renderToken(r, align_first - 2, .none); // align
try renderToken(r, align_first - 1, .none); // lparen
@@ -1012,26 +1419,29 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
try renderExpression(r, bit_range_start, .none);
try renderToken(r, tree.firstToken(bit_range_end) - 1, .none); // colon
try renderExpression(r, bit_range_end, .none);
- try renderToken(r, tree.lastToken(bit_range_end) + 1, .space); // rparen
+ try renderToken(r, tree.lastToken(bit_range_end) + 1, this_space); // rparen
} else {
- try renderToken(r, tree.lastToken(align_node) + 1, .space); // rparen
+ try renderToken(r, tree.lastToken(align_node) + 1, this_space); // rparen
}
}
if (ptr_type.ast.addrspace_node.unwrap()) |addrspace_node| {
+ const this_space: Space = if (final_qual == .@"addrspace") final_qual_space else .space;
const addrspace_first = tree.firstToken(addrspace_node);
try renderToken(r, addrspace_first - 2, .none); // addrspace
try renderToken(r, addrspace_first - 1, .none); // lparen
try renderExpression(r, addrspace_node, .none);
- try renderToken(r, tree.lastToken(addrspace_node) + 1, .space); // rparen
+ try renderToken(r, tree.lastToken(addrspace_node) + 1, this_space); // rparen
}
if (ptr_type.const_token) |const_token| {
- try renderToken(r, const_token, .space);
+ const this_space: Space = if (final_qual == .@"const") final_qual_space else .space;
+ try renderToken(r, const_token, this_space);
}
if (ptr_type.volatile_token) |volatile_token| {
- try renderToken(r, volatile_token, .space);
+ const this_space: Space = if (final_qual == .@"volatile") final_qual_space else unreachable;
+ try renderToken(r, volatile_token, this_space);
}
try renderExpression(r, ptr_type.ast.child_type, space);
@@ -1044,12 +1454,14 @@ fn renderSlice(
space: Space,
) Error!void {
const tree = r.tree;
- const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
+ const space_around_dots = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;
- const after_start_space = if (after_start_space_bool) Space.space else Space.none;
- const after_dots_space = if (slice.ast.end != .none)
- after_start_space
- else if (slice.ast.sentinel != .none) Space.space else Space.none;
+ const after_start_space: Space = if (space_around_dots) .space else .none;
+ const before_sentinel_space: Space = if (slice.ast.sentinel != .none) .space else .none;
+ const after_dots_space: Space = if (slice.ast.end != .none)
+ if (space_around_dots) .maybe_space else .none
+ else
+ before_sentinel_space;
try renderExpression(r, slice.ast.sliced, .none);
try renderToken(r, slice.ast.lbracket, .none); // lbracket
@@ -1059,8 +1471,7 @@ fn renderSlice(
try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")
if (slice.ast.end.unwrap()) |end| {
- const after_end_space = if (slice.ast.sentinel != .none) Space.space else Space.none;
- try renderExpression(r, end, after_end_space);
+ try renderExpression(r, end, before_sentinel_space);
}
if (slice.ast.sentinel.unwrap()) |sentinel| {
@@ -1088,7 +1499,7 @@ fn renderAsmOutput(
if (tree.tokenTag(symbolic_name + 4) == .arrow) {
const type_expr, const rparen = tree.nodeData(asm_output).opt_node_and_token;
- try renderToken(r, symbolic_name + 4, .space); // ->
+ try renderToken(r, symbolic_name + 4, .maybe_space); // ->
try renderExpression(r, type_expr.unwrap().?, Space.none);
return renderToken(r, rparen, space);
} else {
@@ -1169,33 +1580,38 @@ fn renderVarDeclWithoutFixups(
try renderToken(r, var_decl.ast.mut_token, .space); // var
- if (var_decl.ast.type_node != .none or var_decl.ast.align_node != .none or
- var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or
- var_decl.ast.init_node != .none)
- {
- const name_space = if (var_decl.ast.type_node == .none and
- (var_decl.ast.align_node != .none or
- var_decl.ast.addrspace_node != .none or
- var_decl.ast.section_node != .none or
- var_decl.ast.init_node != .none))
- Space.space
- else
- Space.none;
-
- try renderIdentifier(r, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
- } else {
- return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name
+ const last_component: enum {
+ value,
+ @"linksection",
+ @"addrspace",
+ @"align",
+ type,
+ identifier,
+ } = if (var_decl.ast.init_node != .none)
+ .value
+ else if (var_decl.ast.section_node != .none)
+ .@"linksection"
+ else if (var_decl.ast.addrspace_node != .none)
+ .@"addrspace"
+ else if (var_decl.ast.align_node != .none)
+ .@"align"
+ else if (var_decl.ast.type_node != .none)
+ .type
+ else
+ .identifier;
+
+ if (last_component == .identifier) {
+ return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing);
}
+ const after_ident_space: Space = if (var_decl.ast.type_node != .none) .none else .space;
+ try renderIdentifier(r, var_decl.ast.mut_token + 1, after_ident_space, .preserve_when_shadowing);
if (var_decl.ast.type_node.unwrap()) |type_node| {
- try renderToken(r, var_decl.ast.mut_token + 2, Space.space); // :
- if (var_decl.ast.align_node != .none or var_decl.ast.addrspace_node != .none or
- var_decl.ast.section_node != .none or var_decl.ast.init_node != .none)
- {
- try renderExpression(r, type_node, .space);
- } else {
+ try renderToken(r, var_decl.ast.mut_token + 2, .maybe_space); // :
+ if (last_component == .type) {
return renderExpression(r, type_node, space);
}
+ try renderExpression(r, type_node, .space);
}
if (var_decl.ast.align_node.unwrap()) |align_node| {
@@ -1205,13 +1621,10 @@ fn renderVarDeclWithoutFixups(
try renderToken(r, align_kw, Space.none); // align
try renderToken(r, lparen, Space.none); // (
try renderExpression(r, align_node, Space.none);
- if (var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or
- var_decl.ast.init_node != .none)
- {
- try renderToken(r, rparen, .space); // )
- } else {
+ if (last_component == .@"align") {
return renderToken(r, rparen, space); // )
}
+ try renderToken(r, rparen, .space); // )
}
if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
@@ -1221,12 +1634,10 @@ fn renderVarDeclWithoutFixups(
try renderToken(r, addrspace_kw, Space.none); // addrspace
try renderToken(r, lparen, Space.none); // (
try renderExpression(r, addrspace_node, Space.none);
- if (var_decl.ast.section_node != .none or var_decl.ast.init_node != .none) {
- try renderToken(r, rparen, .space); // )
- } else {
- try renderToken(r, rparen, .none); // )
- return renderToken(r, rparen + 1, Space.newline); // ;
+ if (last_component == .@"addrspace") {
+ return renderToken(r, rparen, space); // )
}
+ try renderToken(r, rparen, .space); // )
}
if (var_decl.ast.section_node.unwrap()) |section_node| {
@@ -1236,17 +1647,19 @@ fn renderVarDeclWithoutFixups(
try renderToken(r, section_kw, Space.none); // linksection
try renderToken(r, lparen, Space.none); // (
try renderExpression(r, section_node, Space.none);
- if (var_decl.ast.init_node != .none) {
- try renderToken(r, rparen, .space); // )
- } else {
+ if (last_component == .@"linksection") {
return renderToken(r, rparen, space); // )
}
+ try renderToken(r, rparen, .space); // )
}
+ assert(last_component == .value);
const init_node = var_decl.ast.init_node.unwrap().?;
const eq_token = tree.firstToken(init_node) - 1;
- const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
+ const rhs_seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
+ tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
+ const eq_space: Space = if (rhs_seperate_line) .newline else .space;
try ais.pushIndent(.after_equals);
try renderToken(r, eq_token, eq_space); // =
try renderExpression(r, init_node, space); // ;
@@ -1346,8 +1759,10 @@ fn renderThenElse(
const tree = r.tree;
const ais = r.ais;
const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));
+ const then_expr_first_token = tree.firstToken(then_expr);
const indent_then_expr = !then_expr_is_block and
- !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));
+ (!tree.tokensOnSameLine(last_prefix_token, then_expr_first_token) or
+ tree.tokenTag(then_expr_first_token) == .multiline_string_literal_line);
if (indent_then_expr) try ais.pushIndent(.normal);
@@ -1381,7 +1796,9 @@ fn renderThenElse(
const indent_else_expr = indent_then_expr and
!nodeIsBlock(tree.nodeTag(else_expr)) and
- !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr));
+ !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr)) or
+ tree.tokenTag(tree.firstToken(else_expr)) ==
+ .multiline_string_literal_line;
if (indent_else_expr) {
try ais.pushIndent(.normal);
try renderToken(r, last_else_token, .newline);
@@ -1417,44 +1834,29 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
try renderParamList(r, lparen, for_node.ast.inputs, .space);
var cur = for_node.payload_token;
- const pipe = std.mem.findScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;
- if (tree.tokenTag(@intCast(pipe - 1)) == .comma) {
+ const pipe = std.mem.findScalarPos(Token.Tag, token_tags, cur, .pipe).?;
+ const capture_trailing_comma = token_tags[@intCast(pipe - 1)] == .comma;
+
+ if (capture_trailing_comma)
try ais.pushIndent(.normal);
- try renderToken(r, cur - 1, .newline); // |
- while (true) {
- if (tree.tokenTag(cur) == .asterisk) {
- try renderToken(r, cur, .none); // *
- cur += 1;
- }
- try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
+ try renderToken(r, cur - 1, if (capture_trailing_comma) .newline else .none); // |
+ while (true) {
+ if (token_tags[cur] == .asterisk) {
+ try renderToken(r, cur, .none); // *
cur += 1;
- if (tree.tokenTag(cur) == .comma) {
- try renderToken(r, cur, .newline); // ,
- cur += 1;
- }
- if (tree.tokenTag(cur) == .pipe) {
- break;
- }
}
- ais.popIndent();
- } else {
- try renderToken(r, cur - 1, .none); // |
- while (true) {
- if (tree.tokenTag(cur) == .asterisk) {
- try renderToken(r, cur, .none); // *
- cur += 1;
- }
- try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
+ try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
+ cur += 1;
+ if (token_tags[cur] == .comma) {
+ try renderToken(r, cur, if (capture_trailing_comma) .newline else .space); // ,
cur += 1;
- if (tree.tokenTag(cur) == .comma) {
- try renderToken(r, cur, .space); // ,
- cur += 1;
- }
- if (tree.tokenTag(cur) == .pipe) {
- break;
- }
+ }
+ if (token_tags[cur] == .pipe) {
+ break;
}
}
+ if (capture_trailing_comma)
+ ais.popIndent();
try renderThenElse(
r,
@@ -1483,97 +1885,80 @@ fn renderContainerField(
};
if (field.comptime_token) |t| {
- try renderToken(r, t, .space); // comptime
- }
- if (field.ast.type_expr == .none and field.ast.value_expr == .none) {
- if (field.ast.align_expr.unwrap()) |align_expr| {
- try renderIdentifier(r, field.ast.main_token, .space, quote); // name
- const lparen_token = tree.firstToken(align_expr) - 1;
- const align_kw = lparen_token - 1;
- const rparen_token = tree.lastToken(align_expr) + 1;
- try renderToken(r, align_kw, .none); // align
- try renderToken(r, lparen_token, .none); // (
- try renderExpression(r, align_expr, .none); // alignment
- return renderToken(r, rparen_token, .space); // )
+ try renderToken(r, t, .maybe_space); // comptime
+ }
+
+ const last_component: enum {
+ value,
+ @"align",
+ type,
+ identifier,
+ } = if (field.ast.value_expr != .none)
+ .value
+ else if (field.ast.align_expr != .none)
+ .@"align"
+ else if (field.ast.type_expr != .none)
+ .type
+ else if (!field.ast.tuple_like)
+ .identifier
+ else
+ unreachable;
+
+ if (!field.ast.tuple_like) {
+ if (last_component == .identifier) {
+ return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
}
- return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
+ const this_space: Space = if (field.ast.type_expr != .none) .none else .space;
+ try renderIdentifier(r, field.ast.main_token, this_space, quote); // name
}
- if (field.ast.type_expr != .none and field.ast.value_expr == .none) {
- const type_expr = field.ast.type_expr.unwrap().?;
+
+ if (field.ast.type_expr.unwrap()) |type_expr| {
if (!field.ast.tuple_like) {
- try renderIdentifier(r, field.ast.main_token, .none, quote); // name
- try renderToken(r, field.ast.main_token + 1, .space); // :
+ try renderToken(r, field.ast.main_token + 1, .maybe_space); // :
}
- if (field.ast.align_expr.unwrap()) |align_expr| {
- try renderExpression(r, type_expr, .space); // type
- const align_token = tree.firstToken(align_expr) - 2;
- try renderToken(r, align_token, .none); // align
- try renderToken(r, align_token + 1, .none); // (
- try renderExpression(r, align_expr, .none); // alignment
- const rparen = tree.lastToken(align_expr) + 1;
- return renderTokenComma(r, rparen, space); // )
- } else {
+ if (last_component == .type) {
return renderExpressionComma(r, type_expr, space); // type
}
+ try renderExpression(r, type_expr, .space); // type
}
- if (field.ast.type_expr == .none and field.ast.value_expr != .none) {
- const value_expr = field.ast.value_expr.unwrap().?;
-
- try renderIdentifier(r, field.ast.main_token, .space, quote); // name
- if (field.ast.align_expr.unwrap()) |align_expr| {
- const lparen_token = tree.firstToken(align_expr) - 1;
- const align_kw = lparen_token - 1;
- const rparen_token = tree.lastToken(align_expr) + 1;
- try renderToken(r, align_kw, .none); // align
- try renderToken(r, lparen_token, .none); // (
- try renderExpression(r, align_expr, .none); // alignment
- try renderToken(r, rparen_token, .space); // )
- }
- try renderToken(r, field.ast.main_token + 1, .space); // =
- return renderExpressionComma(r, value_expr, space); // value
- }
- if (!field.ast.tuple_like) {
- try renderIdentifier(r, field.ast.main_token, .none, quote); // name
- try renderToken(r, field.ast.main_token + 1, .space); // :
- }
-
- const type_expr = field.ast.type_expr.unwrap().?;
- const value_expr = field.ast.value_expr.unwrap().?;
-
- try renderExpression(r, type_expr, .space); // type
if (field.ast.align_expr.unwrap()) |align_expr| {
- const lparen_token = tree.firstToken(align_expr) - 1;
- const align_kw = lparen_token - 1;
- const rparen_token = tree.lastToken(align_expr) + 1;
- try renderToken(r, align_kw, .none); // align
- try renderToken(r, lparen_token, .none); // (
+ const align_token = tree.firstToken(align_expr) - 2;
+ try renderToken(r, align_token, .none); // align
+ try renderToken(r, align_token + 1, .none); // (
try renderExpression(r, align_expr, .none); // alignment
- try renderToken(r, rparen_token, .space); // )
+ const rparen = tree.lastToken(align_expr) + 1;
+ if (last_component == .@"align") {
+ return renderTokenComma(r, rparen, space); // )
+ }
+ try renderToken(r, rparen, .space);
}
- const eq_token = tree.firstToken(value_expr) - 1;
- const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
- try ais.pushIndent(.after_equals);
- try renderToken(r, eq_token, eq_space); // =
+ if (field.ast.value_expr.unwrap()) |value_expr| {
+ assert(last_component == .value);
+ const eq_token = tree.firstToken(value_expr) - 1;
+ const seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
+ tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
+ const eq_space: Space = if (seperate_line) .newline else .space;
- if (eq_space == .space) {
- ais.popIndent();
- try renderExpressionComma(r, value_expr, space); // value
- return;
- }
-
- const maybe_comma = tree.lastToken(value_expr) + 1;
+ try ais.pushIndent(.after_equals);
+ try renderToken(r, eq_token, eq_space); // =
+ if (eq_space == .space) {
+ ais.popIndent();
+ return renderExpressionComma(r, value_expr, space); // value
+ }
- if (tree.tokenTag(maybe_comma) == .comma) {
- try renderExpression(r, value_expr, .none); // value
- ais.popIndent();
- try renderToken(r, maybe_comma, .newline);
- } else {
- try renderExpression(r, value_expr, space); // value
- ais.popIndent();
- }
+ const maybe_comma = tree.lastToken(value_expr) + 1;
+ if (tree.tokenTag(maybe_comma) == .comma) {
+ try renderExpression(r, value_expr, .none); // value
+ ais.popIndent();
+ try renderToken(r, maybe_comma, .newline);
+ } else {
+ try renderExpression(r, value_expr, space); // value
+ ais.popIndent();
+ }
+ } else unreachable;
}
fn renderBuiltinCall(
@@ -1587,14 +1972,9 @@ fn renderBuiltinCall(
try renderToken(r, builtin_token, .none); // @name
- if (params.len == 0) {
- try renderToken(r, builtin_token + 1, .none); // (
- return renderToken(r, builtin_token + 2, space); // )
- }
-
if (r.fixups.rebase_imported_paths) |prefix| {
const slice = tree.tokenSlice(builtin_token);
- if (mem.eql(u8, slice, "@import")) f: {
+ if (params.len != 0 and mem.eql(u8, slice, "@import")) f: {
const param = params[0];
const str_lit_token = tree.nodeMainToken(param);
assert(tree.tokenTag(str_lit_token) == .string_literal);
@@ -1613,45 +1993,69 @@ fn renderBuiltinCall(
}
}
- const last_param = params[params.len - 1];
- const after_last_param_token = tree.lastToken(last_param) + 1;
-
- if (tree.tokenTag(after_last_param_token) != .comma) {
- // Render all on one line, no trailing comma.
- try renderToken(r, builtin_token + 1, .none); // (
-
- for (params, 0..) |param_node, i| {
- const first_param_token = tree.firstToken(param_node);
- if (tree.tokenTag(first_param_token) == .multiline_string_literal_line or
- hasSameLineComment(tree, first_param_token - 1))
- {
- try ais.pushIndent(.normal);
- try renderExpression(r, param_node, .none);
- ais.popIndent();
- } else {
- try renderExpression(r, param_node, .none);
- }
+ return renderParamList(r, builtin_token + 1, params, space);
+}
- if (i + 1 < params.len) {
- const comma_token = tree.lastToken(param_node) + 1;
- try renderToken(r, comma_token, .space); // ,
- }
+fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex {
+ // These may appear in any order, so we have to check the token_starts array
+ // to find out which is first.
+ var rparen = if (tree.tokenTag(maybe_bang) == .bang) maybe_bang - 1 else maybe_bang;
+ var smallest_start = tree.tokenStart(maybe_bang);
+ if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
+ const tok = tree.firstToken(align_expr) - 3;
+ const start = tree.tokenStart(tok);
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
}
- return renderToken(r, after_last_param_token, space); // )
- } else {
- // Render one param per line.
- try ais.pushIndent(.normal);
- try renderToken(r, builtin_token + 1, Space.newline); // (
-
- for (params) |param_node| {
- try ais.pushSpace(.comma);
- try renderExpression(r, param_node, .comma);
- ais.popSpace();
+ }
+ if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
+ const tok = tree.firstToken(addrspace_expr) - 3;
+ const start = tree.tokenStart(tok);
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
+ }
+ }
+ if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
+ const tok = tree.firstToken(section_expr) - 3;
+ const start = tree.tokenStart(tok);
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
+ }
+ }
+ if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
+ const tok = tree.firstToken(callconv_expr) - 3;
+ const start = tree.tokenStart(tok);
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
}
- ais.popIndent();
-
- return renderToken(r, after_last_param_token + 1, space); // )
}
+ assert(tree.tokenTag(rparen) == .r_paren);
+ return rparen;
+}
+
+fn isOneLineFnProto(
+ tree: Ast,
+ fn_proto: Ast.full.FnProto,
+ lparen: Ast.TokenIndex,
+ rparen: Ast.TokenIndex,
+) bool {
+ const trailing_comma = tree.tokenTag(rparen - 1) == .comma;
+ if (trailing_comma or hasComment(tree, lparen, rparen))
+ return false;
+
+ // Check that there are no doc comments
+ var after_last_param = lparen + 1;
+ for (fn_proto.ast.params) |expr| {
+ // Looking before each param is insufficient since anytype is not included in `params`
+ if (hasDocComment(tree, after_last_param, tree.firstToken(expr)))
+ return false;
+ after_last_param = tree.lastToken(expr) + 1;
+ }
+ return !hasDocComment(tree, after_last_param, rparen);
}
fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void {
@@ -1671,51 +2075,11 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
const return_type = fn_proto.ast.return_type.unwrap().?;
const maybe_bang = tree.firstToken(return_type) - 1;
- const rparen = blk: {
- // These may appear in any order, so we have to check the token_starts array
- // to find out which is first.
- var rparen = if (tree.tokenTag(maybe_bang) == .bang) maybe_bang - 1 else maybe_bang;
- var smallest_start = tree.tokenStart(maybe_bang);
- if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
- const tok = tree.firstToken(align_expr) - 3;
- const start = tree.tokenStart(tok);
- if (start < smallest_start) {
- rparen = tok;
- smallest_start = start;
- }
- }
- if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
- const tok = tree.firstToken(addrspace_expr) - 3;
- const start = tree.tokenStart(tok);
- if (start < smallest_start) {
- rparen = tok;
- smallest_start = start;
- }
- }
- if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
- const tok = tree.firstToken(section_expr) - 3;
- const start = tree.tokenStart(tok);
- if (start < smallest_start) {
- rparen = tok;
- smallest_start = start;
- }
- }
- if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
- const tok = tree.firstToken(callconv_expr) - 3;
- const start = tree.tokenStart(tok);
- if (start < smallest_start) {
- rparen = tok;
- smallest_start = start;
- }
- }
- break :blk rparen;
- };
- assert(tree.tokenTag(rparen) == .r_paren);
+ const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
// The params list is a sparse set that does *not* include anytype or ... parameters.
- const trailing_comma = tree.tokenTag(rparen - 1) == .comma;
- if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
+ if (isOneLineFnProto(tree, fn_proto, lparen, rparen)) {
// Render all on one line, no trailing comma.
try renderToken(r, lparen, .none); // (
@@ -1724,10 +2088,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
while (true) {
last_param_token += 1;
switch (tree.tokenTag(last_param_token)) {
- .doc_comment => {
- try renderToken(r, last_param_token, .newline);
- continue;
- },
+ .doc_comment => unreachable,
.ellipsis3 => {
try renderToken(r, last_param_token, .none); // ...
break;
@@ -1743,7 +2104,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
},
.r_paren => break,
.comma => {
- try renderToken(r, last_param_token, .space); // ,
+ try renderToken(r, last_param_token, .maybe_space); // ,
continue;
},
else => {}, // Parameter type without a name.
@@ -1753,7 +2114,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
{
try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
last_param_token = last_param_token + 1;
- try renderToken(r, last_param_token, .space); // :
+ try renderToken(r, last_param_token, .maybe_space); // :
last_param_token += 1;
}
if (tree.tokenTag(last_param_token) == .keyword_anytype) {
@@ -1822,7 +2183,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
ais.popIndent();
}
- try renderToken(r, rparen, .space); // )
+ try renderToken(r, rparen, .maybe_space); // )
if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
const align_lparen = tree.firstToken(align_expr) - 1;
@@ -1831,7 +2192,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
try renderToken(r, align_lparen - 1, .none); // align
try renderToken(r, align_lparen, .none); // (
try renderExpression(r, align_expr, .none);
- try renderToken(r, align_rparen, .space); // )
+ try renderToken(r, align_rparen, .maybe_space); // )
}
if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
@@ -1841,7 +2202,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
try renderToken(r, align_lparen - 1, .none); // addrspace
try renderToken(r, align_lparen, .none); // (
try renderExpression(r, addrspace_expr, .none);
- try renderToken(r, align_rparen, .space); // )
+ try renderToken(r, align_rparen, .maybe_space); // )
}
if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
@@ -1851,7 +2212,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
try renderToken(r, section_lparen - 1, .none); // section
try renderToken(r, section_lparen, .none); // (
try renderExpression(r, section_expr, .none);
- try renderToken(r, section_rparen, .space); // )
+ try renderToken(r, section_rparen, .maybe_space); // )
}
if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
@@ -1865,7 +2226,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
try renderToken(r, callconv_lparen - 1, .none); // callconv
try renderToken(r, callconv_lparen, .none); // (
try renderExpression(r, callconv_expr, .none);
- try renderToken(r, callconv_rparen, .space); // )
+ try renderToken(r, callconv_rparen, .maybe_space); // )
}
}
@@ -1890,7 +2251,7 @@ fn renderSwitchCase(
// render inline keyword
if (switch_case.inline_token) |some| {
- try renderToken(r, some, .space);
+ try renderToken(r, some, .maybe_space);
}
// Render everything before the arrow
@@ -1904,33 +2265,26 @@ fn renderSwitchCase(
} else {
// Render on one line
for (switch_case.ast.values) |value_expr| {
- try renderExpression(r, value_expr, .comma_space);
+ try renderExpression(r, value_expr, .comma_maybe_space);
}
}
- // Render the arrow and everything after it
- const pre_target_space = if (tree.nodeTag(switch_case.ast.target_expr) == .multiline_string_literal)
- // Newline gets inserted when rendering the target expr.
- Space.none
- else
- Space.space;
- const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
- try renderToken(r, switch_case.ast.arrow_token, after_arrow_space); // =>
+ try renderToken(r, switch_case.ast.arrow_token, .maybe_space); // =>
if (switch_case.payload_token) |payload_token| {
try renderToken(r, payload_token - 1, .none); // pipe
- const ident = payload_token + @intFromBool(tree.tokenTag(payload_token) == .asterisk);
- if (tree.tokenTag(payload_token) == .asterisk) {
+ var ident = payload_token;
+ if (tree.tokenTag(ident) == .asterisk) {
try renderToken(r, payload_token, .none); // asterisk
+ ident += 1;
}
try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
if (tree.tokenTag(ident + 1) == .comma) {
- try renderToken(r, ident + 1, .space); // ,
- try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // identifier
- try renderToken(r, ident + 3, pre_target_space); // pipe
- } else {
- try renderToken(r, ident + 1, pre_target_space); // pipe
+ ident += 2;
+ try renderToken(r, ident - 1, .space); // ,
+ try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
}
+ try renderToken(r, ident + 1, .maybe_space); // pipe
}
try renderExpression(r, switch_case.ast.target_expr, space);
@@ -2018,26 +2372,13 @@ fn renderStructInit(
try ais.pushIndent(.normal);
try renderToken(r, struct_init.ast.lbrace, .newline);
- try renderToken(r, struct_init.ast.lbrace + 1, .none); // .
- try renderIdentifier(r, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
- // Don't output a space after the = if expression is a multiline string,
- // since then it will start on the next line.
- const field_node = struct_init.ast.fields[0];
- const expr = tree.nodeTag(field_node);
- var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;
- try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =
-
- try ais.pushSpace(.comma);
- try renderExpressionFixup(r, field_node, .comma);
- ais.popSpace();
-
- for (struct_init.ast.fields[1..]) |field_init| {
+ for (0.., struct_init.ast.fields) |i, field_init| {
const init_token = tree.firstToken(field_init);
- try renderExtraNewlineToken(r, init_token - 3);
+ if (i != 0)
+ try renderExtraNewlineToken(r, init_token - 3);
try renderToken(r, init_token - 3, .none); // .
try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
- space_after_equal = if (tree.nodeTag(field_init) == .multiline_string_literal) .none else .space;
- try renderToken(r, init_token - 1, space_after_equal); // =
+ try renderToken(r, init_token - 1, .maybe_space); // =
try ais.pushSpace(.comma);
try renderExpressionFixup(r, field_init, .comma);
@@ -2053,8 +2394,7 @@ fn renderStructInit(
const init_token = tree.firstToken(field_init);
try renderToken(r, init_token - 3, .none); // .
try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
- const space_after_equal: Space = if (tree.nodeTag(field_init) == .multiline_string_literal) .none else .space;
- try renderToken(r, init_token - 1, space_after_equal); // =
+ try renderToken(r, init_token - 1, .maybe_space); // =
try renderExpressionFixup(r, field_init, .comma_space);
}
}
@@ -2122,174 +2462,176 @@ fn renderArrayInit(
try ais.pushIndent(.normal);
try renderToken(r, array_init.ast.lbrace, .newline);
-
- var expr_index: usize = 0;
- while (true) {
- const row_size = rowSize(tree, array_init.ast.elements[expr_index..], rbrace);
- const row_exprs = array_init.ast.elements[expr_index..];
- // A place to store the width of each expression and its column's maximum
- const widths = try gpa.alloc(usize, row_exprs.len + row_size);
- defer gpa.free(widths);
- @memset(widths, 0);
-
- const expr_newlines = try gpa.alloc(bool, row_exprs.len);
- defer gpa.free(expr_newlines);
- @memset(expr_newlines, false);
-
- const expr_widths = widths[0..row_exprs.len];
- const column_widths = widths[row_exprs.len..];
-
- // Find next row with trailing comment (if any) to end the current section.
- const section_end = sec_end: {
- var this_line_first_expr: usize = 0;
- var this_line_size = rowSize(tree, row_exprs, rbrace);
- for (row_exprs, 0..) |expr, i| {
- // Ignore comment on first line of this section.
- if (i == 0) continue;
- const expr_last_token = tree.lastToken(expr);
- if (tree.tokensOnSameLine(tree.firstToken(row_exprs[0]), expr_last_token))
- continue;
- // Track start of line containing comment.
- if (!tree.tokensOnSameLine(tree.firstToken(row_exprs[this_line_first_expr]), expr_last_token)) {
- this_line_first_expr = i;
- this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rbrace);
- }
-
- const maybe_comma = expr_last_token + 1;
- if (tree.tokenTag(maybe_comma) == .comma) {
- if (hasSameLineComment(tree, maybe_comma))
- break :sec_end i - this_line_size + 1;
- }
- }
- break :sec_end row_exprs.len;
- };
- expr_index += section_end;
-
- const section_exprs = row_exprs[0..section_end];
-
- var sub_expr_buffer: Writer.Allocating = .init(gpa);
- defer sub_expr_buffer.deinit();
-
- const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);
- defer gpa.free(sub_expr_buffer_starts);
-
- var auto_indenting_stream: AutoIndentingStream = .init(gpa, &sub_expr_buffer.writer, indent_delta);
- defer auto_indenting_stream.deinit();
- var sub_render: Render = .{
+ try ais.pushSpace(.comma);
+
+ const expr_widths = try gpa.alloc(enum(usize) {
+ /// The expression contains non-printable characters (e.g. unicode / newlines)
+ /// or has formatting disabled at the start or end.
+ nonprint = std.math.maxInt(usize),
+ _,
+ }, array_init.ast.elements.len);
+ defer gpa.free(expr_widths);
+ {
+ var buf: Writer.Allocating = .init(gpa);
+ defer buf.deinit();
+ var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta);
+ sub_ais.disabled_offset = ais.disabled_offset;
+ defer sub_ais.deinit();
+ var sub_r: Render = .{
.gpa = r.gpa,
- .ais = &auto_indenting_stream,
+ .ais = &sub_ais,
.tree = r.tree,
.fixups = r.fixups,
};
-
- // Calculate size of columns in current section
- var column_counter: usize = 0;
- var single_line = true;
- var contains_newline = false;
- for (section_exprs, 0..) |expr, i| {
- const start = sub_expr_buffer.written().len;
- sub_expr_buffer_starts[i] = start;
-
- if (i + 1 < section_exprs.len) {
- try renderExpression(&sub_render, expr, .none);
- const written = sub_expr_buffer.written();
- const width = written.len - start;
- const this_contains_newline = mem.findScalar(u8, written[start..], '\n') != null;
- contains_newline = contains_newline or this_contains_newline;
- expr_widths[i] = width;
- expr_newlines[i] = this_contains_newline;
-
- if (!this_contains_newline) {
- const column = column_counter % row_size;
- column_widths[column] = @max(column_widths[column], width);
-
- const expr_last_token = tree.lastToken(expr) + 1;
- const next_expr = section_exprs[i + 1];
- column_counter += 1;
- if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(next_expr))) single_line = false;
- } else {
- single_line = false;
- column_counter = 0;
- }
+ for (array_init.ast.elements, expr_widths) |e, *width| {
+ const begin_disabled = sub_ais.disabled_offset != null;
+ // `.skip` space so trailing commments aren't included
+ try renderExpressionComma(&sub_r, e, .skip);
+ if (!begin_disabled and sub_ais.disabled_offset == null) {
+ const w = buf.written();
+ width.* = for (w) |c| {
+ if (!std.ascii.isPrint(c))
+ break .nonprint;
+ } else @enumFromInt(w.len - @intFromBool(w[w.len - 1] == ','));
} else {
- try ais.pushSpace(.comma);
- try renderExpression(&sub_render, expr, .comma);
- ais.popSpace();
+ width.* = .nonprint;
+ }
- const written = sub_expr_buffer.written();
- const width = written.len - start - 2;
- const this_contains_newline = mem.findScalar(u8, written[start .. written.len - 1], '\n') != null;
- contains_newline = contains_newline or this_contains_newline;
- expr_widths[i] = width;
- expr_newlines[i] = contains_newline;
+ // Write trailing comments since they may enable/disable zig fmt
+ buf.clearRetainingCapacity();
+ var after_expr = tree.lastToken(e);
+ after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
+ try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none);
- if (!contains_newline) {
- const column = column_counter % row_size;
- column_widths[column] = @max(column_widths[column], width);
- }
- }
+ buf.clearRetainingCapacity();
}
- sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.written().len;
-
- // Render exprs in current section.
- column_counter = 0;
- for (section_exprs, 0..) |expr, i| {
- const start = sub_expr_buffer_starts[i];
- const end = sub_expr_buffer_starts[i + 1];
- const expr_text = sub_expr_buffer.written()[start..end];
- if (!expr_newlines[i]) {
- try ais.writeAll(expr_text);
- } else {
- var by_line = std.mem.splitScalar(u8, expr_text, '\n');
- var last_line_was_empty = false;
- try ais.writeAll(by_line.first());
- while (by_line.next()) |line| {
- if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) {
- try ais.insertNewline();
- } else {
- try ais.maybeInsertNewline();
- }
- last_line_was_empty = (line.len == 0);
- try ais.writeAll(line);
- }
- }
-
- if (i + 1 < section_exprs.len) {
- const next_expr = section_exprs[i + 1];
- const comma = tree.lastToken(expr) + 1;
+ }
- if (column_counter != row_size - 1) {
- if (!expr_newlines[i] and !expr_newlines[i + 1]) {
- // Neither the current or next expression is multiline
- try renderToken(r, comma, .space); // ,
- assert(column_widths[column_counter % row_size] >= expr_widths[i]);
- const padding = column_widths[column_counter % row_size] - expr_widths[i];
- try ais.splatByteAll(' ', padding);
+ var remaining_exprs = array_init.ast.elements;
+ var remaining_widths = expr_widths;
+ while (remaining_exprs.len != 0) {
+ var row_size: usize = 1;
+ for (1.., remaining_exprs, remaining_widths) |len, e, w| {
+ if (w == .nonprint) break;
+ row_size = len;
- column_counter += 1;
- continue;
- }
- }
+ var after_expr = tree.lastToken(e);
+ after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
+ assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
+ if (!tree.tokensOnSameLine(after_expr, after_expr + 1))
+ break;
+ } else {
+ // All the expressions are on the same line.
+ // However, if there is a trailing comma, we put them each on their own line.
+ if (tree.tokenTag(rbrace - 1) == .comma)
+ row_size = 1;
+ }
- if (single_line and row_size != 1) {
- try renderToken(r, comma, .space); // ,
- continue;
+ // Determine the size of this section
+ const section_end = end: {
+ var line_start = row_size; // Start after the first row to ignore comments on it
+ break :end for (line_start.., remaining_exprs[line_start..]) |i, e| {
+ const expr_first = tree.firstToken(e);
+ // Any nonprint character terminates the line because they are always put on their
+ // own line, so they will not end up on the same line as the trailing comment.
+ if (expr_widths[i - 1] == .nonprint or !tree.tokensOnSameLine(expr_first - 1, expr_first)) {
+ line_start = i;
}
- column_counter = 0;
- try renderToken(r, comma, .newline); // ,
- try renderExtraNewline(r, next_expr);
+ var after_expr = tree.lastToken(e);
+ after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
+ assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
+ if (hasTrailingComment(tree, after_expr))
+ break line_start;
+ } else remaining_exprs.len;
+ };
+ const section_exprs = remaining_exprs[0..section_end];
+ const section_widths = remaining_widths[0..section_end];
+ remaining_exprs = remaining_exprs[section_end..];
+ remaining_widths = remaining_widths[section_end..];
+
+ // Determine the width of each column
+ var col_widths = try gpa.alloc(usize, row_size);
+ defer gpa.free(col_widths);
+ @memset(col_widths, 0);
+
+ var col: usize = 0;
+ for (section_widths) |w| {
+ if (w == .nonprint) {
+ col = 0;
+ continue;
+ }
+ col_widths[col] = @max(col_widths[col], @intFromEnum(w));
+ col += 1;
+ if (col == row_size) {
+ col = 0;
}
}
- if (expr_index == array_init.ast.elements.len)
- break;
+ // Render each expression
+ col = 0;
+ for (0.., section_exprs, section_widths) |i, e, w| {
+ if (i + 1 == section_end or col + 1 == row_size or
+ w == .nonprint or section_widths[i + 1] == .nonprint)
+ {
+ try renderExpression(r, e, .comma);
+ col = 0;
+ if (i + 1 != section_end) {
+ try renderExtraNewline(r, section_exprs[i + 1]);
+ }
+ } else {
+ try renderExpression(r, e, .comma_space);
+ try ais.splatByteAll(' ', col_widths[col] - @intFromEnum(w));
+ col += 1;
+ }
+ }
}
+ ais.popSpace();
ais.popIndent();
return renderToken(r, rbrace, space); // rbrace
}
+fn isOneLineErrorSetDecl(
+ tree: Ast,
+ lbrace: Ast.TokenIndex,
+ rbrace: Ast.TokenIndex,
+) bool {
+ // If there is a trailing comma, comment, or document comment, then render each
+ // item on its own line.
+ return tree.tokenTag(rbrace - 1) != .comma and
+ !hasDocComment(tree, lbrace + 1, rbrace) and
+ !hasComment(tree, lbrace, rbrace);
+}
+
+fn isOneLineContainerDecl(
+ tree: Ast,
+ container_decl: Ast.full.ContainerDecl,
+ lbrace: Ast.TokenIndex,
+ rbrace: Ast.TokenIndex,
+) bool {
+ // We print all the members in one-line unless one of the following conditions are true:
+
+ // 1. The container has comments or multiline strings.
+ if (hasComment(tree, lbrace, rbrace) or hasMultilineString(tree, lbrace, rbrace)) {
+ return false;
+ }
+
+ // 2. The container has a container comment.
+ if (tree.tokenTag(lbrace + 1) == .container_doc_comment) return false;
+
+ // 3. A member of the container has a doc comment.
+ if (hasDocComment(tree, lbrace + 1, rbrace))
+ return false;
+
+ // 4. The container has non-field members.
+ for (container_decl.ast.members) |member| {
+ if (tree.fullContainerField(member) == null) return false;
+ }
+
+ return true;
+}
+
fn renderContainerDecl(
r: *Render,
container_decl_node: Ast.Node.Index,
@@ -2354,27 +2696,7 @@ fn renderContainerDecl(
}
const src_has_trailing_comma = tree.tokenTag(rbrace - 1) == .comma;
- if (!src_has_trailing_comma) one_line: {
- // We print all the members in-line unless one of the following conditions are true:
-
- // 1. The container has comments or multiline strings.
- if (hasComment(tree, lbrace, rbrace) or hasMultilineString(tree, lbrace, rbrace)) {
- break :one_line;
- }
-
- // 2. The container has a container comment.
- if (tree.tokenTag(lbrace + 1) == .container_doc_comment) break :one_line;
-
- // 3. A member of the container has a doc comment.
- for (tree.tokens.items(.tag)[lbrace + 1 .. rbrace - 1]) |tag| {
- if (tag == .doc_comment) break :one_line;
- }
-
- // 4. The container has non-field members.
- for (container_decl.ast.members) |member| {
- if (tree.fullContainerField(member) == null) break :one_line;
- }
-
+ if (!src_has_trailing_comma and isOneLineContainerDecl(tree, container_decl, lbrace, rbrace)) {
// Print all the declarations on the same line.
try renderToken(r, lbrace, .space); // lbrace
for (container_decl.ast.members) |member| {
@@ -2436,7 +2758,7 @@ fn renderAsm(
const first_clobber = tree.firstToken(clobbers);
try renderToken(r, first_clobber - 3, .none);
try renderToken(r, first_clobber - 2, .none);
- try renderToken(r, first_clobber - 1, .space);
+ try renderToken(r, first_clobber - 1, .maybe_space);
try renderExpression(r, clobbers, .none);
ais.popIndent();
return renderToken(r, asm_node.ast.rparen, space); // rparen
@@ -2450,6 +2772,9 @@ fn renderAsm(
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);
+
ais.setIndentDelta(asm_indent_delta);
const colon1 = tree.lastToken(asm_node.ast.template) + 1;
@@ -2527,9 +2852,10 @@ fn renderAsm(
unreachable;
};
- try renderToken(r, colon3, .space); // :
+ 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(indent_delta);
ais.popIndent();
return renderToken(r, asm_node.ast.rparen, space); // rparen
@@ -2555,7 +2881,7 @@ fn renderParamList(
if (params.len == 0) {
try ais.pushIndent(.normal);
- try renderToken(r, lparen, .none);
+ try renderToken(r, lparen, .none); // (
ais.popIndent();
return renderToken(r, lparen + 1, space); // )
}
@@ -2590,10 +2916,7 @@ fn renderParamList(
if (i + 1 < params.len) {
const comma = tree.lastToken(param_node) + 1;
- const next_multiline_string =
- tree.tokenTag(tree.firstToken(params[i + 1])) == .multiline_string_literal_line;
- const comma_space: Space = if (next_multiline_string) .none else .space;
- try renderToken(r, comma, comma_space);
+ try renderToken(r, comma, .maybe_space);
}
}
ais.popIndent();
@@ -2655,6 +2978,13 @@ const Space = enum {
/// Additionally consume the next token if it is a semicolon.
/// In either case, a newline will be inserted afterwards.
semicolon,
+ /// If the next token is not a multiline string literal, this acts as .space,
+ /// otherwise this acts as .none.
+ maybe_space,
+ /// Additionally consume the next token if it is a comma.
+ /// In either case, a space will be inserted afterwards
+ /// if the following token is not a multiline string literal.
+ comma_maybe_space,
/// Skip rendering whitespace and comments. If this is used, the caller
/// *must* handle whitespace and comments manually.
skip,
@@ -2719,6 +3049,16 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
try ais.insertNewline();
},
+ .maybe_space => if (!comment and next_token_tag != .multiline_string_literal_line) {
+ try ais.writeByte(' ');
+ },
+
+ .comma_maybe_space => if (next_token_tag == .comma) {
+ try renderToken(r, token_index + 1, .maybe_space);
+ } else if (!comment) {
+ try ais.writeByte(' ');
+ },
+
.skip => unreachable,
}
}
@@ -2727,9 +3067,9 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {
const ais = r.ais;
switch (space) {
.none => {},
- .space => try ais.writeByte(' '),
+ .space, .maybe_space => try ais.writeByte(' '),
.newline => try ais.insertNewline(),
- .comma => try ais.writeAll(",\n"),
+ .comma, .comma_maybe_space => try ais.writeAll(",\n"),
.comma_space => try ais.writeAll(", "),
.semicolon => try ais.writeAll(";\n"),
.skip => unreachable,
@@ -2794,7 +3134,7 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote
},
.failure => return renderQuotedIdentifier(r, token_index, space, false),
}
- contents_i += esc_offset;
+ contents_i = esc_offset;
continue;
},
else => return renderQuotedIdentifier(r, token_index, space, false),
@@ -2884,6 +3224,9 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void
}
},
.failure => {
+ // Escape the stray backslash
+ // This also avoids cases like "\x3\x39" becoming "\x39"
+ try ais.writeByte('\\');
try ais.writeAll(escape_sequence);
},
}
@@ -2910,7 +3253,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex)
const token: Ast.TokenIndex = @intCast(i);
const start = tree.tokenStart(token) + tree.tokenSlice(token).len;
const end = tree.tokenStart(token + 1);
- if (mem.find(u8, tree.source[start..end], "//") != null) return true;
+ if (mem.findScalar(u8, tree.source[start..end], '/') != null) return true;
}
return false;
@@ -2926,6 +3269,16 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok
) != null;
}
+/// Returns true if there exists a doc comment between the start
+/// of token `start_token` and the start of token `end_token`.
+fn hasDocComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
+ return std.mem.indexOfScalar(
+ Token.Tag,
+ tree.tokens.items(.tag)[start_token..end_token],
+ .doc_comment,
+ ) != null;
+}
+
/// Assumes that start is the first byte past the previous token and
/// that end is the last byte before the next token.
fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
@@ -3095,25 +3448,10 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 {
return ret;
}
-fn writeStringLiteralAsIdentifier(r: *Render, token_index: Ast.TokenIndex) !usize {
- const tree = r.tree;
- const ais = r.ais;
- assert(tree.tokenTag(token_index) == .string_literal);
- const lexeme = tokenSliceForRender(tree, token_index);
- const unquoted = lexeme[1..][0 .. lexeme.len - 2];
- if (std.zig.isValidId(unquoted)) {
- try ais.writeAll(unquoted);
- return unquoted.len;
- } else {
- try ais.writeByte('@');
- try ais.writeAll(lexeme);
- return lexeme.len + 1;
- }
-}
-
-fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {
- const between_source = tree.source[tree.tokenStart(token_index)..tree.tokenStart(token_index + 1)];
- for (between_source) |byte| switch (byte) {
+fn hasTrailingComment(tree: Ast, t: Ast.TokenIndex) bool {
+ const start = tree.tokenStart(t) + tree.tokenSlice(t).len;
+ const between = tree.source[start..tree.tokenStart(t + 1)];
+ for (between) |byte| switch (byte) {
'\n' => return false,
'/' => return true,
else => continue,
@@ -3125,12 +3463,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {
/// start_token and end_token.
fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
if (start_token + 1 != end_token) return true;
- const between_source = tree.source[tree.tokenStart(start_token)..tree.tokenStart(start_token + 1)];
- for (between_source) |byte| switch (byte) {
- '/' => return true,
- else => continue,
- };
- return false;
+ return hasComment(tree, start_token, end_token);
}
fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {
@@ -3217,29 +3550,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
};
}
-// Returns the number of nodes in `exprs` that are on the same line as `rtoken`.
-fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usize {
- const first_token = tree.firstToken(exprs[0]);
- if (tree.tokensOnSameLine(first_token, rtoken)) {
- const maybe_comma = rtoken - 1;
- if (tree.tokenTag(maybe_comma) == .comma)
- return 1;
- return exprs.len; // no newlines
- }
-
- var count: usize = 1;
- for (exprs, 0..) |expr, i| {
- if (i + 1 < exprs.len) {
- const expr_last_token = tree.lastToken(expr) + 1;
- if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count;
- count += 1;
- } else {
- return count;
- }
- }
- unreachable;
-}
-
/// Automatically inserts indentation of written data by keeping
/// track of the current indentation level
///
@@ -3477,11 +3787,19 @@ const AutoIndentingStream = struct {
pub fn popIndent(ais: *AutoIndentingStream) void {
if (ais.indent_stack.pop().?.realized) {
- assert(ais.indent_count > 0);
ais.indent_count -= 1;
}
}
+ /// Forces the last pushed indent to be realized
+ pub fn forceLastIndent(ais: *AutoIndentingStream) void {
+ const top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
+ if (!top.realized) {
+ top.realized = true;
+ ais.indent_count += 1;
+ }
+ }
+
pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {
return ais.indent_stack.items.len == 0;
}
@@ -3489,8 +3807,8 @@ const AutoIndentingStream = struct {
/// Writes ' ' bytes if the current line is empty
fn applyIndent(ais: *AutoIndentingStream) Error!void {
const current_indent = ais.currentIndent();
- if (ais.current_line_empty and current_indent > 0) {
- if (ais.disabled_offset == null) {
+ if (ais.current_line_empty) {
+ if (current_indent > 0 and ais.disabled_offset == null) {
try ais.underlying_writer.splatByteAll(' ', current_indent);
}
ais.applied_indent = current_indent;
diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig
@@ -384,28 +384,32 @@ pub const list = list: {
},
},
.{
- "@cVaArg", .{
+ "@cVaArg",
+ .{
.tag = .c_va_arg,
.param_count = 2,
.illegal_outside_function = true,
},
},
.{
- "@cVaCopy", .{
+ "@cVaCopy",
+ .{
.tag = .c_va_copy,
.param_count = 1,
.illegal_outside_function = true,
},
},
.{
- "@cVaEnd", .{
+ "@cVaEnd",
+ .{
.tag = .c_va_end,
.param_count = 1,
.illegal_outside_function = true,
},
},
.{
- "@cVaStart", .{
+ "@cVaStart",
+ .{
.tag = .c_va_start,
.param_count = 0,
.illegal_outside_function = true,
@@ -1042,7 +1046,8 @@ pub const list = list: {
},
},
.{
- "@workItemId", .{
+ "@workItemId",
+ .{
.tag = .work_item_id,
.param_count = 1,
.illegal_outside_function = true,
diff --git a/lib/std/zig/Parse.zig b/lib/std/zig/Parse.zig
@@ -885,7 +885,8 @@ fn expectContainerField(p: *Parse) !Node.Index {
.tag = .container_field,
.main_token = main_token,
.data = .{ .node_and_extra = .{
- type_expr, try p.addExtra(Node.ContainerField{
+ type_expr,
+ try p.addExtra(Node.ContainerField{
.align_expr = align_expr.?,
.value_expr = value_expr.?,
}),
@@ -919,11 +920,15 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
} else {
const assign = try p.expectAssignExpr();
try p.expectSemicolon(.expected_semi_after_stmt, true);
- return p.addNode(.{
- .tag = .@"comptime",
- .main_token = comptime_token,
- .data = .{ .node = assign },
- });
+ if (p.nodeTag(assign) != .assign_destructure) {
+ return p.addNode(.{
+ .tag = .@"comptime",
+ .main_token = comptime_token,
+ .data = .{ .node = assign },
+ });
+ } else {
+ return assign;
+ }
}
}
@@ -1184,7 +1189,8 @@ fn expectIfStatement(p: *Parse) !Node.Index {
.tag = .@"if",
.main_token = if_token,
.data = .{ .node_and_extra = .{
- condition, try p.addExtra(Node.If{
+ condition,
+ try p.addExtra(Node.If{
.then_expr = then_expr,
.else_expr = else_expr,
}),
@@ -1365,7 +1371,8 @@ fn parseWhileStatement(p: *Parse) !?Node.Index {
.tag = .@"while",
.main_token = while_token,
.data = .{ .node_and_extra = .{
- condition, try p.addExtra(Node.While{
+ condition,
+ try p.addExtra(Node.While{
.cont_expr = .fromOptional(cont_expr),
.then_expr = then_expr,
.else_expr = else_expr,
@@ -1933,7 +1940,8 @@ fn parseTypeExpr(p: *Parse) Error!?Node.Index {
.tag = .array_type_sentinel,
.main_token = lbracket,
.data = .{ .node_and_extra = .{
- len_expr.?, try p.addExtra(Node.ArrayTypeSentinel{
+ len_expr.?,
+ try p.addExtra(Node.ArrayTypeSentinel{
.sentinel = sentinel.?,
.elem_type = elem_type,
}),
@@ -2728,7 +2736,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {
.tag = .while_cont,
.main_token = while_token,
.data = .{ .node_and_extra = .{
- condition, try p.addExtra(Node.WhileCont{
+ condition,
+ try p.addExtra(Node.WhileCont{
.cont_expr = cont_expr.?,
.then_expr = then_expr,
}),
@@ -2742,7 +2751,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {
.tag = .@"while",
.main_token = while_token,
.data = .{ .node_and_extra = .{
- condition, try p.addExtra(Node.While{
+ condition,
+ try p.addExtra(Node.While{
.cont_expr = .fromOptional(cont_expr),
.then_expr = then_expr,
.else_expr = else_expr,
@@ -3194,7 +3204,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {
.tag = .slice_sentinel,
.main_token = lbracket,
.data = .{ .node_and_extra = .{
- lhs, try p.addExtra(Node.SliceSentinel{
+ lhs,
+ try p.addExtra(Node.SliceSentinel{
.start = index_expr,
.end = .fromOptional(opt_end_expr),
.sentinel = sentinel,
@@ -3217,7 +3228,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {
.tag = .slice,
.main_token = lbracket,
.data = .{ .node_and_extra = .{
- lhs, try p.addExtra(Node.Slice{
+ lhs,
+ try p.addExtra(Node.Slice{
.start = index_expr,
.end = end_expr,
}),
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const Io = std.Io;
const Allocator = std.mem.Allocator;
+const Token = std.zig.Token;
test "zig fmt: remove extra whitespace at start and end of file with comment between" {
try testTransform(
@@ -1329,12 +1330,30 @@ test "zig fmt: comment to disable/enable zig fmt" {
\\const c = d;
\\// zig fmt: on
\\const e = f;
+ \\const g = .{
+ \\ h, i,
+ \\ // zig fmt: off
+ \\ j,
+ \\ k,
+ \\ // zig fmt: on
+ \\ l, m, n, o,
+ \\};
+ \\
,
\\const a = b;
\\// zig fmt: off
\\const c = d;
\\// zig fmt: on
\\const e = f;
+ \\const g = .{
+ \\ h, i,
+ \\ // zig fmt: off
+ \\ j,
+ \\ k,
+ \\ // zig fmt: on
+ \\ l, m,
+ \\ n, o,
+ \\};
\\
);
}
@@ -1986,6 +2005,38 @@ test "zig fmt: array literal vertical column alignment" {
\\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
\\const a = [12]u8{
\\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, };
+ \\const a = .{
+ \\ 1, \\
+ \\ , 2,
+ \\ 3,
+ \\};
+ \\const a = .{
+ \\ \\
+ \\ , 1, 2,
+ \\ 3,
+ \\};
+ \\const a = .{
+ \\ {{}}, 1,
+ \\ 2, 3,
+ \\};
+ \\const a = .{
+ \\ a, bb //
+ \\ , ccc, dddd,
+ \\};
+ \\const a = .{
+ \\ "a", "b", "ä", "a", "123",
+ \\};
+ \\const a = .{
+ \\ a, a, .{
+ \\ // zig fmt: off
+ \\ },
+ \\ a*a, a,
+ \\ .{
+ \\ // zig fmt: on
+ \\ }, aa,
+ \\ // zig fmt: off
+ \\ a*a,
+ \\};
\\
,
\\const a = []u8{
@@ -2014,6 +2065,53 @@ test "zig fmt: array literal vertical column alignment" {
\\ 30,
\\ 31,
\\};
+ \\const a = .{
+ \\ 1,
+ \\ \\
+ \\ ,
+ \\ 2,
+ \\ 3,
+ \\};
+ \\const a = .{
+ \\ \\
+ \\ ,
+ \\ 1,
+ \\ 2,
+ \\ 3,
+ \\};
+ \\const a = .{
+ \\ {
+ \\ {}
+ \\ },
+ \\ 1,
+ \\ 2,
+ \\ 3,
+ \\};
+ \\const a = .{
+ \\ a,
+ \\ bb //
+ \\ ,
+ \\ ccc,
+ \\ dddd,
+ \\};
+ \\const a = .{
+ \\ "a", "b",
+ \\ "ä",
+ \\ "a", "123",
+ \\};
+ \\const a = .{
+ \\ a, a,
+ \\ .{
+ \\ // zig fmt: off
+ \\ },
+ \\ a*a, a,
+ \\ .{
+ \\ // zig fmt: on
+ \\ },
+ \\ aa,
+ \\ // zig fmt: off
+ \\ a*a,
+ \\};
\\
);
}
@@ -2542,6 +2640,19 @@ test "zig fmt: first line comment in struct initializer" {
);
}
+test "zig fmt: multiline string literals in struct initializer" {
+ try testTransform(
+ \\const a = .{ .a = \\
+ \\+ 1};
+ \\
+ ,
+ \\const a = .{ .a =
+ \\ \\
+ \\+ 1 };
+ \\
+ );
+}
+
test "zig fmt: doc comments before struct field" {
try testCanonical(
\\pub const Allocator = struct {
@@ -2872,6 +2983,7 @@ test "zig fmt: destructure" {
\\ comptime w, var y = .{ 3, 4 };
\\ comptime var z, x = .{ 5, 6 };
\\ comptime y, z = .{ 7, 8 };
+ \\ if (false) unreachable else comptime a, b = .{ 9, 10 };
\\}
\\
);
@@ -4004,6 +4116,18 @@ test "zig fmt: multiline string in array" {
\\}
\\
);
+
+ try testTransform(
+ \\const a = .{ k, \\
+ \\};
+ \\
+ ,
+ \\const a = .{
+ \\ k,
+ \\ \\
+ \\};
+ \\
+ );
}
test "zig fmt: if type expr" {
@@ -4786,8 +4910,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
\\ 0,
\\ }}}}}}}};
\\ myFunc(.{
- \\ "aaaaaaa", "bbbbbb", "ccccc",
- \\ "dddd", ("eee"), ("fff"),
+ \\ "aaaaaaa", "bbbbbb", "ccccc",
+ \\ "dddd", ("eee"), ("fff"),
\\ ("gggg"),
\\ // Line comment
\\ \\Multiline String Literals can be quite long
@@ -4816,11 +4940,9 @@ test "zig fmt: multiline string literals should play nice with array initializer
\\ (
\\ \\ xxx
\\ ),
- \\ "xxx",
- \\ "xxx",
+ \\ "xxx", "xxx",
\\ },
- \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
- \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
+ \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
\\ "aaaaaaa", "bbbbbb", "ccccc", // -
\\ "dddd", ("eee"), ("fff"),
\\ .{
@@ -4828,8 +4950,7 @@ test "zig fmt: multiline string literals should play nice with array initializer
\\ (
\\ \\ xxx
\\ ),
- \\ "xxxxxxxxxxxxxx",
- \\ "xxx",
+ \\ "xxxxxxxxxxxxxx", "xxx",
\\ },
\\ .{
\\ (
@@ -6028,6 +6149,695 @@ test "zig fmt: extern addrspace in struct" {
);
}
+test "zig fmt: seperate errors in error sets with comments" {
+ try testTransform(
+ \\error{
+ \\ /// This error is very bad!
+ \\ A, B}
+ \\
+ ,
+ \\error{
+ \\ /// This error is very bad!
+ \\ A,
+ \\ B,
+ \\}
+ \\
+ );
+
+ try testTransform(
+ \\error{
+ \\ A, B
+ \\ // something important
+ \\}
+ \\
+ ,
+ \\error{
+ \\ A,
+ \\ B,
+ \\ // something important
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: proper escape checks" {
+ try testTransform(
+ \\@"\x41\x42\!"
+ \\
+ ,
+ \\@"AB\\!"
+ \\
+ );
+}
+
+test "zig fmt: field accesses on number literals" {
+ try testCanonical(
+ \\const a = 0xF .A;
+ \\const a = 0xF
+ \\ .A;
+ \\
+ );
+}
+
+test "zig fmt: array indent when inner becomes multi-line" {
+ try testTransform(
+ \\const access_block = x[{{}}];
+ \\
+ \\const block = [{{}}]T;
+ \\const sentinel_block = [15:{{}}]T;
+ \\const container = [enum { A, }]T;
+ \\const container_arg = [union({{}}) {}]T;
+ \\const error_set = [error{ A, }]T;
+ \\const @"switch" = [switch (m) { 0 => {}, }]T;
+ \\const switch_op = [switch ({{}}) {}]T;
+ \\const for_capture = [for (a) |_,| {}]T;
+ \\const for_expr = [for ({{}}) |_| {}]T;
+ \\const for_range = [for ({{}}..15) |_| {}]T;
+ \\const for_input_comma = [for (0..15,) |_| {}]T;
+ \\const call_param = [a({{}})]T;
+ \\const call_fn = [({{}})()]T;
+ \\const builtin_call = [@log2({{}})]T;
+ \\const array_init = [.{{{}}}]T;
+ \\const struct_init = [.{.x = {{}}}]T;
+ \\const @"asm" = [asm ("" : [x] "" (-> T))]T;
+ \\const asm_template = [asm ({{}})]T;
+ \\const asm_clobbers = [asm ("" ::: {{}})]T;
+ \\const @"fn" = [fn (({{}})) void]T;
+ \\const array_type_len = [[{{}}]T]T;
+ \\const array_type_type = [[1]({{}})]T;
+ \\const array_access_array = [({{}})[1]]T;
+ \\const array_access_index = [x[{{}}]]T;
+ \\const binop = [1 + {{}}]T;
+ \\const unaryop = [!{{}}]T;
+ \\const destructure = [while (true) : (x, {{}} = z) {}]T;
+ \\
+ ,
+ \\const access_block = x[
+ \\ {
+ \\ {}
+ \\ }
+ \\];
+ \\
+ \\const block = [
+ \\ {
+ \\ {}
+ \\ }
+ \\]T;
+ \\const sentinel_block = [
+ \\ 15
+ \\ :
+ \\ {
+ \\ {}
+ \\ }
+ \\]T;
+ \\const container = [
+ \\ enum {
+ \\ A,
+ \\ }
+ \\]T;
+ \\const container_arg = [
+ \\ union({
+ \\ {}
+ \\ }) {}
+ \\]T;
+ \\const error_set = [
+ \\ error{
+ \\ A,
+ \\ }
+ \\]T;
+ \\const @"switch" = [
+ \\ switch (m) {
+ \\ 0 => {},
+ \\ }
+ \\]T;
+ \\const switch_op = [
+ \\ switch ({
+ \\ {}
+ \\ }) {}
+ \\]T;
+ \\const for_capture = [
+ \\ for (a) |
+ \\ _,
+ \\ | {}
+ \\]T;
+ \\const for_expr = [
+ \\ for ({
+ \\ {}
+ \\ }) |_| {}
+ \\]T;
+ \\const for_range = [
+ \\ for ({
+ \\ {}
+ \\ }..15) |_| {}
+ \\]T;
+ \\const for_input_comma = [
+ \\ for (
+ \\ 0..15,
+ \\ ) |_| {}
+ \\]T;
+ \\const call_param = [
+ \\ a({
+ \\ {}
+ \\ })
+ \\]T;
+ \\const call_fn = [
+ \\ ({
+ \\ {}
+ \\ })()
+ \\]T;
+ \\const builtin_call = [
+ \\ @log2({
+ \\ {}
+ \\ })
+ \\]T;
+ \\const array_init = [
+ \\ .{{
+ \\ {}
+ \\ }}
+ \\]T;
+ \\const struct_init = [
+ \\ .{ .x = {
+ \\ {}
+ \\ } }
+ \\]T;
+ \\const @"asm" = [
+ \\ asm (""
+ \\ : [x] "" (-> T),
+ \\ )
+ \\]T;
+ \\const asm_template = [
+ \\ asm ({
+ \\ {}
+ \\ })
+ \\]T;
+ \\const asm_clobbers = [
+ \\ asm ("" ::: {
+ \\ {}
+ \\ })
+ \\]T;
+ \\const @"fn" = [
+ \\ fn (({
+ \\ {}
+ \\ })) void
+ \\]T;
+ \\const array_type_len = [
+ \\ [
+ \\ {
+ \\ {}
+ \\ }
+ \\ ]T
+ \\]T;
+ \\const array_type_type = [
+ \\ [1]({
+ \\ {}
+ \\ })
+ \\]T;
+ \\const array_access_array = [
+ \\ ({
+ \\ {}
+ \\ })[1]
+ \\]T;
+ \\const array_access_index = [
+ \\ x[
+ \\ {
+ \\ {}
+ \\ }
+ \\ ]
+ \\]T;
+ \\const binop = [
+ \\ 1 + {
+ \\ {}
+ \\ }
+ \\]T;
+ \\const unaryop = [
+ \\ !{
+ \\ {}
+ \\ }
+ \\]T;
+ \\const destructure = [
+ \\ while (true) : (x, {
+ \\ {}
+ \\ } = z) {}
+ \\]T;
+ \\
+ );
+
+ try testCanonical(
+ \\const oneline_access_block = x[{}];
+ \\const oneline_block = [{}]T;
+ \\const oneline_sentinel_block = [15:{}]T;
+ \\const oneline_container = [enum { A }]T;
+ \\const oneline_error_set = [error{A}]T;
+ \\const oneline_switch = [switch (m) {}]T;
+ \\const oneline_for = [for (a, 0..15) |_, _| {}]T;
+ \\const oneline_call = [a(a)]T;
+ \\const oneline_builtin_call = [@log2(a)]T;
+ \\const oneline_array_init = [.{a}]T;
+ \\const oneline_struct_init = [.{ .x = a }]T;
+ \\const oneline_asm = [asm ("" ::: .{})]T;
+ \\const onlinee_fn = [fn (usize) void]T;
+ \\const oneline_array_type = [[{}]T]T;
+ \\const oneline_array_access = [x[{}]]T;
+ \\const online_binop = [1 + 1]T;
+ \\const online_unaryop = [!false]T;
+ \\const oneline_destructure = [while (true) : (x, y = z) {}]T;
+ \\
+ );
+
+ try testTransform(
+ \\const a = [{
+ \\}]T;
+ \\
+ \\const b = x[{
+ \\}];
+ \\
+ \\const c = [
+ \\ {
+ \\ }
+ \\]T;
+ \\
+ \\const d = x[
+ \\ {
+ \\ }
+ \\];
+ \\
+ ,
+ \\const a = [
+ \\ {}
+ \\]T;
+ \\
+ \\const b = x[
+ \\ {}
+ \\];
+ \\
+ \\const c = [
+ \\ {}
+ \\]T;
+ \\
+ \\const d = x[
+ \\ {}
+ \\];
+ \\
+ );
+}
+
+test "zig fmt: whitespace with multiline strings" {
+ try testCanonical(
+ \\const a = .{
+ \\ .b =
+ \\ \\
+ \\ ++ "",
+ \\};
+ \\const b = switch (a) {
+ \\ a =>
+ \\ \\
+ \\ ++ "",
+ \\};
+ \\
+ );
+
+ try testTransform(
+ \\test {
+ \\ a = \\
+ \\ ;
+ \\ b = \\
+ \\ ();
+ \\ c = x ++ \\
+ \\ ;
+ \\ d = x catch \\
+ \\ ;
+ \\ comptime \\
+ \\ , \\
+ \\ , \\
+ \\ = \\
+ \\ ;
+ \\ e = if (x) \\
+ \\ else y;
+ \\ f = if (x) y else
+ \\ \\
+ \\ ;
+ \\ comptime \\
+ \\ ;
+ \\ errdefer \\
+ \\ ;
+ \\ try \\
+ \\ ;
+ \\ return \\
+ \\ ;
+ \\ const a = asm (\\
+ \\ ++ "": [a] "" (-> \\
+ \\ ) :: \\
+ \\ );
+ \\ const a2 = asm ("" ::: \\
+ \\ );
+ \\ const b = x[1 + 1 .. \\
+ \\ ];
+ \\}
+ \\/// tuple type
+ \\comptime \\
+ \\,
+ \\a: \\
+ \\align(\\
+ \\)
+ \\= \\
+ \\,
+ \\const A = .{
+ \\ *volatile \\
+ \\ ,
+ \\ *const \\
+ \\ ,
+ \\ *addrspace( \\
+ \\ ) \\
+ \\ ,
+ \\ *align( \\
+ \\ : \\
+ \\ : \\
+ \\ ) \\
+ \\ ,
+ \\ *allowzero \\
+ \\ ,
+ \\ *\\
+ \\ ,
+ \\ **\\
+ \\ ,
+ \\ [*]\\
+ \\ ,
+ \\ [*: \\
+ \\ ]\\
+ \\ ,
+ \\ [*c]\\
+ \\ ,
+ \\ []\\
+ \\ ,
+ \\ [: \\
+ \\ ]\\
+ \\ ,
+ \\ *addrspace(a) align(a) \\
+ \\ ,
+ \\};
+ \\const a = blk: {
+ \\ break \\
+ \\ ;
+ \\ break :blk \\
+ \\ ;
+ \\ continue \\
+ \\ ;
+ \\ continue :blk \\
+ \\ ;
+ \\};
+ \\const b = a(a, \\
+ \\++ "");
+ \\const c = @a(a, \\
+ \\++ "");
+ \\extern fn a(T,\\
+ \\) \\
+ \\;
+ \\extern fn b(a: \\
+ \\) align(a) callconv(a) \\
+ \\;
+ \\const d = switch (a) { \\
+ \\ , 1,
+ \\ \\
+ \\ => {},
+ \\ inline \\
+ \\ => {},
+ \\};
+ \\
+ ,
+ \\test {
+ \\ a =
+ \\ \\
+ \\ ;
+ \\ b =
+ \\ \\
+ \\ ();
+ \\ c = x ++
+ \\ \\
+ \\ ;
+ \\ d = x catch
+ \\ \\
+ \\ ;
+ \\ comptime
+ \\ \\
+ \\ ,
+ \\ \\
+ \\ ,
+ \\ \\
+ \\ =
+ \\ \\
+ \\ ;
+ \\ e = if (x)
+ \\ \\
+ \\ else
+ \\ y;
+ \\ f = if (x) y else
+ \\ \\
+ \\ ;
+ \\ comptime
+ \\ \\
+ \\ ;
+ \\ errdefer
+ \\ \\
+ \\ ;
+ \\ try
+ \\ \\
+ \\ ;
+ \\ return
+ \\ \\
+ \\ ;
+ \\ const a = asm (
+ \\ \\
+ \\ ++ ""
+ \\ : [a] "" (->
+ \\ \\
+ \\ ),
+ \\ :
+ \\ :
+ \\ \\
+ \\ );
+ \\ const a2 = asm ("" :::
+ \\ \\
+ \\ );
+ \\ const b = x[1 + 1 ..
+ \\ \\
+ \\ ];
+ \\}
+ \\/// tuple type
+ \\comptime
+ \\\\
+ \\,
+ \\a:
+ \\\\
+ \\align(
+ \\\\
+ \\) =
+ \\ \\
+ \\,
+ \\const A = .{
+ \\ *volatile
+ \\ \\
+ \\ ,
+ \\ *const
+ \\ \\
+ \\ ,
+ \\ *addrspace(
+ \\ \\
+ \\ )
+ \\ \\
+ \\ ,
+ \\ *align(
+ \\ \\
+ \\ :
+ \\ \\
+ \\ :
+ \\ \\
+ \\ )
+ \\ \\
+ \\ ,
+ \\ *allowzero
+ \\ \\
+ \\ ,
+ \\ *
+ \\ \\
+ \\ ,
+ \\ **
+ \\ \\
+ \\ ,
+ \\ [*]
+ \\ \\
+ \\ ,
+ \\ [*:
+ \\ \\
+ \\ ]
+ \\ \\
+ \\ ,
+ \\ [*c]
+ \\ \\
+ \\ ,
+ \\ []
+ \\ \\
+ \\ ,
+ \\ [:
+ \\ \\
+ \\ ]
+ \\ \\
+ \\ ,
+ \\ *align(a) addrspace(a)
+ \\ \\
+ \\ ,
+ \\};
+ \\const a = blk: {
+ \\ break
+ \\ \\
+ \\ ;
+ \\ break :blk
+ \\ \\
+ \\ ;
+ \\ continue
+ \\ \\
+ \\ ;
+ \\ continue :blk
+ \\ \\
+ \\ ;
+ \\};
+ \\const b = a(a,
+ \\ \\
+ \\++ "");
+ \\const c = @a(a,
+ \\ \\
+ \\++ "");
+ \\extern fn a(T,
+ \\\\
+ \\)
+ \\\\
+ \\;
+ \\extern fn b(a:
+ \\\\
+ \\) align(a) callconv(a)
+ \\\\
+ \\;
+ \\const d = switch (a) {
+ \\ \\
+ \\ , 1,
+ \\ \\
+ \\ => {},
+ \\ inline
+ \\ \\
+ \\ => {},
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: doc comments on fn parameters" {
+ try testTransform(
+ \\extern fn foo(
+ \\ /// Bitmap
+ \\ active: u64
+ \\) void;
+ \\extern fn bar(
+ \\ bits: u6,
+ \\ /// Bitmap
+ \\ active: u64
+ \\) void;
+ \\extern fn baz(
+ \\ /// Bitmap
+ \\ active: anytype
+ \\) void;
+ \\
+ ,
+ \\extern fn foo(
+ \\ /// Bitmap
+ \\ active: u64,
+ \\) void;
+ \\extern fn bar(
+ \\ bits: u6,
+ \\ /// Bitmap
+ \\ active: u64,
+ \\) void;
+ \\extern fn baz(
+ \\ /// Bitmap
+ \\ active: anytype,
+ \\) void;
+ \\
+ );
+ try testCanonical(
+ \\extern fn foo(x: struct {
+ \\ /// Bitmap
+ \\ active: u64,
+ \\}) void;
+ \\
+ );
+}
+
+test "zig fmt: array literal formatting when element becomes multiline" {
+ try testTransform(
+ \\const a = .{a,{{}},
+ \\ b,c,};
+ ,
+ \\const a = .{
+ \\ a,
+ \\ {
+ \\ {}
+ \\ },
+ \\ b,
+ \\ c,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: proper tracking of indentation" {
+ try testCanonical(
+ \\const a = {
+ \\ {}
+ \\};
+ \\const b = if (x) {};
+ \\const c = .{
+ \\ {
+ \\ {}
+ \\ } //
+ \\ ,
+ \\ if (x) {},
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: canonicalize stray backslashes in identifiers" {
+ try testTransform(
+ \\const @"\x" = undefined;
+ \\const @"\x3" = undefined;
+ \\const @"\x3\x39" = undefined;
+ \\const @"\u{" = undefined;
+ \\const @"\?" = undefined;
+ \\
+ ,
+ \\const @"\\x" = undefined;
+ \\const @"\\x3" = undefined;
+ \\const @"\\x39" = undefined;
+ \\const @"\\u{" = undefined;
+ \\const @"\\?" = undefined;
+ \\
+ );
+}
+
+test "zig fmt: error set with extra newline before comma" {
+ try testTransform(
+ \\const E = error{
+ \\ A
+ \\
+ \\ ,
+ \\};
+ \\
+ ,
+ \\const E = error{
+ \\ A,
+ \\};
+ \\
+ );
+}
+
test "recovery: top level" {
try testError(
\\test "" {inline}
@@ -6336,6 +7146,23 @@ test "ampersand" {
, &.{});
}
+test "Ast: pointer types with subexprs containing qualifiers" {
+ var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ const allocator = fixed_allocator.allocator();
+ var tree = try std.zig.Ast.parse(allocator, "**addrspace(*align(1)T)T", .zon);
+ defer tree.deinit(allocator);
+
+ const regular_ptr_node = tree.nodeData(.root).node;
+ const full_regular_ptr = tree.fullPtrType(regular_ptr_node) orelse return error.TestFailed;
+ try std.testing.expect(full_regular_ptr.ast.addrspace_node == .none);
+ try std.testing.expect(full_regular_ptr.ast.align_node == .none);
+
+ const special_ptr_node = full_regular_ptr.ast.child_type;
+ const full_special_ptr = tree.fullPtrType(special_ptr_node) orelse return error.TestFailed;
+ try std.testing.expect(full_special_ptr.ast.addrspace_node != .none);
+ try std.testing.expect(full_special_ptr.ast.align_node == .none);
+}
+
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
fn testParse(io: Io, source: [:0]const u8, allocator: Allocator, anything_changed: *bool) ![]u8 {
@@ -6426,3 +7253,263 @@ fn fuzzTestOneParse(_: void, smith: *std.testing.Smith) !void {
var fba: std.heap.FixedBufferAllocator = .init(&fixed_buffer_mem);
_ = std.zig.Ast.parseTokens(fba.allocator(), tokens.source(), tokens.list(), mode) catch return;
}
+
+test "zig fmt: fuzz" {
+ try std.testing.fuzz({}, fuzzRender, .{});
+}
+
+fn parseTokens(
+ fba: Allocator,
+ source: [:0]const u8,
+) error{ SkipZigTest, OutOfMemory }!struct {
+ toks: std.zig.Ast.TokenList,
+ maybe_rewritable: bool,
+ skip_idempotency: bool,
+} {
+ @disableInstrumentation();
+ // Byte-order marker is stripped
+ var maybe_rewritable = std.mem.startsWith(u8, source, "\xEF\xBB\xBF");
+ var skip_idempotency = false; // This should be able to be removed once all the bugs are fixed
+
+ var tokens: std.zig.Ast.TokenList = .{};
+ try tokens.ensureTotalCapacity(fba, source.len / 2);
+ var tokenizer: std.zig.Tokenizer = .init(source);
+ while (true) {
+ const tok = tokenizer.next();
+ switch (tok.tag) {
+ .invalid,
+ .invalid_periodasterisks,
+ => return error.SkipZigTest,
+ // Extra colons can be removed
+ .keyword_asm,
+ // Qualifiers can be reordered
+ // keyword_const is intentionally excluded since it is used in other contexts and
+ // having only one qualifier will never lead to reordering.
+ .keyword_addrspace,
+ .keyword_align,
+ .keyword_allowzero,
+ .keyword_callconv,
+ .keyword_linksection,
+ .keyword_volatile,
+ => maybe_rewritable = true,
+ .builtin,
+ // Pointer casts can be reordered
+ => for ([_][]const u8{
+ "ptrCast",
+ "alignCast",
+ "addrSpaceCast",
+ "constCast",
+ "volatileCast",
+ }) |id| {
+ if (std.mem.eql(u8, source[tok.loc.start + 1 .. tok.loc.end], id)) {
+ maybe_rewritable = false;
+ }
+ },
+ // Quoted identifiers can be unquoted
+ .identifier => maybe_rewritable = maybe_rewritable or source[tok.loc.start] == '@',
+ else => {},
+ // #23754
+ .container_doc_comment,
+ => if (std.mem.endsWith(Token.Tag, tokens.items(.tag), &.{.l_brace})) {
+ return error.SkipZigTest;
+ },
+ // #24507
+ .keyword_inline,
+ .keyword_for,
+ .keyword_while,
+ .l_brace,
+ => if (std.mem.endsWith(Token.Tag, tokens.items(.tag), &.{ .identifier, .colon })) {
+ maybe_rewritable = true;
+ skip_idempotency = true;
+ },
+ }
+ try tokens.append(fba, .{
+ .tag = tok.tag,
+ .start = @intCast(tok.loc.start),
+ });
+ if (tok.tag == .eof)
+ break;
+ }
+ return .{
+ .toks = tokens,
+ .maybe_rewritable = maybe_rewritable,
+ .skip_idempotency = skip_idempotency,
+ };
+}
+
+/// Checks equivelence of non-whitespace characters.
+/// If there are commas in `source`, then it is checked they are also present
+/// in `rendered`. Extra commas in `rendered` are ignored.
+fn isRewritten(source: [:0]const u8, rendered: [:0]const u8) bool {
+ @disableInstrumentation();
+ var i: usize = 0;
+ for (source[0 .. source.len + 1]) |c| switch (c) {
+ ' ', '\r', '\t', '\n' => {},
+ else => while (true) {
+ defer i += 1;
+ switch (rendered[i]) {
+ ' ', '\n' => {},
+ ',' => if (c == ',') break,
+ else => |r| if (c != r) return false else break,
+ }
+ },
+ };
+ std.debug.assert(i >= rendered.len);
+ return false;
+}
+
+/// Checks that no line ends in whitespace
+fn checkBetweenTokens(src: []const u8, fmt_on: *bool) error{
+ TrailingLineWhitespace,
+ DoubleEmptyLine,
+}!void {
+ @disableInstrumentation();
+ var pos: usize = 0;
+ while (true) {
+ const nl_pos = std.mem.indexOfScalarPos(u8, src, pos, '\n');
+ var check_trailing = fmt_on.*;
+
+ const line = src[pos .. nl_pos orelse src.len];
+ if (std.mem.indexOfScalar(u8, line, '/')) |comment_start| {
+ const comment_content = line[comment_start..][2..];
+ const trimmed_comment = std.mem.trim(u8, comment_content, &std.ascii.whitespace);
+ if (std.mem.eql(u8, trimmed_comment, "zig fmt: off")) {
+ fmt_on.* = false;
+ } else if (std.mem.eql(u8, trimmed_comment, "zig fmt: on")) {
+ fmt_on.* = true;
+ check_trailing = true;
+ }
+ }
+
+ pos = nl_pos orelse break;
+ if (check_trailing and pos != 0) switch (src[pos - 1]) {
+ ' ', '\t', '\r' => return error.TrailingLineWhitespace,
+ '\n' => if (pos != 1 and src[pos - 2] == '\n') return error.DoubleEmptyLine,
+ else => {},
+ };
+ pos += 1;
+ }
+}
+
+/// Ignores extre `.comma` tokens in `rendered`
+fn reparseTokens(
+ fba: Allocator,
+ rendered: [:0]const u8,
+ expected_tags: [:.eof]const Token.Tag,
+) error{
+ OutOfMemory,
+ SameLineMultilineStringLiteral,
+ TrailingLineWhitespace,
+ DoubleEmptyLine,
+}!struct {
+ toks: std.zig.Ast.TokenList,
+ rewritten: bool,
+} {
+ @disableInstrumentation();
+ var rewritten = false;
+ var tokens: std.zig.Ast.TokenList = .{};
+ var last_token_end: usize = 0;
+ var fmt_on = true;
+
+ try tokens.ensureTotalCapacity(fba, expected_tags.len + 2); // 1 for EOF and 1 for maybe a comma
+ var tokenizer: std.zig.Tokenizer = .init(rendered);
+ var i: usize = 0;
+ while (true) {
+ const tok = tokenizer.next();
+ try tokens.append(fba, .{
+ .tag = tok.tag,
+ .start = @intCast(tok.loc.start),
+ });
+
+ const between = rendered[last_token_end..tok.loc.start];
+ last_token_end = tok.loc.end;
+ try checkBetweenTokens(between, &fmt_on);
+ if (tok.tag == .multiline_string_literal_line and fmt_on) blk: {
+ if (tokens.len == 1)
+ break :blk; // first token
+ if (std.mem.indexOfScalar(u8, between, '\n') == null)
+ return error.SameLineMultilineStringLiteral;
+ }
+ if (tok.tag == expected_tags[i]) {
+ if (tok.tag == .eof)
+ break;
+ i += 1;
+ } else if (tok.tag != .comma or !fmt_on) {
+ rewritten = true;
+ }
+ }
+ std.debug.assert(i == expected_tags.len);
+ try checkBetweenTokens(rendered[last_token_end..], &fmt_on);
+
+ return .{ .toks = tokens, .rewritten = rewritten };
+}
+
+fn fuzzRender(_: void, smith: *std.testing.Smith) !void {
+ @disableInstrumentation();
+
+ var src_buf: [512]u8 = undefined;
+ const src_len = smith.sliceWeighted(&src_buf, &.{
+ .rangeLessThan(u32, 0, 32, 256),
+ .rangeLessThan(u32, 32, 64, 64),
+ .rangeLessThan(u32, 64, src_buf.len, 1),
+ }, &.{
+ .rangeAtMost(u8, 0x20, 0x7e, 8),
+ .value(u8, '\n', 32),
+ .value(u8, '\t', 8),
+ .value(u8, '\r', 4),
+ .rangeAtMost(u8, 0x7f, 0xff, 1),
+ });
+ src_buf[src_len] = 0;
+
+ var fba_ctx = std.heap.FixedBufferAllocator.init(&fixed_buffer_mem);
+ fuzzRenderInner(src_buf[0..src_len :0], fba_ctx.allocator()) catch |e| return switch (e) {
+ error.OutOfMemory => {},
+ else => e,
+ };
+}
+
+fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void {
+ @disableInstrumentation();
+
+ const src_toks = try parseTokens(fba, source);
+ const src_tree = try std.zig.Ast.parseTokens(fba, source, src_toks.toks.slice(), .zig);
+ if (src_tree.errors.len != 0)
+ return;
+ for (src_tree.nodes.items(.tag)) |tag| switch (tag) {
+ // #24507 (`switch(x) { inline for (a) |a| a => {} }` to
+ // `switch(x) { { inline for (a) |a| a => {} }` since
+ // AST determines inline case token as one before the case expression's first)
+ .switch_case_inline, .switch_case_inline_one => return error.SkipZigTest,
+ else => {},
+ };
+
+ var rendered_w: std.Io.Writer.Allocating = .init(fba);
+ try rendered_w.ensureUnusedCapacity(source.len + source.len / 2);
+ try src_tree.render(fba, &rendered_w.writer, .{});
+ // `toOwnedSliceSentinel` is not used since it reallocates the entire
+ // list to save space which is useless for fixed buffer allocators.
+ try rendered_w.writer.writeByte(0);
+ const rendered = rendered_w.written()[0 .. rendered_w.written().len - 1 :0];
+
+ // First check that the non-whitespace characters match. This ensures that
+ // identifier names, numbers, comments, et cetera are preserved.
+ if (!src_toks.maybe_rewritable and isRewritten(source, rendered))
+ return error.Rewritten;
+ // Next check that the tokens are the same since whitespace removal can change the tokens
+ const src_tags = src_toks.toks.items(.tag);
+ const rendered_toks = try reparseTokens(fba, rendered, src_tags[0 .. src_tags.len - 1 :.eof]);
+ if (!src_toks.maybe_rewritable and rendered_toks.rewritten)
+ return error.Rewritten;
+
+ // Rerender the tree to check idempotency and that new commas
+ // and whitespace changes did not create an AST error.
+ const rendered_tree = try std.zig.Ast.parseTokens(fba, rendered, rendered_toks.toks.slice(), .zig);
+ if (rendered_tree.errors.len != 0)
+ return error.Rewritten;
+ if (!src_toks.skip_idempotency) {
+ var rerendered_w: std.Io.Writer.Allocating = .init(fba);
+ try rerendered_w.ensureUnusedCapacity(source.len);
+ try rendered_tree.render(fba, &rerendered_w.writer, .{});
+ try std.testing.expectEqualStrings(rendered, rerendered_w.written());
+ }
+}
diff --git a/src/codegen/sparc64/abi.zig b/src/codegen/sparc64/abi.zig
@@ -38,10 +38,10 @@ const allocatable_regs = [_]Register{
};
pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };
-pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };
+pub const c_abi_int_param_regs_callee_view = [_]Register{ .i0, .i1, .i2, .i3, .i4, .i5 };
pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };
-pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" };
+pub const c_abi_int_return_regs_callee_view = [_]Register{ .i0, .i1, .i2, .i3 };
pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
diff --git a/src/link.zig b/src/link.zig
@@ -2097,7 +2097,8 @@ fn resolveLibInput(
const test_path: Path = .{
.root_dir = lib_directory,
.sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
- target.libPrefix(), lib_name, switch (link_mode) {
+ target.libPrefix(), lib_name,
+ switch (link_mode) {
.static => target.staticLibSuffix(),
.dynamic => target.dynamicLibSuffix(),
},