authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-13 08:43:19+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-13 08:43:19+01:00
logb15aa0540d03c92c7b822fbebc373bf4e76e3425
tree17525065b9b313ee3a0bd5f02de5bd18079eed4d
parent2c61deaae26ee492fa5a7b68726801c9414305a5
parent02e8339ca78e0f701b84da0bc31201a7fe95cec4

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>

11 files changed, 2071 insertions(+), 640 deletions(-)

lib/compiler/aro/aro/Compilation.zig+2-1
...@@ -1707,7 +1707,8 @@ pub fn initSearchPath(comp: *Compilation, includes: []const Include, verbose: bo...@@ -1707,7 +1707,8 @@ pub fn initSearchPath(comp: *Compilation, includes: []const Include, verbose: bo
1707 std.debug.print("#include <...> search starts here:\n", .{});1707 std.debug.print("#include <...> search starts here:\n", .{});
1708 }1708 }
1709 std.debug.print(" {s}{s}\n", .{1709 std.debug.print(" {s}{s}\n", .{
1710 include.path, if (include.kind.isFramework())1710 include.path,
1711 if (include.kind.isFramework())
1711 " (framework directory)"1712 " (framework directory)"
1712 else1713 else
1713 "",1714 "",
lib/compiler/translate-c/ast.zig+4-2
...@@ -1213,7 +1213,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1213,7 +1213,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1213 .tag = .slice,1213 .tag = .slice,
1214 .main_token = l_bracket,1214 .main_token = l_bracket,
1215 .data = .{ .node_and_extra = .{1215 .data = .{ .node_and_extra = .{
1216 string, try c.addExtra(std.zig.Ast.Node.Slice{1216 string,
1217 try c.addExtra(std.zig.Ast.Node.Slice{
1217 .start = start,1218 .start = start,
1218 .end = end,1219 .end = end,
1219 }),1220 }),
...@@ -2720,7 +2721,8 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {...@@ -2720,7 +2721,8 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
2720 .tag = .call,2721 .tag = .call,
2721 .main_token = lparen,2722 .main_token = lparen,
2722 .data = .{ .node_and_extra = .{2723 .data = .{ .node_and_extra = .{
2723 lhs, try c.addExtra(NodeSubRange{2724 lhs,
2725 try c.addExtra(NodeSubRange{
2724 .start = span.start,2726 .start = span.start,
2725 .end = span.end,2727 .end = span.end,
2726 }),2728 }),
lib/std/crypto/aes.zig+1-2
...@@ -8,8 +8,7 @@ const has_armaes = builtin.cpu.has(.aarch64, .aes);...@@ -8,8 +8,7 @@ const has_armaes = builtin.cpu.has(.aarch64, .aes);
8// C backend doesn't currently support passing vectors to inline asm.8// C backend doesn't currently support passing vectors to inline asm.
9const impl = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c and has_aesni and has_avx) impl: {9const impl = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c and has_aesni and has_avx) impl: {
10 break :impl @import("aes/aesni.zig");10 break :impl @import("aes/aesni.zig");
11} else if (builtin.cpu.arch == .aarch64 and builtin.zig_backend != .stage2_c and has_armaes)11} else if (builtin.cpu.arch == .aarch64 and builtin.zig_backend != .stage2_c and has_armaes) impl: {
12impl: {
13 break :impl @import("aes/armcrypto.zig");12 break :impl @import("aes/armcrypto.zig");
14} else impl: {13} else impl: {
15 break :impl @import("aes/soft.zig");14 break :impl @import("aes/soft.zig");
lib/std/unicode.zig+20-16
...@@ -1245,14 +1245,16 @@ test utf8ToUtf16Le {...@@ -1245,14 +1245,16 @@ test utf8ToUtf16Le {
1245 const length = try utf8ToUtf16Le(utf16le[0..], "This string has been designed to test the vectorized implementat" ++1245 const length = try utf8ToUtf16Le(utf16le[0..], "This string has been designed to test the vectorized implementat" ++
1246 "ion by beginning with one hundred twenty-seven ASCII characters¡");1246 "ion by beginning with one hundred twenty-seven ASCII characters¡");
1247 try testing.expectEqualSlices(u8, &.{1247 try testing.expectEqualSlices(u8, &.{
1248 '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,1248 '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,
1249 '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,1249 '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,
1250 ' ', 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,1250 ' ', 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,
1251 '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,1251 '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,
1252 '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,1252 '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,
1253 ' ', 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,1253 ' ', 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,
1254 '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,1254 '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,
1255 '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,1255 '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,
1256 '¡',
1257 0,
1256 }, mem.sliceAsBytes(utf16le[0..length]));1258 }, mem.sliceAsBytes(utf16le[0..length]));
1257 }1259 }
1258}1260}
...@@ -1317,14 +1319,16 @@ test utf8ToUtf16LeAllocZ {...@@ -1317,14 +1319,16 @@ test utf8ToUtf16LeAllocZ {
1317 "ion by beginning with one hundred twenty-seven ASCII characters¡");1319 "ion by beginning with one hundred twenty-seven ASCII characters¡");
1318 defer testing.allocator.free(utf16);1320 defer testing.allocator.free(utf16);
1319 try testing.expectEqualSlices(u8, &.{1321 try testing.expectEqualSlices(u8, &.{
1320 '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,1322 '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,
1321 '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,1323 '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,
1322 ' ', 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,1324 ' ', 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,
1323 '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,1325 '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,
1324 '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,1326 '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,
1325 ' ', 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,1327 ' ', 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,
1326 '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,1328 '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,
1327 '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,1329 '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,
1330 '¡',
1331 0,
1328 }, mem.sliceAsBytes(utf16));1332 }, mem.sliceAsBytes(utf16));
1329 }1333 }
1330}1334}
lib/std/zig/Ast.zig+10-8
...@@ -2136,10 +2136,12 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType...@@ -2136,10 +2136,12 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType
2136 // here while looking for modifiers as that could result in false2136 // here while looking for modifiers as that could result in false
2137 // positives. Therefore, start after a sentinel if there is one and2137 // positives. Therefore, start after a sentinel if there is one and
2138 // skip over any align node and bit range nodes.2138 // skip over any align node and bit range nodes.
2139 var i = if (info.sentinel.unwrap()) |sentinel| tree.lastToken(sentinel) + 1 else switch (size) {2139 var i = (if (info.sentinel.unwrap()) |sentinel| tree.lastToken(sentinel) + 1 else switch (size) {
2140 .many, .c => info.main_token + 1,2140 .one => info.main_token,
2141 else => info.main_token,2141 .slice => info.main_token + 1,
2142 };2142 .many => info.main_token + 2,
2143 .c => info.main_token + 3,
2144 }) + 1;
2143 const end = tree.firstToken(info.child_type);2145 const end = tree.firstToken(info.child_type);
2144 while (i < end) : (i += 1) {2146 while (i < end) : (i += 1) {
2145 switch (tree.tokenTag(i)) {2147 switch (tree.tokenTag(i)) {
...@@ -2147,15 +2149,15 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType...@@ -2147,15 +2149,15 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType
2147 .keyword_const => result.const_token = i,2149 .keyword_const => result.const_token = i,
2148 .keyword_volatile => result.volatile_token = i,2150 .keyword_volatile => result.volatile_token = i,
2149 .keyword_align => {2151 .keyword_align => {
2150 const align_node = info.align_node.unwrap().?;
2151 if (info.bit_range_end.unwrap()) |bit_range_end| {2152 if (info.bit_range_end.unwrap()) |bit_range_end| {
2152 assert(info.bit_range_start != .none);2153 assert(info.bit_range_start != .none);
2153 i = tree.lastToken(bit_range_end) + 1;2154 i = tree.lastToken(bit_range_end) + 1;
2154 } else {2155 } else if (info.align_node.unwrap()) |align_node| {
2155 i = tree.lastToken(align_node) + 1;2156 i = tree.lastToken(align_node) + 1;
2156 }2157 } else unreachable;
2157 },2158 },
2158 else => {},2159 .keyword_addrspace => i = tree.lastToken(info.addrspace_node.unwrap().?) + 1,
2160 else => unreachable,
2159 }2161 }
2160 }2162 }
2161 return result;2163 return result;
lib/std/zig/Ast/Render.zig+900-582
...@@ -346,20 +346,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -346,20 +346,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
346 const next_token_tag = tree.tokenTag(next_token);346 const next_token_tag = tree.tokenTag(next_token);
347347
348 // dedent the next thing that comes after a multiline string literal348 // dedent the next thing that comes after a multiline string literal
349 if (!ais.indentStackEmpty() and349 if (next_token_tag != .colon and
350 next_token_tag != .colon and350 !ais.indentStackEmpty() and
351 ((next_token_tag != .semicolon and next_token_tag != .comma) or351 ais.lastSpaceModeIndent() < ais.currentIndent())
352 ais.lastSpaceModeIndent() < ais.currentIndent()))
353 {352 {
354 ais.popIndent();353 const indent_top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
355 try ais.pushIndent(.normal);354 if (indent_top.realized) {
355 indent_top.realized = false;
356 ais.indent_count -= 1;
357 }
356 }358 }
357359
358 switch (space) {360 switch (space) {
359 .none, .space, .newline, .skip => {},361 .none, .space, .newline, .maybe_space, .skip => {},
360 .semicolon => if (next_token_tag == .semicolon) try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),362 .semicolon => if (next_token_tag == .semicolon)
361 .comma => if (next_token_tag == .comma) try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),363 try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
362 .comma_space => if (next_token_tag == .comma) try renderToken(r, next_token, .space),364 .comma => if (next_token_tag == .comma)
365 try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
366 .comma_space => if (next_token_tag == .comma)
367 try renderToken(r, next_token, .space),
368 .comma_maybe_space => if (next_token_tag == .comma)
369 try renderToken(r, next_token, .maybe_space),
363 }370 }
364 },371 },
365372
...@@ -384,11 +391,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -384,11 +391,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
384 const defer_token = tree.nodeMainToken(node);391 const defer_token = tree.nodeMainToken(node);
385 const maybe_payload_token, const expr = tree.nodeData(node).opt_token_and_node;392 const maybe_payload_token, const expr = tree.nodeData(node).opt_token_and_node;
386393
387 try renderToken(r, defer_token, .space);394 try renderToken(r, defer_token, .maybe_space);
388 if (maybe_payload_token.unwrap()) |payload_token| {395 if (maybe_payload_token.unwrap()) |payload_token| {
389 try renderToken(r, payload_token - 1, .none); // |396 try renderToken(r, payload_token - 1, .none); // |
390 try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier397 try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier
391 try renderToken(r, payload_token + 1, .space); // |398 try renderToken(r, payload_token + 1, .maybe_space); // |
392 }399 }
393 return renderExpression(r, expr, space);400 return renderExpression(r, expr, space);
394 },401 },
...@@ -400,7 +407,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -400,7 +407,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
400 => {407 => {
401 const main_token = tree.nodeMainToken(node);408 const main_token = tree.nodeMainToken(node);
402 const item = tree.nodeData(node).node;409 const item = tree.nodeData(node).node;
403 try renderToken(r, main_token, .space);410 try renderToken(r, main_token, .maybe_space);
404 return renderExpression(r, item, space);411 return renderExpression(r, item, space);
405 },412 },
406413
...@@ -409,8 +416,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -409,8 +416,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
409 const lhs, const rhs = tree.nodeData(node).node_and_node;416 const lhs, const rhs = tree.nodeData(node).node_and_node;
410 const fallback_first = tree.firstToken(rhs);417 const fallback_first = tree.firstToken(rhs);
411418
412 const same_line = tree.tokensOnSameLine(main_token, fallback_first);419 const seperate_line = !tree.tokensOnSameLine(main_token, fallback_first) or
413 const after_op_space = if (same_line) Space.space else Space.newline;420 tree.tokenTag(fallback_first) == .multiline_string_literal_line;
421 const after_op_space: Space = if (seperate_line) .newline else .space;
414422
415 try renderExpression(r, lhs, .space); // target423 try renderExpression(r, lhs, .space); // target
416424
...@@ -433,13 +441,18 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -433,13 +441,18 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
433 const dot_token = name_token - 1;441 const dot_token = name_token - 1;
434442
435 try ais.pushIndent(.field_access);443 try ais.pushIndent(.field_access);
436 try renderExpression(r, lhs, .none);
437444
438 // Allow a line break between the lhs and the dot if the lhs and rhs
439 // are on different lines.
440 const lhs_last_token = tree.lastToken(lhs);445 const lhs_last_token = tree.lastToken(lhs);
441 const same_line = tree.tokensOnSameLine(lhs_last_token, name_token);446 const same_line = tree.tokensOnSameLine(lhs_last_token, name_token);
442 if (!same_line and !hasComment(tree, lhs_last_token, dot_token)) try ais.insertNewline();447 // Keeping a space after the number ensures it will not turn into a decimal number
448 // (e.g. "0xF .A").
449 const number_space: Space = if (tree.tokenTag(lhs_last_token) == .number_literal and same_line) .space else .none;
450 try renderExpression(r, lhs, number_space);
451
452 // Allow a line break between the lhs and the dot if the lhs and rhs
453 // are on different lines.
454 if (!same_line and !hasComment(tree, lhs_last_token, dot_token))
455 try ais.insertNewline();
443456
444 try renderToken(r, dot_token, .none);457 try renderToken(r, dot_token, .none);
445458
...@@ -489,11 +502,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -489,11 +502,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
489 try renderExpression(r, lhs, .space);502 try renderExpression(r, lhs, .space);
490 const op_token = tree.nodeMainToken(node);503 const op_token = tree.nodeMainToken(node);
491 try ais.pushIndent(.after_equals);504 try ais.pushIndent(.after_equals);
492 if (tree.tokensOnSameLine(op_token, op_token + 1)) {505 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
493 try renderToken(r, op_token, .space);506 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
494 } else {507 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
495 try renderToken(r, op_token, .newline);
496 }
497 try renderExpression(r, rhs, space);508 try renderExpression(r, rhs, space);
498 ais.popIndent();509 ais.popIndent();
499 },510 },
...@@ -532,11 +543,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -532,11 +543,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
532 try renderExpression(r, lhs, .space);543 try renderExpression(r, lhs, .space);
533 const op_token = tree.nodeMainToken(node);544 const op_token = tree.nodeMainToken(node);
534 try ais.pushIndent(.binop);545 try ais.pushIndent(.binop);
535 if (tree.tokensOnSameLine(op_token, op_token + 1)) {546 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
536 try renderToken(r, op_token, .space);547 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
537 } else {548 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
538 try renderToken(r, op_token, .newline);
539 }
540 try renderExpression(r, rhs, space);549 try renderExpression(r, rhs, space);
541 ais.popIndent();550 ais.popIndent();
542 },551 },
...@@ -544,11 +553,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -544,11 +553,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
544 .assign_destructure => {553 .assign_destructure => {
545 const full = tree.assignDestructure(node);554 const full = tree.assignDestructure(node);
546 if (full.comptime_token) |comptime_token| {555 if (full.comptime_token) |comptime_token| {
547 try renderToken(r, comptime_token, .space);556 try renderToken(r, comptime_token, .maybe_space);
548 }557 }
549558
550 for (full.ast.variables, 0..) |variable_node, i| {559 for (full.ast.variables, 0..) |variable_node, i| {
551 const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;560 const variable_space: Space = if (i == full.ast.variables.len - 1) .maybe_space else .comma_maybe_space;
552 switch (tree.nodeTag(variable_node)) {561 switch (tree.nodeTag(variable_node)) {
553 .global_var_decl,562 .global_var_decl,
554 .local_var_decl,563 .local_var_decl,
...@@ -561,11 +570,10 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -561,11 +570,10 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
561 }570 }
562 }571 }
563 try ais.pushIndent(.after_equals);572 try ais.pushIndent(.after_equals);
564 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {573 const expr_seperate_line =
565 try renderToken(r, full.ast.equal_token, .space);574 !tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1) or
566 } else {575 tree.tokenTag(full.ast.equal_token + 1) == .multiline_string_literal_line;
567 try renderToken(r, full.ast.equal_token, .newline);576 try renderToken(r, full.ast.equal_token, if (expr_seperate_line) .newline else .space);
568 }
569 try renderExpression(r, full.ast.value_expr, space);577 try renderExpression(r, full.ast.value_expr, space);
570 ais.popIndent();578 ais.popIndent();
571 },579 },
...@@ -584,7 +592,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -584,7 +592,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
584 .@"try",592 .@"try",
585 .@"resume",593 .@"resume",
586 => {594 => {
587 try renderToken(r, tree.nodeMainToken(node), .space);595 try renderToken(r, tree.nodeMainToken(node), .maybe_space);
588 return renderExpression(r, tree.nodeData(node).node, space);596 return renderExpression(r, tree.nodeData(node).node, space);
589 },597 },
590598
...@@ -637,7 +645,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -637,7 +645,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
637 const lhs, const rhs = tree.nodeData(node).node_and_node;645 const lhs, const rhs = tree.nodeData(node).node_and_node;
638 const lbracket = tree.firstToken(rhs) - 1;646 const lbracket = tree.firstToken(rhs) - 1;
639 const rbracket = tree.lastToken(rhs) + 1;647 const rbracket = tree.lastToken(rhs) + 1;
640 const one_line = tree.tokensOnSameLine(lbracket, rbracket);648 const one_line = tree.tokensOnSameLine(lbracket, rbracket) and
649 !becomesMultilineExpr(tree, rhs);
641 const inner_space = if (one_line) Space.none else Space.newline;650 const inner_space = if (one_line) Space.none else Space.newline;
642 try renderExpression(r, lhs, .none);651 try renderExpression(r, lhs, .none);
643 try ais.pushIndent(.normal);652 try ais.pushIndent(.normal);
...@@ -668,30 +677,23 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -668,30 +677,23 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
668 .@"break", .@"continue" => {677 .@"break", .@"continue" => {
669 const main_token = tree.nodeMainToken(node);678 const main_token = tree.nodeMainToken(node);
670 const opt_label_token, const opt_target = tree.nodeData(node).opt_token_and_opt_node;679 const opt_label_token, const opt_target = tree.nodeData(node).opt_token_and_opt_node;
671 if (opt_label_token == .none and opt_target == .none) {680
672 try renderToken(r, main_token, space); // break/continue681 const before_target_space: Space = if (opt_target != .none) .maybe_space else space;
673 } else if (opt_label_token == .none and opt_target != .none) {682 const before_label_space: Space = if (opt_label_token != .none) .space else before_target_space;
674 const target = opt_target.unwrap().?;683
675 try renderToken(r, main_token, .space); // break/continue684 try renderToken(r, main_token, before_label_space);
676 try renderExpression(r, target, space);685 if (opt_label_token.unwrap()) |label_token| {
677 } else if (opt_label_token != .none and opt_target == .none) {
678 const label_token = opt_label_token.unwrap().?;
679 try renderToken(r, main_token, .space); // break/continue
680 try renderToken(r, label_token - 1, .none); // :
681 try renderIdentifier(r, label_token, space, .eagerly_unquote); // identifier
682 } else if (opt_label_token != .none and opt_target != .none) {
683 const label_token = opt_label_token.unwrap().?;
684 const target = opt_target.unwrap().?;
685 try renderToken(r, main_token, .space); // break/continue
686 try renderToken(r, label_token - 1, .none); // :686 try renderToken(r, label_token - 1, .none); // :
687 try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier687 try renderIdentifier(r, label_token, before_target_space, .eagerly_unquote); // identifier
688 }
689 if (opt_target.unwrap()) |target| {
688 try renderExpression(r, target, space);690 try renderExpression(r, target, space);
689 } else unreachable;691 }
690 },692 },
691693
692 .@"return" => {694 .@"return" => {
693 if (tree.nodeData(node).opt_node.unwrap()) |expr| {695 if (tree.nodeData(node).opt_node.unwrap()) |expr| {
694 try renderToken(r, tree.nodeMainToken(node), .space);696 try renderToken(r, tree.nodeMainToken(node), .maybe_space);
695 try renderExpression(r, expr, space);697 try renderExpression(r, expr, space);
696 } else {698 } else {
697 try renderToken(r, tree.nodeMainToken(node), space);699 try renderToken(r, tree.nodeMainToken(node), space);
...@@ -740,33 +742,36 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -740,33 +742,36 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
740 try renderToken(r, lbrace, .none);742 try renderToken(r, lbrace, .none);
741 try renderIdentifier(r, lbrace + 1, .none, .eagerly_unquote); // identifier743 try renderIdentifier(r, lbrace + 1, .none, .eagerly_unquote); // identifier
742 return renderToken(r, rbrace, space);744 return renderToken(r, rbrace, space);
743 } else if (tree.tokenTag(rbrace - 1) == .comma) {745 } else if (!isOneLineErrorSetDecl(tree, lbrace, rbrace)) {
744 // There is a trailing comma so render each member on a new line.746 // Render each member on a new line.
745 try ais.pushIndent(.normal);747 try ais.pushIndent(.normal);
746 try renderToken(r, lbrace, .newline);748 try renderToken(r, lbrace, .newline);
747 var i = lbrace + 1;749 var i = lbrace + 1;
748 while (i < rbrace) : (i += 1) {750 while (i < rbrace) : (i += 1) {
751 const tag = tree.tokenTag(i);
752 if (tag == .comma) {
753 assert(tree.tokenTag(i - 1) == .identifier);
754 continue;
755 }
749 if (i > lbrace + 1) try renderExtraNewlineToken(r, i);756 if (i > lbrace + 1) try renderExtraNewlineToken(r, i);
750 switch (tree.tokenTag(i)) {757 switch (tag) {
751 .doc_comment => try renderToken(r, i, .newline),758 .doc_comment => try renderToken(r, i, .newline),
752 .identifier => {759 .identifier => {
753 try ais.pushSpace(.comma);760 try ais.pushSpace(.comma);
754 try renderIdentifier(r, i, .comma, .eagerly_unquote);761 try renderIdentifier(r, i, .comma, .eagerly_unquote);
755 ais.popSpace();762 ais.popSpace();
756 },763 },
757 .comma => {},
758 else => unreachable,764 else => unreachable,
759 }765 }
760 }766 }
761 ais.popIndent();767 ais.popIndent();
762 return renderToken(r, rbrace, space);768 return renderToken(r, rbrace, space);
763 } else {769 } else {
764 // There is no trailing comma so render everything on one line.770 // Render each member on one line.
765 try renderToken(r, lbrace, .space);771 try renderToken(r, lbrace, .space);
766 var i = lbrace + 1;772 var i = lbrace + 1;
767 while (i < rbrace) : (i += 1) {773 while (i < rbrace) : (i += 1) {
768 switch (tree.tokenTag(i)) {774 switch (tree.tokenTag(i)) {
769 .doc_comment => unreachable, // TODO
770 .identifier => try renderIdentifier(r, i, .comma_space, .eagerly_unquote),775 .identifier => try renderIdentifier(r, i, .comma_space, .eagerly_unquote),
771 .comma => {},776 .comma => {},
772 else => unreachable,777 else => unreachable,
...@@ -926,6 +931,382 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v...@@ -926,6 +931,382 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v
926 }931 }
927}932}
928933
934/// Same as becomesMultilineExpr, but returns false when `node == .none`
935fn optBecomesMultilineExpr(tree: Ast, node: Ast.Node.OptionalIndex) bool {
936 return if (node.unwrap()) |payload| becomesMultilineExpr(tree, payload) else false;
937}
938
939/// May return false if `node` is already multiline
940fn becomesMultilineExpr(tree: Ast, node: Ast.Node.Index) bool {
941 // Conditions related to comments, doc comments, and multiline string literals are ignored
942 // since they always go to the end of the line, which already make them a multi-line
943 // expression (since they contain a newline).
944 switch (tree.nodeTag(node)) {
945 .identifier,
946 .number_literal,
947 .char_literal,
948 .unreachable_literal,
949 .anyframe_literal,
950 .string_literal,
951 .multiline_string_literal,
952 .error_value,
953 .enum_literal,
954 => return false,
955 .container_decl_trailing,
956 .container_decl_arg_trailing,
957 .container_decl_two_trailing,
958 .tagged_union_trailing,
959 .tagged_union_enum_tag_trailing,
960 .tagged_union_two_trailing,
961 .switch_comma,
962 .builtin_call_two_comma,
963 .builtin_call_comma,
964 .call_one_comma,
965 .call_comma,
966 .struct_init_one_comma,
967 .struct_init_dot_two_comma,
968 .struct_init_dot_comma,
969 .struct_init_comma,
970 .array_init_one_comma,
971 .array_init_dot_two_comma,
972 .array_init_dot_comma,
973 .array_init_comma,
974 // The following always have a non-zero amount of members
975 // which is also the condition for them to be multi-line.
976 .block,
977 .block_semicolon,
978 => return true,
979 .block_two,
980 .block_two_semicolon,
981 => return tree.nodeData(node).opt_node_and_opt_node[0] != .none,
982 .container_decl,
983 .container_decl_arg,
984 .container_decl_two,
985 .tagged_union,
986 .tagged_union_enum_tag,
987 .tagged_union_two,
988 => {
989 var buf: [2]Ast.Node.Index = undefined;
990 const full = tree.fullContainerDecl(&buf, node).?;
991 if (full.ast.arg.unwrap()) |arg| {
992 if (becomesMultilineExpr(tree, arg))
993 return true;
994 }
995 // This does the same checks as `isOneLineContainerDecl`, however it avoids unnecessary
996 // checks related to comments and multiline strings, which would mean the container is
997 // already multiple lines.
998 for (full.ast.members) |member| {
999 if (tree.fullContainerField(member)) |field_full| {
1000 for ([_]Ast.Node.OptionalIndex{
1001 field_full.ast.type_expr,
1002 field_full.ast.align_expr,
1003 field_full.ast.value_expr,
1004 }) |opt_expr| {
1005 if (opt_expr.unwrap()) |expr| {
1006 if (becomesMultilineExpr(tree, expr))
1007 return true;
1008 }
1009 }
1010 } else return true;
1011 }
1012 return false;
1013 },
1014 .error_set_decl => {
1015 const lbrace, const rbrace = tree.nodeData(node).token_and_token;
1016 return !isOneLineErrorSetDecl(tree, lbrace, rbrace);
1017 },
1018 .@"switch" => {
1019 const op, const extra_index = tree.nodeData(node).node_and_extra;
1020 const case_range = tree.extraData(extra_index, Ast.Node.SubRange);
1021 return @intFromEnum(case_range.end) - @intFromEnum(case_range.start) != 0 or
1022 becomesMultilineExpr(tree, op);
1023 },
1024 .for_simple, .@"for" => {
1025 const full = tree.fullFor(node).?;
1026 if (becomesMultilineExpr(tree, full.ast.then_expr) or
1027 optBecomesMultilineExpr(tree, full.ast.else_expr))
1028 return true;
1029
1030 for (full.ast.inputs) |expr| {
1031 if (if (tree.nodeTag(expr) == .for_range) blk: {
1032 const lhs, const rhs = tree.nodeData(expr).node_and_opt_node;
1033 break :blk becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, rhs);
1034 } else becomesMultilineExpr(tree, expr))
1035 return true;
1036 }
1037 const final_input_expr = full.ast.inputs[full.ast.inputs.len - 1];
1038 if (tree.tokenTag(tree.lastToken(final_input_expr) + 1) == .comma)
1039 return true;
1040
1041 const token_tags = tree.tokens.items(.tag);
1042 const payload = full.payload_token;
1043 const pipe = std.mem.indexOfScalarPos(Token.Tag, token_tags, payload, .pipe).?;
1044 return token_tags[@intCast(pipe - 1)] == .comma;
1045 },
1046 .while_simple,
1047 .while_cont,
1048 .@"while",
1049 => {
1050 const full = tree.fullWhile(node).?;
1051 return becomesMultilineExpr(tree, full.ast.cond_expr) or
1052 becomesMultilineExpr(tree, full.ast.then_expr) or
1053 optBecomesMultilineExpr(tree, full.ast.cont_expr) or
1054 optBecomesMultilineExpr(tree, full.ast.else_expr);
1055 },
1056 .if_simple,
1057 .@"if",
1058 => {
1059 const full = tree.fullIf(node).?;
1060 return becomesMultilineExpr(tree, full.ast.cond_expr) or
1061 becomesMultilineExpr(tree, full.ast.then_expr) or
1062 optBecomesMultilineExpr(tree, full.ast.else_expr);
1063 },
1064 .fn_proto_simple,
1065 .fn_proto_multi,
1066 .fn_proto_one,
1067 .fn_proto,
1068 => {
1069 var buf: [1]Ast.Node.Index = undefined;
1070 const fn_proto = tree.fullFnProto(&buf, node).?;
1071
1072 for ([_]Ast.Node.OptionalIndex{
1073 fn_proto.ast.return_type,
1074 fn_proto.ast.align_expr,
1075 fn_proto.ast.addrspace_expr,
1076 fn_proto.ast.section_expr,
1077 fn_proto.ast.callconv_expr,
1078 }) |opt_expr| {
1079 if (opt_expr.unwrap()) |expr| {
1080 if (becomesMultilineExpr(tree, expr))
1081 return true;
1082 }
1083 }
1084 for (fn_proto.ast.params) |expr| {
1085 if (becomesMultilineExpr(tree, expr))
1086 return true;
1087 }
1088
1089 const lparen = fn_proto.ast.fn_token + 1;
1090 const return_type = fn_proto.ast.return_type.unwrap().?;
1091 const maybe_bang = tree.firstToken(return_type) - 1;
1092 const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
1093 return !isOneLineFnProto(tree, fn_proto, lparen, rparen);
1094 },
1095 .asm_simple,
1096 => {
1097 const lhs = tree.nodeData(node).node_and_token[0];
1098 return becomesMultilineExpr(tree, lhs);
1099 },
1100 .@"asm",
1101 => {
1102 const lhs, const extra_index = tree.nodeData(node).node_and_extra;
1103 const asm_extra = tree.extraData(extra_index, Ast.Node.Asm);
1104 return @intFromEnum(asm_extra.items_end) - @intFromEnum(asm_extra.items_start) != 0 or
1105 becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, asm_extra.clobbers);
1106 },
1107 .array_type, .array_type_sentinel => {
1108 const array_type = tree.fullArrayType(node).?;
1109 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
1110 return !isOneLineArrayType(tree, array_type, rbracket) or
1111 becomesMultilineExpr(tree, array_type.ast.elem_type);
1112 },
1113 .array_access => {
1114 const lhs, const rhs = tree.nodeData(node).node_and_node;
1115 const lbracket = tree.firstToken(rhs) - 1;
1116 const rbracket = tree.lastToken(rhs) + 1;
1117 return !tree.tokensOnSameLine(lbracket, rbracket) or
1118 becomesMultilineExpr(tree, lhs) or
1119 becomesMultilineExpr(tree, rhs);
1120 },
1121 .call_one,
1122 .call,
1123 .builtin_call_two,
1124 .builtin_call,
1125 .array_init_one,
1126 .array_init_dot_two,
1127 .array_init_dot,
1128 .array_init,
1129 .struct_init_one,
1130 .struct_init_dot_two,
1131 .struct_init_dot,
1132 .struct_init,
1133 => |tag| {
1134 var buf: [2]Ast.Node.Index = undefined;
1135 const opt_lhs: Ast.Node.OptionalIndex, const items = switch (tag) {
1136 .call_one, .call => blk: {
1137 const full = tree.fullCall(buf[0..1], node).?;
1138 break :blk .{ full.ast.fn_expr.toOptional(), full.ast.params };
1139 },
1140 .builtin_call_two, .builtin_call => .{ .none, tree.builtinCallParams(&buf, node).? },
1141 .array_init_one,
1142 .array_init_dot_two,
1143 .array_init_dot,
1144 .array_init,
1145 => blk: {
1146 const full = tree.fullArrayInit(&buf, node).?;
1147 break :blk .{ full.ast.type_expr, full.ast.elements };
1148 },
1149 .struct_init_one,
1150 .struct_init_dot_two,
1151 .struct_init_dot,
1152 .struct_init,
1153 => blk: {
1154 const full = tree.fullStructInit(&buf, node).?;
1155 break :blk .{ full.ast.type_expr, full.ast.fields };
1156 },
1157 else => unreachable,
1158 };
1159 if (opt_lhs.unwrap()) |lhs| {
1160 if (becomesMultilineExpr(tree, lhs))
1161 return true;
1162 }
1163 for (items) |expr| {
1164 if (becomesMultilineExpr(tree, expr))
1165 return true;
1166 }
1167 return false;
1168 },
1169 .assign_destructure => {
1170 const full = tree.assignDestructure(node);
1171 for (full.ast.variables) |expr| {
1172 if (becomesMultilineExpr(tree, expr))
1173 return true;
1174 }
1175 return becomesMultilineExpr(tree, full.ast.value_expr);
1176 },
1177 .ptr_type_aligned,
1178 .ptr_type_sentinel,
1179 .ptr_type,
1180 .ptr_type_bit_range,
1181 => {
1182 const full = tree.fullPtrType(node).?;
1183 return becomesMultilineExpr(tree, full.ast.child_type) or
1184 optBecomesMultilineExpr(tree, full.ast.sentinel) or
1185 optBecomesMultilineExpr(tree, full.ast.align_node) or
1186 optBecomesMultilineExpr(tree, full.ast.addrspace_node) or
1187 optBecomesMultilineExpr(tree, full.ast.bit_range_start) or
1188 optBecomesMultilineExpr(tree, full.ast.bit_range_end);
1189 },
1190 .slice_open,
1191 .slice,
1192 .slice_sentinel,
1193 => {
1194 const full = tree.fullSlice(node).?;
1195 return becomesMultilineExpr(tree, full.ast.sliced) or
1196 becomesMultilineExpr(tree, full.ast.start) or
1197 optBecomesMultilineExpr(tree, full.ast.end) or
1198 optBecomesMultilineExpr(tree, full.ast.sentinel);
1199 },
1200 .@"comptime",
1201 .@"nosuspend",
1202 .@"suspend",
1203 .@"resume",
1204 .bit_not,
1205 .bool_not,
1206 .negation,
1207 .negation_wrap,
1208 .optional_type,
1209 .address_of,
1210 .deref,
1211 .@"try",
1212 => return becomesMultilineExpr(tree, tree.nodeData(node).node),
1213 .@"return" => return optBecomesMultilineExpr(tree, tree.nodeData(node).opt_node),
1214 .field_access,
1215 .unwrap_optional,
1216 .grouped_expression,
1217 => return becomesMultilineExpr(tree, tree.nodeData(node).node_and_token[0]),
1218 .add,
1219 .add_wrap,
1220 .add_sat,
1221 .array_cat,
1222 .array_mult,
1223 .bang_equal,
1224 .bit_and,
1225 .bit_or,
1226 .shl,
1227 .shl_sat,
1228 .shr,
1229 .bit_xor,
1230 .bool_and,
1231 .bool_or,
1232 .div,
1233 .equal_equal,
1234 .greater_or_equal,
1235 .greater_than,
1236 .less_or_equal,
1237 .less_than,
1238 .merge_error_sets,
1239 .mod,
1240 .mul,
1241 .mul_wrap,
1242 .mul_sat,
1243 .sub,
1244 .sub_wrap,
1245 .sub_sat,
1246 .@"orelse",
1247 .@"catch",
1248 .error_union,
1249 .assign,
1250 .assign_bit_and,
1251 .assign_bit_or,
1252 .assign_shl,
1253 .assign_shl_sat,
1254 .assign_shr,
1255 .assign_bit_xor,
1256 .assign_div,
1257 .assign_sub,
1258 .assign_sub_wrap,
1259 .assign_sub_sat,
1260 .assign_mod,
1261 .assign_add,
1262 .assign_add_wrap,
1263 .assign_add_sat,
1264 .assign_mul,
1265 .assign_mul_wrap,
1266 .assign_mul_sat,
1267 => {
1268 const lhs, const rhs = tree.nodeData(node).node_and_node;
1269 return becomesMultilineExpr(tree, lhs) or becomesMultilineExpr(tree, rhs);
1270 },
1271 .@"break", .@"continue" => {
1272 const opt_expr = tree.nodeData(node).opt_token_and_opt_node[1];
1273 return optBecomesMultilineExpr(tree, opt_expr);
1274 },
1275 .anyframe_type => return becomesMultilineExpr(tree, tree.nodeData(node).token_and_node[1]),
1276 .@"errdefer",
1277 .@"defer",
1278 .for_range,
1279 .switch_range,
1280 .switch_case_one,
1281 .switch_case_inline_one,
1282 .switch_case,
1283 .switch_case_inline,
1284 .asm_output,
1285 .asm_input,
1286 .fn_decl,
1287 .container_field,
1288 .container_field_init,
1289 .container_field_align,
1290 .root,
1291 .global_var_decl,
1292 .local_var_decl,
1293 .simple_var_decl,
1294 .aligned_var_decl,
1295 .test_decl,
1296 => unreachable,
1297 }
1298}
1299
1300fn isOneLineArrayType(
1301 tree: Ast,
1302 array_type: Ast.full.ArrayType,
1303 rbracket: Ast.TokenIndex,
1304) bool {
1305 return tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and
1306 !becomesMultilineExpr(tree, array_type.ast.elem_count) and
1307 !optBecomesMultilineExpr(tree, array_type.ast.sentinel);
1308}
1309
929fn renderArrayType(1310fn renderArrayType(
930 r: *Render,1311 r: *Render,
931 array_type: Ast.full.ArrayType,1312 array_type: Ast.full.ArrayType,
...@@ -934,7 +1315,7 @@ fn renderArrayType(...@@ -934,7 +1315,7 @@ fn renderArrayType(
934 const tree = r.tree;1315 const tree = r.tree;
935 const ais = r.ais;1316 const ais = r.ais;
936 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;1317 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
937 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);1318 const one_line = isOneLineArrayType(tree, array_type, rbracket);
938 const inner_space = if (one_line) Space.none else Space.newline;1319 const inner_space = if (one_line) Space.none else Space.newline;
939 try ais.pushIndent(.normal);1320 try ais.pushIndent(.normal);
940 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket1321 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
...@@ -951,6 +1332,7 @@ fn renderArrayType(...@@ -951,6 +1332,7 @@ fn renderArrayType(
951fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {1332fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
952 const tree = r.tree;1333 const tree = r.tree;
953 const main_token = ptr_type.ast.main_token;1334 const main_token = ptr_type.ast.main_token;
1335
954 switch (ptr_type.size) {1336 switch (ptr_type.size) {
955 .one => {1337 .one => {
956 // Since ** tokens exist and the same token is shared by two1338 // 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...@@ -997,11 +1379,36 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
997 },1379 },
998 }1380 }
9991381
1382 // .maybe_space cannot be used at the end of each qualifier since they may be reordered
1383 const final_qual: enum {
1384 @"volatile",
1385 @"const",
1386 @"addrspace",
1387 @"align",
1388 @"allowzero",
1389 none,
1390 } = if (ptr_type.volatile_token != null)
1391 .@"volatile"
1392 else if (ptr_type.const_token != null)
1393 .@"const"
1394 else if (ptr_type.ast.addrspace_node != .none)
1395 .@"addrspace"
1396 else if (ptr_type.ast.align_node != .none)
1397 .@"align"
1398 else if (ptr_type.allowzero_token != null)
1399 .@"allowzero"
1400 else
1401 .none;
1402 const final_qual_space: Space = if (tree.tokenTag(tree.firstToken(ptr_type.ast.child_type)) !=
1403 .multiline_string_literal_line) .space else .none;
1404
1000 if (ptr_type.allowzero_token) |allowzero_token| {1405 if (ptr_type.allowzero_token) |allowzero_token| {
1001 try renderToken(r, allowzero_token, .space);1406 const this_space: Space = if (final_qual == .@"allowzero") final_qual_space else .space;
1407 try renderToken(r, allowzero_token, this_space);
1002 }1408 }
10031409
1004 if (ptr_type.ast.align_node.unwrap()) |align_node| {1410 if (ptr_type.ast.align_node.unwrap()) |align_node| {
1411 const this_space: Space = if (final_qual == .@"align") final_qual_space else .space;
1005 const align_first = tree.firstToken(align_node);1412 const align_first = tree.firstToken(align_node);
1006 try renderToken(r, align_first - 2, .none); // align1413 try renderToken(r, align_first - 2, .none); // align
1007 try renderToken(r, align_first - 1, .none); // lparen1414 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...@@ -1012,26 +1419,29 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
1012 try renderExpression(r, bit_range_start, .none);1419 try renderExpression(r, bit_range_start, .none);
1013 try renderToken(r, tree.firstToken(bit_range_end) - 1, .none); // colon1420 try renderToken(r, tree.firstToken(bit_range_end) - 1, .none); // colon
1014 try renderExpression(r, bit_range_end, .none);1421 try renderExpression(r, bit_range_end, .none);
1015 try renderToken(r, tree.lastToken(bit_range_end) + 1, .space); // rparen1422 try renderToken(r, tree.lastToken(bit_range_end) + 1, this_space); // rparen
1016 } else {1423 } else {
1017 try renderToken(r, tree.lastToken(align_node) + 1, .space); // rparen1424 try renderToken(r, tree.lastToken(align_node) + 1, this_space); // rparen
1018 }1425 }
1019 }1426 }
10201427
1021 if (ptr_type.ast.addrspace_node.unwrap()) |addrspace_node| {1428 if (ptr_type.ast.addrspace_node.unwrap()) |addrspace_node| {
1429 const this_space: Space = if (final_qual == .@"addrspace") final_qual_space else .space;
1022 const addrspace_first = tree.firstToken(addrspace_node);1430 const addrspace_first = tree.firstToken(addrspace_node);
1023 try renderToken(r, addrspace_first - 2, .none); // addrspace1431 try renderToken(r, addrspace_first - 2, .none); // addrspace
1024 try renderToken(r, addrspace_first - 1, .none); // lparen1432 try renderToken(r, addrspace_first - 1, .none); // lparen
1025 try renderExpression(r, addrspace_node, .none);1433 try renderExpression(r, addrspace_node, .none);
1026 try renderToken(r, tree.lastToken(addrspace_node) + 1, .space); // rparen1434 try renderToken(r, tree.lastToken(addrspace_node) + 1, this_space); // rparen
1027 }1435 }
10281436
1029 if (ptr_type.const_token) |const_token| {1437 if (ptr_type.const_token) |const_token| {
1030 try renderToken(r, const_token, .space);1438 const this_space: Space = if (final_qual == .@"const") final_qual_space else .space;
1439 try renderToken(r, const_token, this_space);
1031 }1440 }
10321441
1033 if (ptr_type.volatile_token) |volatile_token| {1442 if (ptr_type.volatile_token) |volatile_token| {
1034 try renderToken(r, volatile_token, .space);1443 const this_space: Space = if (final_qual == .@"volatile") final_qual_space else unreachable;
1444 try renderToken(r, volatile_token, this_space);
1035 }1445 }
10361446
1037 try renderExpression(r, ptr_type.ast.child_type, space);1447 try renderExpression(r, ptr_type.ast.child_type, space);
...@@ -1044,12 +1454,14 @@ fn renderSlice(...@@ -1044,12 +1454,14 @@ fn renderSlice(
1044 space: Space,1454 space: Space,
1045) Error!void {1455) Error!void {
1046 const tree = r.tree;1456 const tree = r.tree;
1047 const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or1457 const space_around_dots = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
1048 if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;1458 if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;
1049 const after_start_space = if (after_start_space_bool) Space.space else Space.none;1459 const after_start_space: Space = if (space_around_dots) .space else .none;
1050 const after_dots_space = if (slice.ast.end != .none)1460 const before_sentinel_space: Space = if (slice.ast.sentinel != .none) .space else .none;
1051 after_start_space1461 const after_dots_space: Space = if (slice.ast.end != .none)
1052 else if (slice.ast.sentinel != .none) Space.space else Space.none;1462 if (space_around_dots) .maybe_space else .none
1463 else
1464 before_sentinel_space;
10531465
1054 try renderExpression(r, slice.ast.sliced, .none);1466 try renderExpression(r, slice.ast.sliced, .none);
1055 try renderToken(r, slice.ast.lbracket, .none); // lbracket1467 try renderToken(r, slice.ast.lbracket, .none); // lbracket
...@@ -1059,8 +1471,7 @@ fn renderSlice(...@@ -1059,8 +1471,7 @@ fn renderSlice(
1059 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")1471 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")
10601472
1061 if (slice.ast.end.unwrap()) |end| {1473 if (slice.ast.end.unwrap()) |end| {
1062 const after_end_space = if (slice.ast.sentinel != .none) Space.space else Space.none;1474 try renderExpression(r, end, before_sentinel_space);
1063 try renderExpression(r, end, after_end_space);
1064 }1475 }
10651476
1066 if (slice.ast.sentinel.unwrap()) |sentinel| {1477 if (slice.ast.sentinel.unwrap()) |sentinel| {
...@@ -1088,7 +1499,7 @@ fn renderAsmOutput(...@@ -1088,7 +1499,7 @@ fn renderAsmOutput(
10881499
1089 if (tree.tokenTag(symbolic_name + 4) == .arrow) {1500 if (tree.tokenTag(symbolic_name + 4) == .arrow) {
1090 const type_expr, const rparen = tree.nodeData(asm_output).opt_node_and_token;1501 const type_expr, const rparen = tree.nodeData(asm_output).opt_node_and_token;
1091 try renderToken(r, symbolic_name + 4, .space); // ->1502 try renderToken(r, symbolic_name + 4, .maybe_space); // ->
1092 try renderExpression(r, type_expr.unwrap().?, Space.none);1503 try renderExpression(r, type_expr.unwrap().?, Space.none);
1093 return renderToken(r, rparen, space);1504 return renderToken(r, rparen, space);
1094 } else {1505 } else {
...@@ -1169,33 +1580,38 @@ fn renderVarDeclWithoutFixups(...@@ -1169,33 +1580,38 @@ fn renderVarDeclWithoutFixups(
11691580
1170 try renderToken(r, var_decl.ast.mut_token, .space); // var1581 try renderToken(r, var_decl.ast.mut_token, .space); // var
11711582
1172 if (var_decl.ast.type_node != .none or var_decl.ast.align_node != .none or1583 const last_component: enum {
1173 var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or1584 value,
1174 var_decl.ast.init_node != .none)1585 @"linksection",
1175 {1586 @"addrspace",
1176 const name_space = if (var_decl.ast.type_node == .none and1587 @"align",
1177 (var_decl.ast.align_node != .none or1588 type,
1178 var_decl.ast.addrspace_node != .none or1589 identifier,
1179 var_decl.ast.section_node != .none or1590 } = if (var_decl.ast.init_node != .none)
1180 var_decl.ast.init_node != .none))1591 .value
1181 Space.space1592 else if (var_decl.ast.section_node != .none)
1182 else1593 .@"linksection"
1183 Space.none;1594 else if (var_decl.ast.addrspace_node != .none)
11841595 .@"addrspace"
1185 try renderIdentifier(r, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name1596 else if (var_decl.ast.align_node != .none)
1186 } else {1597 .@"align"
1187 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name1598 else if (var_decl.ast.type_node != .none)
1599 .type
1600 else
1601 .identifier;
1602
1603 if (last_component == .identifier) {
1604 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing);
1188 }1605 }
1606 const after_ident_space: Space = if (var_decl.ast.type_node != .none) .none else .space;
1607 try renderIdentifier(r, var_decl.ast.mut_token + 1, after_ident_space, .preserve_when_shadowing);
11891608
1190 if (var_decl.ast.type_node.unwrap()) |type_node| {1609 if (var_decl.ast.type_node.unwrap()) |type_node| {
1191 try renderToken(r, var_decl.ast.mut_token + 2, Space.space); // :1610 try renderToken(r, var_decl.ast.mut_token + 2, .maybe_space); // :
1192 if (var_decl.ast.align_node != .none or var_decl.ast.addrspace_node != .none or1611 if (last_component == .type) {
1193 var_decl.ast.section_node != .none or var_decl.ast.init_node != .none)
1194 {
1195 try renderExpression(r, type_node, .space);
1196 } else {
1197 return renderExpression(r, type_node, space);1612 return renderExpression(r, type_node, space);
1198 }1613 }
1614 try renderExpression(r, type_node, .space);
1199 }1615 }
12001616
1201 if (var_decl.ast.align_node.unwrap()) |align_node| {1617 if (var_decl.ast.align_node.unwrap()) |align_node| {
...@@ -1205,13 +1621,10 @@ fn renderVarDeclWithoutFixups(...@@ -1205,13 +1621,10 @@ fn renderVarDeclWithoutFixups(
1205 try renderToken(r, align_kw, Space.none); // align1621 try renderToken(r, align_kw, Space.none); // align
1206 try renderToken(r, lparen, Space.none); // (1622 try renderToken(r, lparen, Space.none); // (
1207 try renderExpression(r, align_node, Space.none);1623 try renderExpression(r, align_node, Space.none);
1208 if (var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or1624 if (last_component == .@"align") {
1209 var_decl.ast.init_node != .none)
1210 {
1211 try renderToken(r, rparen, .space); // )
1212 } else {
1213 return renderToken(r, rparen, space); // )1625 return renderToken(r, rparen, space); // )
1214 }1626 }
1627 try renderToken(r, rparen, .space); // )
1215 }1628 }
12161629
1217 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {1630 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
...@@ -1221,12 +1634,10 @@ fn renderVarDeclWithoutFixups(...@@ -1221,12 +1634,10 @@ fn renderVarDeclWithoutFixups(
1221 try renderToken(r, addrspace_kw, Space.none); // addrspace1634 try renderToken(r, addrspace_kw, Space.none); // addrspace
1222 try renderToken(r, lparen, Space.none); // (1635 try renderToken(r, lparen, Space.none); // (
1223 try renderExpression(r, addrspace_node, Space.none);1636 try renderExpression(r, addrspace_node, Space.none);
1224 if (var_decl.ast.section_node != .none or var_decl.ast.init_node != .none) {1637 if (last_component == .@"addrspace") {
1225 try renderToken(r, rparen, .space); // )1638 return renderToken(r, rparen, space); // )
1226 } else {
1227 try renderToken(r, rparen, .none); // )
1228 return renderToken(r, rparen + 1, Space.newline); // ;
1229 }1639 }
1640 try renderToken(r, rparen, .space); // )
1230 }1641 }
12311642
1232 if (var_decl.ast.section_node.unwrap()) |section_node| {1643 if (var_decl.ast.section_node.unwrap()) |section_node| {
...@@ -1236,17 +1647,19 @@ fn renderVarDeclWithoutFixups(...@@ -1236,17 +1647,19 @@ fn renderVarDeclWithoutFixups(
1236 try renderToken(r, section_kw, Space.none); // linksection1647 try renderToken(r, section_kw, Space.none); // linksection
1237 try renderToken(r, lparen, Space.none); // (1648 try renderToken(r, lparen, Space.none); // (
1238 try renderExpression(r, section_node, Space.none);1649 try renderExpression(r, section_node, Space.none);
1239 if (var_decl.ast.init_node != .none) {1650 if (last_component == .@"linksection") {
1240 try renderToken(r, rparen, .space); // )
1241 } else {
1242 return renderToken(r, rparen, space); // )1651 return renderToken(r, rparen, space); // )
1243 }1652 }
1653 try renderToken(r, rparen, .space); // )
1244 }1654 }
12451655
1656 assert(last_component == .value);
1246 const init_node = var_decl.ast.init_node.unwrap().?;1657 const init_node = var_decl.ast.init_node.unwrap().?;
12471658
1248 const eq_token = tree.firstToken(init_node) - 1;1659 const eq_token = tree.firstToken(init_node) - 1;
1249 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;1660 const rhs_seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
1661 tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
1662 const eq_space: Space = if (rhs_seperate_line) .newline else .space;
1250 try ais.pushIndent(.after_equals);1663 try ais.pushIndent(.after_equals);
1251 try renderToken(r, eq_token, eq_space); // =1664 try renderToken(r, eq_token, eq_space); // =
1252 try renderExpression(r, init_node, space); // ;1665 try renderExpression(r, init_node, space); // ;
...@@ -1346,8 +1759,10 @@ fn renderThenElse(...@@ -1346,8 +1759,10 @@ fn renderThenElse(
1346 const tree = r.tree;1759 const tree = r.tree;
1347 const ais = r.ais;1760 const ais = r.ais;
1348 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));1761 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));
1762 const then_expr_first_token = tree.firstToken(then_expr);
1349 const indent_then_expr = !then_expr_is_block and1763 const indent_then_expr = !then_expr_is_block and
1350 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));1764 (!tree.tokensOnSameLine(last_prefix_token, then_expr_first_token) or
1765 tree.tokenTag(then_expr_first_token) == .multiline_string_literal_line);
13511766
1352 if (indent_then_expr) try ais.pushIndent(.normal);1767 if (indent_then_expr) try ais.pushIndent(.normal);
13531768
...@@ -1381,7 +1796,9 @@ fn renderThenElse(...@@ -1381,7 +1796,9 @@ fn renderThenElse(
13811796
1382 const indent_else_expr = indent_then_expr and1797 const indent_else_expr = indent_then_expr and
1383 !nodeIsBlock(tree.nodeTag(else_expr)) and1798 !nodeIsBlock(tree.nodeTag(else_expr)) and
1384 !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr));1799 !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr)) or
1800 tree.tokenTag(tree.firstToken(else_expr)) ==
1801 .multiline_string_literal_line;
1385 if (indent_else_expr) {1802 if (indent_else_expr) {
1386 try ais.pushIndent(.normal);1803 try ais.pushIndent(.normal);
1387 try renderToken(r, last_else_token, .newline);1804 try renderToken(r, last_else_token, .newline);
...@@ -1417,44 +1834,29 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {...@@ -1417,44 +1834,29 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
1417 try renderParamList(r, lparen, for_node.ast.inputs, .space);1834 try renderParamList(r, lparen, for_node.ast.inputs, .space);
14181835
1419 var cur = for_node.payload_token;1836 var cur = for_node.payload_token;
1420 const pipe = std.mem.findScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;1837 const pipe = std.mem.findScalarPos(Token.Tag, token_tags, cur, .pipe).?;
1421 if (tree.tokenTag(@intCast(pipe - 1)) == .comma) {1838 const capture_trailing_comma = token_tags[@intCast(pipe - 1)] == .comma;
1839
1840 if (capture_trailing_comma)
1422 try ais.pushIndent(.normal);1841 try ais.pushIndent(.normal);
1423 try renderToken(r, cur - 1, .newline); // |1842 try renderToken(r, cur - 1, if (capture_trailing_comma) .newline else .none); // |
1424 while (true) {1843 while (true) {
1425 if (tree.tokenTag(cur) == .asterisk) {1844 if (token_tags[cur] == .asterisk) {
1426 try renderToken(r, cur, .none); // *1845 try renderToken(r, cur, .none); // *
1427 cur += 1;
1428 }
1429 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1430 cur += 1;1846 cur += 1;
1431 if (tree.tokenTag(cur) == .comma) {
1432 try renderToken(r, cur, .newline); // ,
1433 cur += 1;
1434 }
1435 if (tree.tokenTag(cur) == .pipe) {
1436 break;
1437 }
1438 }1847 }
1439 ais.popIndent();1848 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1440 } else {1849 cur += 1;
1441 try renderToken(r, cur - 1, .none); // |1850 if (token_tags[cur] == .comma) {
1442 while (true) {1851 try renderToken(r, cur, if (capture_trailing_comma) .newline else .space); // ,
1443 if (tree.tokenTag(cur) == .asterisk) {
1444 try renderToken(r, cur, .none); // *
1445 cur += 1;
1446 }
1447 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1448 cur += 1;1852 cur += 1;
1449 if (tree.tokenTag(cur) == .comma) {1853 }
1450 try renderToken(r, cur, .space); // ,1854 if (token_tags[cur] == .pipe) {
1451 cur += 1;1855 break;
1452 }
1453 if (tree.tokenTag(cur) == .pipe) {
1454 break;
1455 }
1456 }1856 }
1457 }1857 }
1858 if (capture_trailing_comma)
1859 ais.popIndent();
14581860
1459 try renderThenElse(1861 try renderThenElse(
1460 r,1862 r,
...@@ -1483,97 +1885,80 @@ fn renderContainerField(...@@ -1483,97 +1885,80 @@ fn renderContainerField(
1483 };1885 };
14841886
1485 if (field.comptime_token) |t| {1887 if (field.comptime_token) |t| {
1486 try renderToken(r, t, .space); // comptime1888 try renderToken(r, t, .maybe_space); // comptime
1487 }1889 }
1488 if (field.ast.type_expr == .none and field.ast.value_expr == .none) {1890
1489 if (field.ast.align_expr.unwrap()) |align_expr| {1891 const last_component: enum {
1490 try renderIdentifier(r, field.ast.main_token, .space, quote); // name1892 value,
1491 const lparen_token = tree.firstToken(align_expr) - 1;1893 @"align",
1492 const align_kw = lparen_token - 1;1894 type,
1493 const rparen_token = tree.lastToken(align_expr) + 1;1895 identifier,
1494 try renderToken(r, align_kw, .none); // align1896 } = if (field.ast.value_expr != .none)
1495 try renderToken(r, lparen_token, .none); // (1897 .value
1496 try renderExpression(r, align_expr, .none); // alignment1898 else if (field.ast.align_expr != .none)
1497 return renderToken(r, rparen_token, .space); // )1899 .@"align"
1900 else if (field.ast.type_expr != .none)
1901 .type
1902 else if (!field.ast.tuple_like)
1903 .identifier
1904 else
1905 unreachable;
1906
1907 if (!field.ast.tuple_like) {
1908 if (last_component == .identifier) {
1909 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
1498 }1910 }
1499 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name1911 const this_space: Space = if (field.ast.type_expr != .none) .none else .space;
1912 try renderIdentifier(r, field.ast.main_token, this_space, quote); // name
1500 }1913 }
1501 if (field.ast.type_expr != .none and field.ast.value_expr == .none) {1914
1502 const type_expr = field.ast.type_expr.unwrap().?;1915 if (field.ast.type_expr.unwrap()) |type_expr| {
1503 if (!field.ast.tuple_like) {1916 if (!field.ast.tuple_like) {
1504 try renderIdentifier(r, field.ast.main_token, .none, quote); // name1917 try renderToken(r, field.ast.main_token + 1, .maybe_space); // :
1505 try renderToken(r, field.ast.main_token + 1, .space); // :
1506 }1918 }
15071919
1508 if (field.ast.align_expr.unwrap()) |align_expr| {1920 if (last_component == .type) {
1509 try renderExpression(r, type_expr, .space); // type
1510 const align_token = tree.firstToken(align_expr) - 2;
1511 try renderToken(r, align_token, .none); // align
1512 try renderToken(r, align_token + 1, .none); // (
1513 try renderExpression(r, align_expr, .none); // alignment
1514 const rparen = tree.lastToken(align_expr) + 1;
1515 return renderTokenComma(r, rparen, space); // )
1516 } else {
1517 return renderExpressionComma(r, type_expr, space); // type1921 return renderExpressionComma(r, type_expr, space); // type
1518 }1922 }
1923 try renderExpression(r, type_expr, .space); // type
1519 }1924 }
1520 if (field.ast.type_expr == .none and field.ast.value_expr != .none) {
1521 const value_expr = field.ast.value_expr.unwrap().?;
1522
1523 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
1524 if (field.ast.align_expr.unwrap()) |align_expr| {
1525 const lparen_token = tree.firstToken(align_expr) - 1;
1526 const align_kw = lparen_token - 1;
1527 const rparen_token = tree.lastToken(align_expr) + 1;
1528 try renderToken(r, align_kw, .none); // align
1529 try renderToken(r, lparen_token, .none); // (
1530 try renderExpression(r, align_expr, .none); // alignment
1531 try renderToken(r, rparen_token, .space); // )
1532 }
1533 try renderToken(r, field.ast.main_token + 1, .space); // =
1534 return renderExpressionComma(r, value_expr, space); // value
1535 }
1536 if (!field.ast.tuple_like) {
1537 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1538 try renderToken(r, field.ast.main_token + 1, .space); // :
1539 }
1540
1541 const type_expr = field.ast.type_expr.unwrap().?;
1542 const value_expr = field.ast.value_expr.unwrap().?;
1543
1544 try renderExpression(r, type_expr, .space); // type
15451925
1546 if (field.ast.align_expr.unwrap()) |align_expr| {1926 if (field.ast.align_expr.unwrap()) |align_expr| {
1547 const lparen_token = tree.firstToken(align_expr) - 1;1927 const align_token = tree.firstToken(align_expr) - 2;
1548 const align_kw = lparen_token - 1;1928 try renderToken(r, align_token, .none); // align
1549 const rparen_token = tree.lastToken(align_expr) + 1;1929 try renderToken(r, align_token + 1, .none); // (
1550 try renderToken(r, align_kw, .none); // align
1551 try renderToken(r, lparen_token, .none); // (
1552 try renderExpression(r, align_expr, .none); // alignment1930 try renderExpression(r, align_expr, .none); // alignment
1553 try renderToken(r, rparen_token, .space); // )1931 const rparen = tree.lastToken(align_expr) + 1;
1932 if (last_component == .@"align") {
1933 return renderTokenComma(r, rparen, space); // )
1934 }
1935 try renderToken(r, rparen, .space);
1554 }1936 }
1555 const eq_token = tree.firstToken(value_expr) - 1;
1556 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
15571937
1558 try ais.pushIndent(.after_equals);1938 if (field.ast.value_expr.unwrap()) |value_expr| {
1559 try renderToken(r, eq_token, eq_space); // =1939 assert(last_component == .value);
1940 const eq_token = tree.firstToken(value_expr) - 1;
1941 const seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
1942 tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
1943 const eq_space: Space = if (seperate_line) .newline else .space;
15601944
1561 if (eq_space == .space) {1945 try ais.pushIndent(.after_equals);
1562 ais.popIndent();1946 try renderToken(r, eq_token, eq_space); // =
1563 try renderExpressionComma(r, value_expr, space); // value1947 if (eq_space == .space) {
1564 return;1948 ais.popIndent();
1565 }1949 return renderExpressionComma(r, value_expr, space); // value
15661950 }
1567 const maybe_comma = tree.lastToken(value_expr) + 1;
15681951
1569 if (tree.tokenTag(maybe_comma) == .comma) {1952 const maybe_comma = tree.lastToken(value_expr) + 1;
1570 try renderExpression(r, value_expr, .none); // value1953 if (tree.tokenTag(maybe_comma) == .comma) {
1571 ais.popIndent();1954 try renderExpression(r, value_expr, .none); // value
1572 try renderToken(r, maybe_comma, .newline);1955 ais.popIndent();
1573 } else {1956 try renderToken(r, maybe_comma, .newline);
1574 try renderExpression(r, value_expr, space); // value1957 } else {
1575 ais.popIndent();1958 try renderExpression(r, value_expr, space); // value
1576 }1959 ais.popIndent();
1960 }
1961 } else unreachable;
1577}1962}
15781963
1579fn renderBuiltinCall(1964fn renderBuiltinCall(
...@@ -1587,14 +1972,9 @@ fn renderBuiltinCall(...@@ -1587,14 +1972,9 @@ fn renderBuiltinCall(
15871972
1588 try renderToken(r, builtin_token, .none); // @name1973 try renderToken(r, builtin_token, .none); // @name
15891974
1590 if (params.len == 0) {
1591 try renderToken(r, builtin_token + 1, .none); // (
1592 return renderToken(r, builtin_token + 2, space); // )
1593 }
1594
1595 if (r.fixups.rebase_imported_paths) |prefix| {1975 if (r.fixups.rebase_imported_paths) |prefix| {
1596 const slice = tree.tokenSlice(builtin_token);1976 const slice = tree.tokenSlice(builtin_token);
1597 if (mem.eql(u8, slice, "@import")) f: {1977 if (params.len != 0 and mem.eql(u8, slice, "@import")) f: {
1598 const param = params[0];1978 const param = params[0];
1599 const str_lit_token = tree.nodeMainToken(param);1979 const str_lit_token = tree.nodeMainToken(param);
1600 assert(tree.tokenTag(str_lit_token) == .string_literal);1980 assert(tree.tokenTag(str_lit_token) == .string_literal);
...@@ -1613,45 +1993,69 @@ fn renderBuiltinCall(...@@ -1613,45 +1993,69 @@ fn renderBuiltinCall(
1613 }1993 }
1614 }1994 }
16151995
1616 const last_param = params[params.len - 1];1996 return renderParamList(r, builtin_token + 1, params, space);
1617 const after_last_param_token = tree.lastToken(last_param) + 1;1997}
1618
1619 if (tree.tokenTag(after_last_param_token) != .comma) {
1620 // Render all on one line, no trailing comma.
1621 try renderToken(r, builtin_token + 1, .none); // (
1622
1623 for (params, 0..) |param_node, i| {
1624 const first_param_token = tree.firstToken(param_node);
1625 if (tree.tokenTag(first_param_token) == .multiline_string_literal_line or
1626 hasSameLineComment(tree, first_param_token - 1))
1627 {
1628 try ais.pushIndent(.normal);
1629 try renderExpression(r, param_node, .none);
1630 ais.popIndent();
1631 } else {
1632 try renderExpression(r, param_node, .none);
1633 }
16341998
1635 if (i + 1 < params.len) {1999fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex {
1636 const comma_token = tree.lastToken(param_node) + 1;2000 // These may appear in any order, so we have to check the token_starts array
1637 try renderToken(r, comma_token, .space); // ,2001 // to find out which is first.
1638 }2002 var rparen = if (tree.tokenTag(maybe_bang) == .bang) maybe_bang - 1 else maybe_bang;
2003 var smallest_start = tree.tokenStart(maybe_bang);
2004 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
2005 const tok = tree.firstToken(align_expr) - 3;
2006 const start = tree.tokenStart(tok);
2007 if (start < smallest_start) {
2008 rparen = tok;
2009 smallest_start = start;
1639 }2010 }
1640 return renderToken(r, after_last_param_token, space); // )2011 }
1641 } else {2012 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
1642 // Render one param per line.2013 const tok = tree.firstToken(addrspace_expr) - 3;
1643 try ais.pushIndent(.normal);2014 const start = tree.tokenStart(tok);
1644 try renderToken(r, builtin_token + 1, Space.newline); // (2015 if (start < smallest_start) {
16452016 rparen = tok;
1646 for (params) |param_node| {2017 smallest_start = start;
1647 try ais.pushSpace(.comma);2018 }
1648 try renderExpression(r, param_node, .comma);2019 }
1649 ais.popSpace();2020 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
2021 const tok = tree.firstToken(section_expr) - 3;
2022 const start = tree.tokenStart(tok);
2023 if (start < smallest_start) {
2024 rparen = tok;
2025 smallest_start = start;
2026 }
2027 }
2028 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
2029 const tok = tree.firstToken(callconv_expr) - 3;
2030 const start = tree.tokenStart(tok);
2031 if (start < smallest_start) {
2032 rparen = tok;
2033 smallest_start = start;
1650 }2034 }
1651 ais.popIndent();
1652
1653 return renderToken(r, after_last_param_token + 1, space); // )
1654 }2035 }
2036 assert(tree.tokenTag(rparen) == .r_paren);
2037 return rparen;
2038}
2039
2040fn isOneLineFnProto(
2041 tree: Ast,
2042 fn_proto: Ast.full.FnProto,
2043 lparen: Ast.TokenIndex,
2044 rparen: Ast.TokenIndex,
2045) bool {
2046 const trailing_comma = tree.tokenTag(rparen - 1) == .comma;
2047 if (trailing_comma or hasComment(tree, lparen, rparen))
2048 return false;
2049
2050 // Check that there are no doc comments
2051 var after_last_param = lparen + 1;
2052 for (fn_proto.ast.params) |expr| {
2053 // Looking before each param is insufficient since anytype is not included in `params`
2054 if (hasDocComment(tree, after_last_param, tree.firstToken(expr)))
2055 return false;
2056 after_last_param = tree.lastToken(expr) + 1;
2057 }
2058 return !hasDocComment(tree, after_last_param, rparen);
1655}2059}
16562060
1657fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void {2061fn 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...@@ -1671,51 +2075,11 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
16712075
1672 const return_type = fn_proto.ast.return_type.unwrap().?;2076 const return_type = fn_proto.ast.return_type.unwrap().?;
1673 const maybe_bang = tree.firstToken(return_type) - 1;2077 const maybe_bang = tree.firstToken(return_type) - 1;
1674 const rparen = blk: {2078 const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
1675 // These may appear in any order, so we have to check the token_starts array
1676 // to find out which is first.
1677 var rparen = if (tree.tokenTag(maybe_bang) == .bang) maybe_bang - 1 else maybe_bang;
1678 var smallest_start = tree.tokenStart(maybe_bang);
1679 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
1680 const tok = tree.firstToken(align_expr) - 3;
1681 const start = tree.tokenStart(tok);
1682 if (start < smallest_start) {
1683 rparen = tok;
1684 smallest_start = start;
1685 }
1686 }
1687 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
1688 const tok = tree.firstToken(addrspace_expr) - 3;
1689 const start = tree.tokenStart(tok);
1690 if (start < smallest_start) {
1691 rparen = tok;
1692 smallest_start = start;
1693 }
1694 }
1695 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
1696 const tok = tree.firstToken(section_expr) - 3;
1697 const start = tree.tokenStart(tok);
1698 if (start < smallest_start) {
1699 rparen = tok;
1700 smallest_start = start;
1701 }
1702 }
1703 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
1704 const tok = tree.firstToken(callconv_expr) - 3;
1705 const start = tree.tokenStart(tok);
1706 if (start < smallest_start) {
1707 rparen = tok;
1708 smallest_start = start;
1709 }
1710 }
1711 break :blk rparen;
1712 };
1713 assert(tree.tokenTag(rparen) == .r_paren);
17142079
1715 // The params list is a sparse set that does *not* include anytype or ... parameters.2080 // The params list is a sparse set that does *not* include anytype or ... parameters.
17162081
1717 const trailing_comma = tree.tokenTag(rparen - 1) == .comma;2082 if (isOneLineFnProto(tree, fn_proto, lparen, rparen)) {
1718 if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
1719 // Render all on one line, no trailing comma.2083 // Render all on one line, no trailing comma.
1720 try renderToken(r, lparen, .none); // (2084 try renderToken(r, lparen, .none); // (
17212085
...@@ -1724,10 +2088,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1724,10 +2088,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1724 while (true) {2088 while (true) {
1725 last_param_token += 1;2089 last_param_token += 1;
1726 switch (tree.tokenTag(last_param_token)) {2090 switch (tree.tokenTag(last_param_token)) {
1727 .doc_comment => {2091 .doc_comment => unreachable,
1728 try renderToken(r, last_param_token, .newline);
1729 continue;
1730 },
1731 .ellipsis3 => {2092 .ellipsis3 => {
1732 try renderToken(r, last_param_token, .none); // ...2093 try renderToken(r, last_param_token, .none); // ...
1733 break;2094 break;
...@@ -1743,7 +2104,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1743,7 +2104,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1743 },2104 },
1744 .r_paren => break,2105 .r_paren => break,
1745 .comma => {2106 .comma => {
1746 try renderToken(r, last_param_token, .space); // ,2107 try renderToken(r, last_param_token, .maybe_space); // ,
1747 continue;2108 continue;
1748 },2109 },
1749 else => {}, // Parameter type without a name.2110 else => {}, // Parameter type without a name.
...@@ -1753,7 +2114,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1753,7 +2114,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1753 {2114 {
1754 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name2115 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
1755 last_param_token = last_param_token + 1;2116 last_param_token = last_param_token + 1;
1756 try renderToken(r, last_param_token, .space); // :2117 try renderToken(r, last_param_token, .maybe_space); // :
1757 last_param_token += 1;2118 last_param_token += 1;
1758 }2119 }
1759 if (tree.tokenTag(last_param_token) == .keyword_anytype) {2120 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...@@ -1822,7 +2183,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1822 ais.popIndent();2183 ais.popIndent();
1823 }2184 }
18242185
1825 try renderToken(r, rparen, .space); // )2186 try renderToken(r, rparen, .maybe_space); // )
18262187
1827 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {2188 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
1828 const align_lparen = tree.firstToken(align_expr) - 1;2189 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...@@ -1831,7 +2192,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1831 try renderToken(r, align_lparen - 1, .none); // align2192 try renderToken(r, align_lparen - 1, .none); // align
1832 try renderToken(r, align_lparen, .none); // (2193 try renderToken(r, align_lparen, .none); // (
1833 try renderExpression(r, align_expr, .none);2194 try renderExpression(r, align_expr, .none);
1834 try renderToken(r, align_rparen, .space); // )2195 try renderToken(r, align_rparen, .maybe_space); // )
1835 }2196 }
18362197
1837 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {2198 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...@@ -1841,7 +2202,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1841 try renderToken(r, align_lparen - 1, .none); // addrspace2202 try renderToken(r, align_lparen - 1, .none); // addrspace
1842 try renderToken(r, align_lparen, .none); // (2203 try renderToken(r, align_lparen, .none); // (
1843 try renderExpression(r, addrspace_expr, .none);2204 try renderExpression(r, addrspace_expr, .none);
1844 try renderToken(r, align_rparen, .space); // )2205 try renderToken(r, align_rparen, .maybe_space); // )
1845 }2206 }
18462207
1847 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {2208 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...@@ -1851,7 +2212,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1851 try renderToken(r, section_lparen - 1, .none); // section2212 try renderToken(r, section_lparen - 1, .none); // section
1852 try renderToken(r, section_lparen, .none); // (2213 try renderToken(r, section_lparen, .none); // (
1853 try renderExpression(r, section_expr, .none);2214 try renderExpression(r, section_expr, .none);
1854 try renderToken(r, section_rparen, .space); // )2215 try renderToken(r, section_rparen, .maybe_space); // )
1855 }2216 }
18562217
1857 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {2218 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...@@ -1865,7 +2226,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1865 try renderToken(r, callconv_lparen - 1, .none); // callconv2226 try renderToken(r, callconv_lparen - 1, .none); // callconv
1866 try renderToken(r, callconv_lparen, .none); // (2227 try renderToken(r, callconv_lparen, .none); // (
1867 try renderExpression(r, callconv_expr, .none);2228 try renderExpression(r, callconv_expr, .none);
1868 try renderToken(r, callconv_rparen, .space); // )2229 try renderToken(r, callconv_rparen, .maybe_space); // )
1869 }2230 }
1870 }2231 }
18712232
...@@ -1890,7 +2251,7 @@ fn renderSwitchCase(...@@ -1890,7 +2251,7 @@ fn renderSwitchCase(
18902251
1891 // render inline keyword2252 // render inline keyword
1892 if (switch_case.inline_token) |some| {2253 if (switch_case.inline_token) |some| {
1893 try renderToken(r, some, .space);2254 try renderToken(r, some, .maybe_space);
1894 }2255 }
18952256
1896 // Render everything before the arrow2257 // Render everything before the arrow
...@@ -1904,33 +2265,26 @@ fn renderSwitchCase(...@@ -1904,33 +2265,26 @@ fn renderSwitchCase(
1904 } else {2265 } else {
1905 // Render on one line2266 // Render on one line
1906 for (switch_case.ast.values) |value_expr| {2267 for (switch_case.ast.values) |value_expr| {
1907 try renderExpression(r, value_expr, .comma_space);2268 try renderExpression(r, value_expr, .comma_maybe_space);
1908 }2269 }
1909 }2270 }
19102271
1911 // Render the arrow and everything after it2272 try renderToken(r, switch_case.ast.arrow_token, .maybe_space); // =>
1912 const pre_target_space = if (tree.nodeTag(switch_case.ast.target_expr) == .multiline_string_literal)
1913 // Newline gets inserted when rendering the target expr.
1914 Space.none
1915 else
1916 Space.space;
1917 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
1918 try renderToken(r, switch_case.ast.arrow_token, after_arrow_space); // =>
19192273
1920 if (switch_case.payload_token) |payload_token| {2274 if (switch_case.payload_token) |payload_token| {
1921 try renderToken(r, payload_token - 1, .none); // pipe2275 try renderToken(r, payload_token - 1, .none); // pipe
1922 const ident = payload_token + @intFromBool(tree.tokenTag(payload_token) == .asterisk);2276 var ident = payload_token;
1923 if (tree.tokenTag(payload_token) == .asterisk) {2277 if (tree.tokenTag(ident) == .asterisk) {
1924 try renderToken(r, payload_token, .none); // asterisk2278 try renderToken(r, payload_token, .none); // asterisk
2279 ident += 1;
1925 }2280 }
1926 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier2281 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
1927 if (tree.tokenTag(ident + 1) == .comma) {2282 if (tree.tokenTag(ident + 1) == .comma) {
1928 try renderToken(r, ident + 1, .space); // ,2283 ident += 2;
1929 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // identifier2284 try renderToken(r, ident - 1, .space); // ,
1930 try renderToken(r, ident + 3, pre_target_space); // pipe2285 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
1931 } else {
1932 try renderToken(r, ident + 1, pre_target_space); // pipe
1933 }2286 }
2287 try renderToken(r, ident + 1, .maybe_space); // pipe
1934 }2288 }
19352289
1936 try renderExpression(r, switch_case.ast.target_expr, space);2290 try renderExpression(r, switch_case.ast.target_expr, space);
...@@ -2018,26 +2372,13 @@ fn renderStructInit(...@@ -2018,26 +2372,13 @@ fn renderStructInit(
2018 try ais.pushIndent(.normal);2372 try ais.pushIndent(.normal);
2019 try renderToken(r, struct_init.ast.lbrace, .newline);2373 try renderToken(r, struct_init.ast.lbrace, .newline);
20202374
2021 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .2375 for (0.., struct_init.ast.fields) |i, field_init| {
2022 try renderIdentifier(r, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
2023 // Don't output a space after the = if expression is a multiline string,
2024 // since then it will start on the next line.
2025 const field_node = struct_init.ast.fields[0];
2026 const expr = tree.nodeTag(field_node);
2027 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;
2028 try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =
2029
2030 try ais.pushSpace(.comma);
2031 try renderExpressionFixup(r, field_node, .comma);
2032 ais.popSpace();
2033
2034 for (struct_init.ast.fields[1..]) |field_init| {
2035 const init_token = tree.firstToken(field_init);2376 const init_token = tree.firstToken(field_init);
2036 try renderExtraNewlineToken(r, init_token - 3);2377 if (i != 0)
2378 try renderExtraNewlineToken(r, init_token - 3);
2037 try renderToken(r, init_token - 3, .none); // .2379 try renderToken(r, init_token - 3, .none); // .
2038 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name2380 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
2039 space_after_equal = if (tree.nodeTag(field_init) == .multiline_string_literal) .none else .space;2381 try renderToken(r, init_token - 1, .maybe_space); // =
2040 try renderToken(r, init_token - 1, space_after_equal); // =
20412382
2042 try ais.pushSpace(.comma);2383 try ais.pushSpace(.comma);
2043 try renderExpressionFixup(r, field_init, .comma);2384 try renderExpressionFixup(r, field_init, .comma);
...@@ -2053,8 +2394,7 @@ fn renderStructInit(...@@ -2053,8 +2394,7 @@ fn renderStructInit(
2053 const init_token = tree.firstToken(field_init);2394 const init_token = tree.firstToken(field_init);
2054 try renderToken(r, init_token - 3, .none); // .2395 try renderToken(r, init_token - 3, .none); // .
2055 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name2396 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
2056 const space_after_equal: Space = if (tree.nodeTag(field_init) == .multiline_string_literal) .none else .space;2397 try renderToken(r, init_token - 1, .maybe_space); // =
2057 try renderToken(r, init_token - 1, space_after_equal); // =
2058 try renderExpressionFixup(r, field_init, .comma_space);2398 try renderExpressionFixup(r, field_init, .comma_space);
2059 }2399 }
2060 }2400 }
...@@ -2122,174 +2462,176 @@ fn renderArrayInit(...@@ -2122,174 +2462,176 @@ fn renderArrayInit(
21222462
2123 try ais.pushIndent(.normal);2463 try ais.pushIndent(.normal);
2124 try renderToken(r, array_init.ast.lbrace, .newline);2464 try renderToken(r, array_init.ast.lbrace, .newline);
21252465 try ais.pushSpace(.comma);
2126 var expr_index: usize = 0;2466
2127 while (true) {2467 const expr_widths = try gpa.alloc(enum(usize) {
2128 const row_size = rowSize(tree, array_init.ast.elements[expr_index..], rbrace);2468 /// The expression contains non-printable characters (e.g. unicode / newlines)
2129 const row_exprs = array_init.ast.elements[expr_index..];2469 /// or has formatting disabled at the start or end.
2130 // A place to store the width of each expression and its column's maximum2470 nonprint = std.math.maxInt(usize),
2131 const widths = try gpa.alloc(usize, row_exprs.len + row_size);2471 _,
2132 defer gpa.free(widths);2472 }, array_init.ast.elements.len);
2133 @memset(widths, 0);2473 defer gpa.free(expr_widths);
21342474 {
2135 const expr_newlines = try gpa.alloc(bool, row_exprs.len);2475 var buf: Writer.Allocating = .init(gpa);
2136 defer gpa.free(expr_newlines);2476 defer buf.deinit();
2137 @memset(expr_newlines, false);2477 var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta);
21382478 sub_ais.disabled_offset = ais.disabled_offset;
2139 const expr_widths = widths[0..row_exprs.len];2479 defer sub_ais.deinit();
2140 const column_widths = widths[row_exprs.len..];2480 var sub_r: Render = .{
2141
2142 // Find next row with trailing comment (if any) to end the current section.
2143 const section_end = sec_end: {
2144 var this_line_first_expr: usize = 0;
2145 var this_line_size = rowSize(tree, row_exprs, rbrace);
2146 for (row_exprs, 0..) |expr, i| {
2147 // Ignore comment on first line of this section.
2148 if (i == 0) continue;
2149 const expr_last_token = tree.lastToken(expr);
2150 if (tree.tokensOnSameLine(tree.firstToken(row_exprs[0]), expr_last_token))
2151 continue;
2152 // Track start of line containing comment.
2153 if (!tree.tokensOnSameLine(tree.firstToken(row_exprs[this_line_first_expr]), expr_last_token)) {
2154 this_line_first_expr = i;
2155 this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rbrace);
2156 }
2157
2158 const maybe_comma = expr_last_token + 1;
2159 if (tree.tokenTag(maybe_comma) == .comma) {
2160 if (hasSameLineComment(tree, maybe_comma))
2161 break :sec_end i - this_line_size + 1;
2162 }
2163 }
2164 break :sec_end row_exprs.len;
2165 };
2166 expr_index += section_end;
2167
2168 const section_exprs = row_exprs[0..section_end];
2169
2170 var sub_expr_buffer: Writer.Allocating = .init(gpa);
2171 defer sub_expr_buffer.deinit();
2172
2173 const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);
2174 defer gpa.free(sub_expr_buffer_starts);
2175
2176 var auto_indenting_stream: AutoIndentingStream = .init(gpa, &sub_expr_buffer.writer, indent_delta);
2177 defer auto_indenting_stream.deinit();
2178 var sub_render: Render = .{
2179 .gpa = r.gpa,2481 .gpa = r.gpa,
2180 .ais = &auto_indenting_stream,2482 .ais = &sub_ais,
2181 .tree = r.tree,2483 .tree = r.tree,
2182 .fixups = r.fixups,2484 .fixups = r.fixups,
2183 };2485 };
21842486 for (array_init.ast.elements, expr_widths) |e, *width| {
2185 // Calculate size of columns in current section2487 const begin_disabled = sub_ais.disabled_offset != null;
2186 var column_counter: usize = 0;2488 // `.skip` space so trailing commments aren't included
2187 var single_line = true;2489 try renderExpressionComma(&sub_r, e, .skip);
2188 var contains_newline = false;2490 if (!begin_disabled and sub_ais.disabled_offset == null) {
2189 for (section_exprs, 0..) |expr, i| {2491 const w = buf.written();
2190 const start = sub_expr_buffer.written().len;2492 width.* = for (w) |c| {
2191 sub_expr_buffer_starts[i] = start;2493 if (!std.ascii.isPrint(c))
21922494 break .nonprint;
2193 if (i + 1 < section_exprs.len) {2495 } else @enumFromInt(w.len - @intFromBool(w[w.len - 1] == ','));
2194 try renderExpression(&sub_render, expr, .none);
2195 const written = sub_expr_buffer.written();
2196 const width = written.len - start;
2197 const this_contains_newline = mem.findScalar(u8, written[start..], '\n') != null;
2198 contains_newline = contains_newline or this_contains_newline;
2199 expr_widths[i] = width;
2200 expr_newlines[i] = this_contains_newline;
2201
2202 if (!this_contains_newline) {
2203 const column = column_counter % row_size;
2204 column_widths[column] = @max(column_widths[column], width);
2205
2206 const expr_last_token = tree.lastToken(expr) + 1;
2207 const next_expr = section_exprs[i + 1];
2208 column_counter += 1;
2209 if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(next_expr))) single_line = false;
2210 } else {
2211 single_line = false;
2212 column_counter = 0;
2213 }
2214 } else {2496 } else {
2215 try ais.pushSpace(.comma);2497 width.* = .nonprint;
2216 try renderExpression(&sub_render, expr, .comma);2498 }
2217 ais.popSpace();
22182499
2219 const written = sub_expr_buffer.written();2500 // Write trailing comments since they may enable/disable zig fmt
2220 const width = written.len - start - 2;2501 buf.clearRetainingCapacity();
2221 const this_contains_newline = mem.findScalar(u8, written[start .. written.len - 1], '\n') != null;2502 var after_expr = tree.lastToken(e);
2222 contains_newline = contains_newline or this_contains_newline;2503 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2223 expr_widths[i] = width;2504 try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none);
2224 expr_newlines[i] = contains_newline;
22252505
2226 if (!contains_newline) {2506 buf.clearRetainingCapacity();
2227 const column = column_counter % row_size;
2228 column_widths[column] = @max(column_widths[column], width);
2229 }
2230 }
2231 }2507 }
2232 sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.written().len;2508 }
2233
2234 // Render exprs in current section.
2235 column_counter = 0;
2236 for (section_exprs, 0..) |expr, i| {
2237 const start = sub_expr_buffer_starts[i];
2238 const end = sub_expr_buffer_starts[i + 1];
2239 const expr_text = sub_expr_buffer.written()[start..end];
2240 if (!expr_newlines[i]) {
2241 try ais.writeAll(expr_text);
2242 } else {
2243 var by_line = std.mem.splitScalar(u8, expr_text, '\n');
2244 var last_line_was_empty = false;
2245 try ais.writeAll(by_line.first());
2246 while (by_line.next()) |line| {
2247 if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) {
2248 try ais.insertNewline();
2249 } else {
2250 try ais.maybeInsertNewline();
2251 }
2252 last_line_was_empty = (line.len == 0);
2253 try ais.writeAll(line);
2254 }
2255 }
2256
2257 if (i + 1 < section_exprs.len) {
2258 const next_expr = section_exprs[i + 1];
2259 const comma = tree.lastToken(expr) + 1;
22602509
2261 if (column_counter != row_size - 1) {2510 var remaining_exprs = array_init.ast.elements;
2262 if (!expr_newlines[i] and !expr_newlines[i + 1]) {2511 var remaining_widths = expr_widths;
2263 // Neither the current or next expression is multiline2512 while (remaining_exprs.len != 0) {
2264 try renderToken(r, comma, .space); // ,2513 var row_size: usize = 1;
2265 assert(column_widths[column_counter % row_size] >= expr_widths[i]);2514 for (1.., remaining_exprs, remaining_widths) |len, e, w| {
2266 const padding = column_widths[column_counter % row_size] - expr_widths[i];2515 if (w == .nonprint) break;
2267 try ais.splatByteAll(' ', padding);2516 row_size = len;
22682517
2269 column_counter += 1;2518 var after_expr = tree.lastToken(e);
2270 continue;2519 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2271 }2520 assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
2272 }2521 if (!tree.tokensOnSameLine(after_expr, after_expr + 1))
2522 break;
2523 } else {
2524 // All the expressions are on the same line.
2525 // However, if there is a trailing comma, we put them each on their own line.
2526 if (tree.tokenTag(rbrace - 1) == .comma)
2527 row_size = 1;
2528 }
22732529
2274 if (single_line and row_size != 1) {2530 // Determine the size of this section
2275 try renderToken(r, comma, .space); // ,2531 const section_end = end: {
2276 continue;2532 var line_start = row_size; // Start after the first row to ignore comments on it
2533 break :end for (line_start.., remaining_exprs[line_start..]) |i, e| {
2534 const expr_first = tree.firstToken(e);
2535 // Any nonprint character terminates the line because they are always put on their
2536 // own line, so they will not end up on the same line as the trailing comment.
2537 if (expr_widths[i - 1] == .nonprint or !tree.tokensOnSameLine(expr_first - 1, expr_first)) {
2538 line_start = i;
2277 }2539 }
22782540
2279 column_counter = 0;2541 var after_expr = tree.lastToken(e);
2280 try renderToken(r, comma, .newline); // ,2542 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2281 try renderExtraNewline(r, next_expr);2543 assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
2544 if (hasTrailingComment(tree, after_expr))
2545 break line_start;
2546 } else remaining_exprs.len;
2547 };
2548 const section_exprs = remaining_exprs[0..section_end];
2549 const section_widths = remaining_widths[0..section_end];
2550 remaining_exprs = remaining_exprs[section_end..];
2551 remaining_widths = remaining_widths[section_end..];
2552
2553 // Determine the width of each column
2554 var col_widths = try gpa.alloc(usize, row_size);
2555 defer gpa.free(col_widths);
2556 @memset(col_widths, 0);
2557
2558 var col: usize = 0;
2559 for (section_widths) |w| {
2560 if (w == .nonprint) {
2561 col = 0;
2562 continue;
2563 }
2564 col_widths[col] = @max(col_widths[col], @intFromEnum(w));
2565 col += 1;
2566 if (col == row_size) {
2567 col = 0;
2282 }2568 }
2283 }2569 }
22842570
2285 if (expr_index == array_init.ast.elements.len)2571 // Render each expression
2286 break;2572 col = 0;
2573 for (0.., section_exprs, section_widths) |i, e, w| {
2574 if (i + 1 == section_end or col + 1 == row_size or
2575 w == .nonprint or section_widths[i + 1] == .nonprint)
2576 {
2577 try renderExpression(r, e, .comma);
2578 col = 0;
2579 if (i + 1 != section_end) {
2580 try renderExtraNewline(r, section_exprs[i + 1]);
2581 }
2582 } else {
2583 try renderExpression(r, e, .comma_space);
2584 try ais.splatByteAll(' ', col_widths[col] - @intFromEnum(w));
2585 col += 1;
2586 }
2587 }
2287 }2588 }
22882589
2590 ais.popSpace();
2289 ais.popIndent();2591 ais.popIndent();
2290 return renderToken(r, rbrace, space); // rbrace2592 return renderToken(r, rbrace, space); // rbrace
2291}2593}
22922594
2595fn isOneLineErrorSetDecl(
2596 tree: Ast,
2597 lbrace: Ast.TokenIndex,
2598 rbrace: Ast.TokenIndex,
2599) bool {
2600 // If there is a trailing comma, comment, or document comment, then render each
2601 // item on its own line.
2602 return tree.tokenTag(rbrace - 1) != .comma and
2603 !hasDocComment(tree, lbrace + 1, rbrace) and
2604 !hasComment(tree, lbrace, rbrace);
2605}
2606
2607fn isOneLineContainerDecl(
2608 tree: Ast,
2609 container_decl: Ast.full.ContainerDecl,
2610 lbrace: Ast.TokenIndex,
2611 rbrace: Ast.TokenIndex,
2612) bool {
2613 // We print all the members in one-line unless one of the following conditions are true:
2614
2615 // 1. The container has comments or multiline strings.
2616 if (hasComment(tree, lbrace, rbrace) or hasMultilineString(tree, lbrace, rbrace)) {
2617 return false;
2618 }
2619
2620 // 2. The container has a container comment.
2621 if (tree.tokenTag(lbrace + 1) == .container_doc_comment) return false;
2622
2623 // 3. A member of the container has a doc comment.
2624 if (hasDocComment(tree, lbrace + 1, rbrace))
2625 return false;
2626
2627 // 4. The container has non-field members.
2628 for (container_decl.ast.members) |member| {
2629 if (tree.fullContainerField(member) == null) return false;
2630 }
2631
2632 return true;
2633}
2634
2293fn renderContainerDecl(2635fn renderContainerDecl(
2294 r: *Render,2636 r: *Render,
2295 container_decl_node: Ast.Node.Index,2637 container_decl_node: Ast.Node.Index,
...@@ -2354,27 +2696,7 @@ fn renderContainerDecl(...@@ -2354,27 +2696,7 @@ fn renderContainerDecl(
2354 }2696 }
23552697
2356 const src_has_trailing_comma = tree.tokenTag(rbrace - 1) == .comma;2698 const src_has_trailing_comma = tree.tokenTag(rbrace - 1) == .comma;
2357 if (!src_has_trailing_comma) one_line: {2699 if (!src_has_trailing_comma and isOneLineContainerDecl(tree, container_decl, lbrace, rbrace)) {
2358 // We print all the members in-line unless one of the following conditions are true:
2359
2360 // 1. The container has comments or multiline strings.
2361 if (hasComment(tree, lbrace, rbrace) or hasMultilineString(tree, lbrace, rbrace)) {
2362 break :one_line;
2363 }
2364
2365 // 2. The container has a container comment.
2366 if (tree.tokenTag(lbrace + 1) == .container_doc_comment) break :one_line;
2367
2368 // 3. A member of the container has a doc comment.
2369 for (tree.tokens.items(.tag)[lbrace + 1 .. rbrace - 1]) |tag| {
2370 if (tag == .doc_comment) break :one_line;
2371 }
2372
2373 // 4. The container has non-field members.
2374 for (container_decl.ast.members) |member| {
2375 if (tree.fullContainerField(member) == null) break :one_line;
2376 }
2377
2378 // Print all the declarations on the same line.2700 // Print all the declarations on the same line.
2379 try renderToken(r, lbrace, .space); // lbrace2701 try renderToken(r, lbrace, .space); // lbrace
2380 for (container_decl.ast.members) |member| {2702 for (container_decl.ast.members) |member| {
...@@ -2436,7 +2758,7 @@ fn renderAsm(...@@ -2436,7 +2758,7 @@ fn renderAsm(
2436 const first_clobber = tree.firstToken(clobbers);2758 const first_clobber = tree.firstToken(clobbers);
2437 try renderToken(r, first_clobber - 3, .none);2759 try renderToken(r, first_clobber - 3, .none);
2438 try renderToken(r, first_clobber - 2, .none);2760 try renderToken(r, first_clobber - 2, .none);
2439 try renderToken(r, first_clobber - 1, .space);2761 try renderToken(r, first_clobber - 1, .maybe_space);
2440 try renderExpression(r, clobbers, .none);2762 try renderExpression(r, clobbers, .none);
2441 ais.popIndent();2763 ais.popIndent();
2442 return renderToken(r, asm_node.ast.rparen, space); // rparen2764 return renderToken(r, asm_node.ast.rparen, space); // rparen
...@@ -2450,6 +2772,9 @@ fn renderAsm(...@@ -2450,6 +2772,9 @@ fn renderAsm(
24502772
2451 try ais.forcePushIndent(.normal);2773 try ais.forcePushIndent(.normal);
2452 try renderExpression(r, asm_node.ast.template, .newline);2774 try renderExpression(r, asm_node.ast.template, .newline);
2775 ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2776 assert(ais.current_line_empty);
2777
2453 ais.setIndentDelta(asm_indent_delta);2778 ais.setIndentDelta(asm_indent_delta);
2454 const colon1 = tree.lastToken(asm_node.ast.template) + 1;2779 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
24552780
...@@ -2527,9 +2852,10 @@ fn renderAsm(...@@ -2527,9 +2852,10 @@ fn renderAsm(
2527 unreachable;2852 unreachable;
2528 };2853 };
25292854
2530 try renderToken(r, colon3, .space); // :2855 try renderToken(r, colon3, .maybe_space); // :
2531 const clobbers = asm_node.ast.clobbers.unwrap().?;2856 const clobbers = asm_node.ast.clobbers.unwrap().?;
2532 try renderExpression(r, clobbers, .none);2857 try renderExpression(r, clobbers, .none);
2858 ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2533 ais.setIndentDelta(indent_delta);2859 ais.setIndentDelta(indent_delta);
2534 ais.popIndent();2860 ais.popIndent();
2535 return renderToken(r, asm_node.ast.rparen, space); // rparen2861 return renderToken(r, asm_node.ast.rparen, space); // rparen
...@@ -2555,7 +2881,7 @@ fn renderParamList(...@@ -2555,7 +2881,7 @@ fn renderParamList(
25552881
2556 if (params.len == 0) {2882 if (params.len == 0) {
2557 try ais.pushIndent(.normal);2883 try ais.pushIndent(.normal);
2558 try renderToken(r, lparen, .none);2884 try renderToken(r, lparen, .none); // (
2559 ais.popIndent();2885 ais.popIndent();
2560 return renderToken(r, lparen + 1, space); // )2886 return renderToken(r, lparen + 1, space); // )
2561 }2887 }
...@@ -2590,10 +2916,7 @@ fn renderParamList(...@@ -2590,10 +2916,7 @@ fn renderParamList(
25902916
2591 if (i + 1 < params.len) {2917 if (i + 1 < params.len) {
2592 const comma = tree.lastToken(param_node) + 1;2918 const comma = tree.lastToken(param_node) + 1;
2593 const next_multiline_string =2919 try renderToken(r, comma, .maybe_space);
2594 tree.tokenTag(tree.firstToken(params[i + 1])) == .multiline_string_literal_line;
2595 const comma_space: Space = if (next_multiline_string) .none else .space;
2596 try renderToken(r, comma, comma_space);
2597 }2920 }
2598 }2921 }
2599 ais.popIndent();2922 ais.popIndent();
...@@ -2655,6 +2978,13 @@ const Space = enum {...@@ -2655,6 +2978,13 @@ const Space = enum {
2655 /// Additionally consume the next token if it is a semicolon.2978 /// Additionally consume the next token if it is a semicolon.
2656 /// In either case, a newline will be inserted afterwards.2979 /// In either case, a newline will be inserted afterwards.
2657 semicolon,2980 semicolon,
2981 /// If the next token is not a multiline string literal, this acts as .space,
2982 /// otherwise this acts as .none.
2983 maybe_space,
2984 /// Additionally consume the next token if it is a comma.
2985 /// In either case, a space will be inserted afterwards
2986 /// if the following token is not a multiline string literal.
2987 comma_maybe_space,
2658 /// Skip rendering whitespace and comments. If this is used, the caller2988 /// Skip rendering whitespace and comments. If this is used, the caller
2659 /// *must* handle whitespace and comments manually.2989 /// *must* handle whitespace and comments manually.
2660 skip,2990 skip,
...@@ -2719,6 +3049,16 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space...@@ -2719,6 +3049,16 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
2719 try ais.insertNewline();3049 try ais.insertNewline();
2720 },3050 },
27213051
3052 .maybe_space => if (!comment and next_token_tag != .multiline_string_literal_line) {
3053 try ais.writeByte(' ');
3054 },
3055
3056 .comma_maybe_space => if (next_token_tag == .comma) {
3057 try renderToken(r, token_index + 1, .maybe_space);
3058 } else if (!comment) {
3059 try ais.writeByte(' ');
3060 },
3061
2722 .skip => unreachable,3062 .skip => unreachable,
2723 }3063 }
2724}3064}
...@@ -2727,9 +3067,9 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {...@@ -2727,9 +3067,9 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {
2727 const ais = r.ais;3067 const ais = r.ais;
2728 switch (space) {3068 switch (space) {
2729 .none => {},3069 .none => {},
2730 .space => try ais.writeByte(' '),3070 .space, .maybe_space => try ais.writeByte(' '),
2731 .newline => try ais.insertNewline(),3071 .newline => try ais.insertNewline(),
2732 .comma => try ais.writeAll(",\n"),3072 .comma, .comma_maybe_space => try ais.writeAll(",\n"),
2733 .comma_space => try ais.writeAll(", "),3073 .comma_space => try ais.writeAll(", "),
2734 .semicolon => try ais.writeAll(";\n"),3074 .semicolon => try ais.writeAll(";\n"),
2735 .skip => unreachable,3075 .skip => unreachable,
...@@ -2794,7 +3134,7 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote...@@ -2794,7 +3134,7 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote
2794 },3134 },
2795 .failure => return renderQuotedIdentifier(r, token_index, space, false),3135 .failure => return renderQuotedIdentifier(r, token_index, space, false),
2796 }3136 }
2797 contents_i += esc_offset;3137 contents_i = esc_offset;
2798 continue;3138 continue;
2799 },3139 },
2800 else => return renderQuotedIdentifier(r, token_index, space, false),3140 else => return renderQuotedIdentifier(r, token_index, space, false),
...@@ -2884,6 +3224,9 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void...@@ -2884,6 +3224,9 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void
2884 }3224 }
2885 },3225 },
2886 .failure => {3226 .failure => {
3227 // Escape the stray backslash
3228 // This also avoids cases like "\x3\x39" becoming "\x39"
3229 try ais.writeByte('\\');
2887 try ais.writeAll(escape_sequence);3230 try ais.writeAll(escape_sequence);
2888 },3231 },
2889 }3232 }
...@@ -2910,7 +3253,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex)...@@ -2910,7 +3253,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex)
2910 const token: Ast.TokenIndex = @intCast(i);3253 const token: Ast.TokenIndex = @intCast(i);
2911 const start = tree.tokenStart(token) + tree.tokenSlice(token).len;3254 const start = tree.tokenStart(token) + tree.tokenSlice(token).len;
2912 const end = tree.tokenStart(token + 1);3255 const end = tree.tokenStart(token + 1);
2913 if (mem.find(u8, tree.source[start..end], "//") != null) return true;3256 if (mem.findScalar(u8, tree.source[start..end], '/') != null) return true;
2914 }3257 }
29153258
2916 return false;3259 return false;
...@@ -2926,6 +3269,16 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok...@@ -2926,6 +3269,16 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok
2926 ) != null;3269 ) != null;
2927}3270}
29283271
3272/// Returns true if there exists a doc comment between the start
3273/// of token `start_token` and the start of token `end_token`.
3274fn hasDocComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
3275 return std.mem.indexOfScalar(
3276 Token.Tag,
3277 tree.tokens.items(.tag)[start_token..end_token],
3278 .doc_comment,
3279 ) != null;
3280}
3281
2929/// Assumes that start is the first byte past the previous token and3282/// Assumes that start is the first byte past the previous token and
2930/// that end is the last byte before the next token.3283/// that end is the last byte before the next token.
2931fn renderComments(r: *Render, start: usize, end: usize) Error!bool {3284fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
...@@ -3095,25 +3448,10 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 {...@@ -3095,25 +3448,10 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 {
3095 return ret;3448 return ret;
3096}3449}
30973450
3098fn writeStringLiteralAsIdentifier(r: *Render, token_index: Ast.TokenIndex) !usize {3451fn hasTrailingComment(tree: Ast, t: Ast.TokenIndex) bool {
3099 const tree = r.tree;3452 const start = tree.tokenStart(t) + tree.tokenSlice(t).len;
3100 const ais = r.ais;3453 const between = tree.source[start..tree.tokenStart(t + 1)];
3101 assert(tree.tokenTag(token_index) == .string_literal);3454 for (between) |byte| switch (byte) {
3102 const lexeme = tokenSliceForRender(tree, token_index);
3103 const unquoted = lexeme[1..][0 .. lexeme.len - 2];
3104 if (std.zig.isValidId(unquoted)) {
3105 try ais.writeAll(unquoted);
3106 return unquoted.len;
3107 } else {
3108 try ais.writeByte('@');
3109 try ais.writeAll(lexeme);
3110 return lexeme.len + 1;
3111 }
3112}
3113
3114fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {
3115 const between_source = tree.source[tree.tokenStart(token_index)..tree.tokenStart(token_index + 1)];
3116 for (between_source) |byte| switch (byte) {
3117 '\n' => return false,3455 '\n' => return false,
3118 '/' => return true,3456 '/' => return true,
3119 else => continue,3457 else => continue,
...@@ -3125,12 +3463,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {...@@ -3125,12 +3463,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {
3125/// start_token and end_token.3463/// start_token and end_token.
3126fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {3464fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
3127 if (start_token + 1 != end_token) return true;3465 if (start_token + 1 != end_token) return true;
3128 const between_source = tree.source[tree.tokenStart(start_token)..tree.tokenStart(start_token + 1)];3466 return hasComment(tree, start_token, end_token);
3129 for (between_source) |byte| switch (byte) {
3130 '/' => return true,
3131 else => continue,
3132 };
3133 return false;
3134}3467}
31353468
3136fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {3469fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {
...@@ -3217,29 +3550,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -3217,29 +3550,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
3217 };3550 };
3218}3551}
32193552
3220// Returns the number of nodes in `exprs` that are on the same line as `rtoken`.
3221fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usize {
3222 const first_token = tree.firstToken(exprs[0]);
3223 if (tree.tokensOnSameLine(first_token, rtoken)) {
3224 const maybe_comma = rtoken - 1;
3225 if (tree.tokenTag(maybe_comma) == .comma)
3226 return 1;
3227 return exprs.len; // no newlines
3228 }
3229
3230 var count: usize = 1;
3231 for (exprs, 0..) |expr, i| {
3232 if (i + 1 < exprs.len) {
3233 const expr_last_token = tree.lastToken(expr) + 1;
3234 if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count;
3235 count += 1;
3236 } else {
3237 return count;
3238 }
3239 }
3240 unreachable;
3241}
3242
3243/// Automatically inserts indentation of written data by keeping3553/// Automatically inserts indentation of written data by keeping
3244/// track of the current indentation level3554/// track of the current indentation level
3245///3555///
...@@ -3477,11 +3787,19 @@ const AutoIndentingStream = struct {...@@ -3477,11 +3787,19 @@ const AutoIndentingStream = struct {
34773787
3478 pub fn popIndent(ais: *AutoIndentingStream) void {3788 pub fn popIndent(ais: *AutoIndentingStream) void {
3479 if (ais.indent_stack.pop().?.realized) {3789 if (ais.indent_stack.pop().?.realized) {
3480 assert(ais.indent_count > 0);
3481 ais.indent_count -= 1;3790 ais.indent_count -= 1;
3482 }3791 }
3483 }3792 }
34843793
3794 /// Forces the last pushed indent to be realized
3795 pub fn forceLastIndent(ais: *AutoIndentingStream) void {
3796 const top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
3797 if (!top.realized) {
3798 top.realized = true;
3799 ais.indent_count += 1;
3800 }
3801 }
3802
3485 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {3803 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {
3486 return ais.indent_stack.items.len == 0;3804 return ais.indent_stack.items.len == 0;
3487 }3805 }
...@@ -3489,8 +3807,8 @@ const AutoIndentingStream = struct {...@@ -3489,8 +3807,8 @@ const AutoIndentingStream = struct {
3489 /// Writes ' ' bytes if the current line is empty3807 /// Writes ' ' bytes if the current line is empty
3490 fn applyIndent(ais: *AutoIndentingStream) Error!void {3808 fn applyIndent(ais: *AutoIndentingStream) Error!void {
3491 const current_indent = ais.currentIndent();3809 const current_indent = ais.currentIndent();
3492 if (ais.current_line_empty and current_indent > 0) {3810 if (ais.current_line_empty) {
3493 if (ais.disabled_offset == null) {3811 if (current_indent > 0 and ais.disabled_offset == null) {
3494 try ais.underlying_writer.splatByteAll(' ', current_indent);3812 try ais.underlying_writer.splatByteAll(' ', current_indent);
3495 }3813 }
3496 ais.applied_indent = current_indent;3814 ais.applied_indent = current_indent;
lib/std/zig/BuiltinFn.zig+10-5
...@@ -384,28 +384,32 @@ pub const list = list: {...@@ -384,28 +384,32 @@ pub const list = list: {
384 },384 },
385 },385 },
386 .{386 .{
387 "@cVaArg", .{387 "@cVaArg",
388 .{
388 .tag = .c_va_arg,389 .tag = .c_va_arg,
389 .param_count = 2,390 .param_count = 2,
390 .illegal_outside_function = true,391 .illegal_outside_function = true,
391 },392 },
392 },393 },
393 .{394 .{
394 "@cVaCopy", .{395 "@cVaCopy",
396 .{
395 .tag = .c_va_copy,397 .tag = .c_va_copy,
396 .param_count = 1,398 .param_count = 1,
397 .illegal_outside_function = true,399 .illegal_outside_function = true,
398 },400 },
399 },401 },
400 .{402 .{
401 "@cVaEnd", .{403 "@cVaEnd",
404 .{
402 .tag = .c_va_end,405 .tag = .c_va_end,
403 .param_count = 1,406 .param_count = 1,
404 .illegal_outside_function = true,407 .illegal_outside_function = true,
405 },408 },
406 },409 },
407 .{410 .{
408 "@cVaStart", .{411 "@cVaStart",
412 .{
409 .tag = .c_va_start,413 .tag = .c_va_start,
410 .param_count = 0,414 .param_count = 0,
411 .illegal_outside_function = true,415 .illegal_outside_function = true,
...@@ -1042,7 +1046,8 @@ pub const list = list: {...@@ -1042,7 +1046,8 @@ pub const list = list: {
1042 },1046 },
1043 },1047 },
1044 .{1048 .{
1045 "@workItemId", .{1049 "@workItemId",
1050 .{
1046 .tag = .work_item_id,1051 .tag = .work_item_id,
1047 .param_count = 1,1052 .param_count = 1,
1048 .illegal_outside_function = true,1053 .illegal_outside_function = true,
lib/std/zig/Parse.zig+25-13
...@@ -885,7 +885,8 @@ fn expectContainerField(p: *Parse) !Node.Index {...@@ -885,7 +885,8 @@ fn expectContainerField(p: *Parse) !Node.Index {
885 .tag = .container_field,885 .tag = .container_field,
886 .main_token = main_token,886 .main_token = main_token,
887 .data = .{ .node_and_extra = .{887 .data = .{ .node_and_extra = .{
888 type_expr, try p.addExtra(Node.ContainerField{888 type_expr,
889 try p.addExtra(Node.ContainerField{
889 .align_expr = align_expr.?,890 .align_expr = align_expr.?,
890 .value_expr = value_expr.?,891 .value_expr = value_expr.?,
891 }),892 }),
...@@ -919,11 +920,15 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {...@@ -919,11 +920,15 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
919 } else {920 } else {
920 const assign = try p.expectAssignExpr();921 const assign = try p.expectAssignExpr();
921 try p.expectSemicolon(.expected_semi_after_stmt, true);922 try p.expectSemicolon(.expected_semi_after_stmt, true);
922 return p.addNode(.{923 if (p.nodeTag(assign) != .assign_destructure) {
923 .tag = .@"comptime",924 return p.addNode(.{
924 .main_token = comptime_token,925 .tag = .@"comptime",
925 .data = .{ .node = assign },926 .main_token = comptime_token,
926 });927 .data = .{ .node = assign },
928 });
929 } else {
930 return assign;
931 }
927 }932 }
928 }933 }
929934
...@@ -1184,7 +1189,8 @@ fn expectIfStatement(p: *Parse) !Node.Index {...@@ -1184,7 +1189,8 @@ fn expectIfStatement(p: *Parse) !Node.Index {
1184 .tag = .@"if",1189 .tag = .@"if",
1185 .main_token = if_token,1190 .main_token = if_token,
1186 .data = .{ .node_and_extra = .{1191 .data = .{ .node_and_extra = .{
1187 condition, try p.addExtra(Node.If{1192 condition,
1193 try p.addExtra(Node.If{
1188 .then_expr = then_expr,1194 .then_expr = then_expr,
1189 .else_expr = else_expr,1195 .else_expr = else_expr,
1190 }),1196 }),
...@@ -1365,7 +1371,8 @@ fn parseWhileStatement(p: *Parse) !?Node.Index {...@@ -1365,7 +1371,8 @@ fn parseWhileStatement(p: *Parse) !?Node.Index {
1365 .tag = .@"while",1371 .tag = .@"while",
1366 .main_token = while_token,1372 .main_token = while_token,
1367 .data = .{ .node_and_extra = .{1373 .data = .{ .node_and_extra = .{
1368 condition, try p.addExtra(Node.While{1374 condition,
1375 try p.addExtra(Node.While{
1369 .cont_expr = .fromOptional(cont_expr),1376 .cont_expr = .fromOptional(cont_expr),
1370 .then_expr = then_expr,1377 .then_expr = then_expr,
1371 .else_expr = else_expr,1378 .else_expr = else_expr,
...@@ -1933,7 +1940,8 @@ fn parseTypeExpr(p: *Parse) Error!?Node.Index {...@@ -1933,7 +1940,8 @@ fn parseTypeExpr(p: *Parse) Error!?Node.Index {
1933 .tag = .array_type_sentinel,1940 .tag = .array_type_sentinel,
1934 .main_token = lbracket,1941 .main_token = lbracket,
1935 .data = .{ .node_and_extra = .{1942 .data = .{ .node_and_extra = .{
1936 len_expr.?, try p.addExtra(Node.ArrayTypeSentinel{1943 len_expr.?,
1944 try p.addExtra(Node.ArrayTypeSentinel{
1937 .sentinel = sentinel.?,1945 .sentinel = sentinel.?,
1938 .elem_type = elem_type,1946 .elem_type = elem_type,
1939 }),1947 }),
...@@ -2728,7 +2736,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {...@@ -2728,7 +2736,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {
2728 .tag = .while_cont,2736 .tag = .while_cont,
2729 .main_token = while_token,2737 .main_token = while_token,
2730 .data = .{ .node_and_extra = .{2738 .data = .{ .node_and_extra = .{
2731 condition, try p.addExtra(Node.WhileCont{2739 condition,
2740 try p.addExtra(Node.WhileCont{
2732 .cont_expr = cont_expr.?,2741 .cont_expr = cont_expr.?,
2733 .then_expr = then_expr,2742 .then_expr = then_expr,
2734 }),2743 }),
...@@ -2742,7 +2751,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {...@@ -2742,7 +2751,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {
2742 .tag = .@"while",2751 .tag = .@"while",
2743 .main_token = while_token,2752 .main_token = while_token,
2744 .data = .{ .node_and_extra = .{2753 .data = .{ .node_and_extra = .{
2745 condition, try p.addExtra(Node.While{2754 condition,
2755 try p.addExtra(Node.While{
2746 .cont_expr = .fromOptional(cont_expr),2756 .cont_expr = .fromOptional(cont_expr),
2747 .then_expr = then_expr,2757 .then_expr = then_expr,
2748 .else_expr = else_expr,2758 .else_expr = else_expr,
...@@ -3194,7 +3204,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {...@@ -3194,7 +3204,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {
3194 .tag = .slice_sentinel,3204 .tag = .slice_sentinel,
3195 .main_token = lbracket,3205 .main_token = lbracket,
3196 .data = .{ .node_and_extra = .{3206 .data = .{ .node_and_extra = .{
3197 lhs, try p.addExtra(Node.SliceSentinel{3207 lhs,
3208 try p.addExtra(Node.SliceSentinel{
3198 .start = index_expr,3209 .start = index_expr,
3199 .end = .fromOptional(opt_end_expr),3210 .end = .fromOptional(opt_end_expr),
3200 .sentinel = sentinel,3211 .sentinel = sentinel,
...@@ -3217,7 +3228,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {...@@ -3217,7 +3228,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {
3217 .tag = .slice,3228 .tag = .slice,
3218 .main_token = lbracket,3229 .main_token = lbracket,
3219 .data = .{ .node_and_extra = .{3230 .data = .{ .node_and_extra = .{
3220 lhs, try p.addExtra(Node.Slice{3231 lhs,
3232 try p.addExtra(Node.Slice{
3221 .start = index_expr,3233 .start = index_expr,
3222 .end = end_expr,3234 .end = end_expr,
3223 }),3235 }),
lib/std/zig/parser_test.zig+1095-8
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const Io = std.Io;2const Io = std.Io;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const Token = std.zig.Token;
45
5test "zig fmt: remove extra whitespace at start and end of file with comment between" {6test "zig fmt: remove extra whitespace at start and end of file with comment between" {
6 try testTransform(7 try testTransform(
...@@ -1329,12 +1330,30 @@ test "zig fmt: comment to disable/enable zig fmt" {...@@ -1329,12 +1330,30 @@ test "zig fmt: comment to disable/enable zig fmt" {
1329 \\const c = d;1330 \\const c = d;
1330 \\// zig fmt: on1331 \\// zig fmt: on
1331 \\const e = f;1332 \\const e = f;
1333 \\const g = .{
1334 \\ h, i,
1335 \\ // zig fmt: off
1336 \\ j,
1337 \\ k,
1338 \\ // zig fmt: on
1339 \\ l, m, n, o,
1340 \\};
1341 \\
1332 ,1342 ,
1333 \\const a = b;1343 \\const a = b;
1334 \\// zig fmt: off1344 \\// zig fmt: off
1335 \\const c = d;1345 \\const c = d;
1336 \\// zig fmt: on1346 \\// zig fmt: on
1337 \\const e = f;1347 \\const e = f;
1348 \\const g = .{
1349 \\ h, i,
1350 \\ // zig fmt: off
1351 \\ j,
1352 \\ k,
1353 \\ // zig fmt: on
1354 \\ l, m,
1355 \\ n, o,
1356 \\};
1338 \\1357 \\
1339 );1358 );
1340}1359}
...@@ -1986,6 +2005,38 @@ test "zig fmt: array literal vertical column alignment" {...@@ -1986,6 +2005,38 @@ test "zig fmt: array literal vertical column alignment" {
1986 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };2005 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
1987 \\const a = [12]u8{2006 \\const a = [12]u8{
1988 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, };2007 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, };
2008 \\const a = .{
2009 \\ 1, \\
2010 \\ , 2,
2011 \\ 3,
2012 \\};
2013 \\const a = .{
2014 \\ \\
2015 \\ , 1, 2,
2016 \\ 3,
2017 \\};
2018 \\const a = .{
2019 \\ {{}}, 1,
2020 \\ 2, 3,
2021 \\};
2022 \\const a = .{
2023 \\ a, bb //
2024 \\ , ccc, dddd,
2025 \\};
2026 \\const a = .{
2027 \\ "a", "b", "ä", "a", "123",
2028 \\};
2029 \\const a = .{
2030 \\ a, a, .{
2031 \\ // zig fmt: off
2032 \\ },
2033 \\ a*a, a,
2034 \\ .{
2035 \\ // zig fmt: on
2036 \\ }, aa,
2037 \\ // zig fmt: off
2038 \\ a*a,
2039 \\};
1989 \\2040 \\
1990 ,2041 ,
1991 \\const a = []u8{2042 \\const a = []u8{
...@@ -2014,6 +2065,53 @@ test "zig fmt: array literal vertical column alignment" {...@@ -2014,6 +2065,53 @@ test "zig fmt: array literal vertical column alignment" {
2014 \\ 30,2065 \\ 30,
2015 \\ 31,2066 \\ 31,
2016 \\};2067 \\};
2068 \\const a = .{
2069 \\ 1,
2070 \\ \\
2071 \\ ,
2072 \\ 2,
2073 \\ 3,
2074 \\};
2075 \\const a = .{
2076 \\ \\
2077 \\ ,
2078 \\ 1,
2079 \\ 2,
2080 \\ 3,
2081 \\};
2082 \\const a = .{
2083 \\ {
2084 \\ {}
2085 \\ },
2086 \\ 1,
2087 \\ 2,
2088 \\ 3,
2089 \\};
2090 \\const a = .{
2091 \\ a,
2092 \\ bb //
2093 \\ ,
2094 \\ ccc,
2095 \\ dddd,
2096 \\};
2097 \\const a = .{
2098 \\ "a", "b",
2099 \\ "ä",
2100 \\ "a", "123",
2101 \\};
2102 \\const a = .{
2103 \\ a, a,
2104 \\ .{
2105 \\ // zig fmt: off
2106 \\ },
2107 \\ a*a, a,
2108 \\ .{
2109 \\ // zig fmt: on
2110 \\ },
2111 \\ aa,
2112 \\ // zig fmt: off
2113 \\ a*a,
2114 \\};
2017 \\2115 \\
2018 );2116 );
2019}2117}
...@@ -2542,6 +2640,19 @@ test "zig fmt: first line comment in struct initializer" {...@@ -2542,6 +2640,19 @@ test "zig fmt: first line comment in struct initializer" {
2542 );2640 );
2543}2641}
25442642
2643test "zig fmt: multiline string literals in struct initializer" {
2644 try testTransform(
2645 \\const a = .{ .a = \\
2646 \\+ 1};
2647 \\
2648 ,
2649 \\const a = .{ .a =
2650 \\ \\
2651 \\+ 1 };
2652 \\
2653 );
2654}
2655
2545test "zig fmt: doc comments before struct field" {2656test "zig fmt: doc comments before struct field" {
2546 try testCanonical(2657 try testCanonical(
2547 \\pub const Allocator = struct {2658 \\pub const Allocator = struct {
...@@ -2872,6 +2983,7 @@ test "zig fmt: destructure" {...@@ -2872,6 +2983,7 @@ test "zig fmt: destructure" {
2872 \\ comptime w, var y = .{ 3, 4 };2983 \\ comptime w, var y = .{ 3, 4 };
2873 \\ comptime var z, x = .{ 5, 6 };2984 \\ comptime var z, x = .{ 5, 6 };
2874 \\ comptime y, z = .{ 7, 8 };2985 \\ comptime y, z = .{ 7, 8 };
2986 \\ if (false) unreachable else comptime a, b = .{ 9, 10 };
2875 \\}2987 \\}
2876 \\2988 \\
2877 );2989 );
...@@ -4004,6 +4116,18 @@ test "zig fmt: multiline string in array" {...@@ -4004,6 +4116,18 @@ test "zig fmt: multiline string in array" {
4004 \\}4116 \\}
4005 \\4117 \\
4006 );4118 );
4119
4120 try testTransform(
4121 \\const a = .{ k, \\
4122 \\};
4123 \\
4124 ,
4125 \\const a = .{
4126 \\ k,
4127 \\ \\
4128 \\};
4129 \\
4130 );
4007}4131}
40084132
4009test "zig fmt: if type expr" {4133test "zig fmt: if type expr" {
...@@ -4786,8 +4910,8 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -4786,8 +4910,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
4786 \\ 0,4910 \\ 0,
4787 \\ }}}}}}}};4911 \\ }}}}}}}};
4788 \\ myFunc(.{4912 \\ myFunc(.{
4789 \\ "aaaaaaa", "bbbbbb", "ccccc",4913 \\ "aaaaaaa", "bbbbbb", "ccccc",
4790 \\ "dddd", ("eee"), ("fff"),4914 \\ "dddd", ("eee"), ("fff"),
4791 \\ ("gggg"),4915 \\ ("gggg"),
4792 \\ // Line comment4916 \\ // Line comment
4793 \\ \\Multiline String Literals can be quite long4917 \\ \\Multiline String Literals can be quite long
...@@ -4816,11 +4940,9 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -4816,11 +4940,9 @@ test "zig fmt: multiline string literals should play nice with array initializer
4816 \\ (4940 \\ (
4817 \\ \\ xxx4941 \\ \\ xxx
4818 \\ ),4942 \\ ),
4819 \\ "xxx",4943 \\ "xxx", "xxx",
4820 \\ "xxx",
4821 \\ },4944 \\ },
4822 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },4945 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4823 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4824 \\ "aaaaaaa", "bbbbbb", "ccccc", // -4946 \\ "aaaaaaa", "bbbbbb", "ccccc", // -
4825 \\ "dddd", ("eee"), ("fff"),4947 \\ "dddd", ("eee"), ("fff"),
4826 \\ .{4948 \\ .{
...@@ -4828,8 +4950,7 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -4828,8 +4950,7 @@ test "zig fmt: multiline string literals should play nice with array initializer
4828 \\ (4950 \\ (
4829 \\ \\ xxx4951 \\ \\ xxx
4830 \\ ),4952 \\ ),
4831 \\ "xxxxxxxxxxxxxx",4953 \\ "xxxxxxxxxxxxxx", "xxx",
4832 \\ "xxx",
4833 \\ },4954 \\ },
4834 \\ .{4955 \\ .{
4835 \\ (4956 \\ (
...@@ -6028,6 +6149,695 @@ test "zig fmt: extern addrspace in struct" {...@@ -6028,6 +6149,695 @@ test "zig fmt: extern addrspace in struct" {
6028 );6149 );
6029}6150}
60306151
6152test "zig fmt: seperate errors in error sets with comments" {
6153 try testTransform(
6154 \\error{
6155 \\ /// This error is very bad!
6156 \\ A, B}
6157 \\
6158 ,
6159 \\error{
6160 \\ /// This error is very bad!
6161 \\ A,
6162 \\ B,
6163 \\}
6164 \\
6165 );
6166
6167 try testTransform(
6168 \\error{
6169 \\ A, B
6170 \\ // something important
6171 \\}
6172 \\
6173 ,
6174 \\error{
6175 \\ A,
6176 \\ B,
6177 \\ // something important
6178 \\}
6179 \\
6180 );
6181}
6182
6183test "zig fmt: proper escape checks" {
6184 try testTransform(
6185 \\@"\x41\x42\!"
6186 \\
6187 ,
6188 \\@"AB\\!"
6189 \\
6190 );
6191}
6192
6193test "zig fmt: field accesses on number literals" {
6194 try testCanonical(
6195 \\const a = 0xF .A;
6196 \\const a = 0xF
6197 \\ .A;
6198 \\
6199 );
6200}
6201
6202test "zig fmt: array indent when inner becomes multi-line" {
6203 try testTransform(
6204 \\const access_block = x[{{}}];
6205 \\
6206 \\const block = [{{}}]T;
6207 \\const sentinel_block = [15:{{}}]T;
6208 \\const container = [enum { A, }]T;
6209 \\const container_arg = [union({{}}) {}]T;
6210 \\const error_set = [error{ A, }]T;
6211 \\const @"switch" = [switch (m) { 0 => {}, }]T;
6212 \\const switch_op = [switch ({{}}) {}]T;
6213 \\const for_capture = [for (a) |_,| {}]T;
6214 \\const for_expr = [for ({{}}) |_| {}]T;
6215 \\const for_range = [for ({{}}..15) |_| {}]T;
6216 \\const for_input_comma = [for (0..15,) |_| {}]T;
6217 \\const call_param = [a({{}})]T;
6218 \\const call_fn = [({{}})()]T;
6219 \\const builtin_call = [@log2({{}})]T;
6220 \\const array_init = [.{{{}}}]T;
6221 \\const struct_init = [.{.x = {{}}}]T;
6222 \\const @"asm" = [asm ("" : [x] "" (-> T))]T;
6223 \\const asm_template = [asm ({{}})]T;
6224 \\const asm_clobbers = [asm ("" ::: {{}})]T;
6225 \\const @"fn" = [fn (({{}})) void]T;
6226 \\const array_type_len = [[{{}}]T]T;
6227 \\const array_type_type = [[1]({{}})]T;
6228 \\const array_access_array = [({{}})[1]]T;
6229 \\const array_access_index = [x[{{}}]]T;
6230 \\const binop = [1 + {{}}]T;
6231 \\const unaryop = [!{{}}]T;
6232 \\const destructure = [while (true) : (x, {{}} = z) {}]T;
6233 \\
6234 ,
6235 \\const access_block = x[
6236 \\ {
6237 \\ {}
6238 \\ }
6239 \\];
6240 \\
6241 \\const block = [
6242 \\ {
6243 \\ {}
6244 \\ }
6245 \\]T;
6246 \\const sentinel_block = [
6247 \\ 15
6248 \\ :
6249 \\ {
6250 \\ {}
6251 \\ }
6252 \\]T;
6253 \\const container = [
6254 \\ enum {
6255 \\ A,
6256 \\ }
6257 \\]T;
6258 \\const container_arg = [
6259 \\ union({
6260 \\ {}
6261 \\ }) {}
6262 \\]T;
6263 \\const error_set = [
6264 \\ error{
6265 \\ A,
6266 \\ }
6267 \\]T;
6268 \\const @"switch" = [
6269 \\ switch (m) {
6270 \\ 0 => {},
6271 \\ }
6272 \\]T;
6273 \\const switch_op = [
6274 \\ switch ({
6275 \\ {}
6276 \\ }) {}
6277 \\]T;
6278 \\const for_capture = [
6279 \\ for (a) |
6280 \\ _,
6281 \\ | {}
6282 \\]T;
6283 \\const for_expr = [
6284 \\ for ({
6285 \\ {}
6286 \\ }) |_| {}
6287 \\]T;
6288 \\const for_range = [
6289 \\ for ({
6290 \\ {}
6291 \\ }..15) |_| {}
6292 \\]T;
6293 \\const for_input_comma = [
6294 \\ for (
6295 \\ 0..15,
6296 \\ ) |_| {}
6297 \\]T;
6298 \\const call_param = [
6299 \\ a({
6300 \\ {}
6301 \\ })
6302 \\]T;
6303 \\const call_fn = [
6304 \\ ({
6305 \\ {}
6306 \\ })()
6307 \\]T;
6308 \\const builtin_call = [
6309 \\ @log2({
6310 \\ {}
6311 \\ })
6312 \\]T;
6313 \\const array_init = [
6314 \\ .{{
6315 \\ {}
6316 \\ }}
6317 \\]T;
6318 \\const struct_init = [
6319 \\ .{ .x = {
6320 \\ {}
6321 \\ } }
6322 \\]T;
6323 \\const @"asm" = [
6324 \\ asm (""
6325 \\ : [x] "" (-> T),
6326 \\ )
6327 \\]T;
6328 \\const asm_template = [
6329 \\ asm ({
6330 \\ {}
6331 \\ })
6332 \\]T;
6333 \\const asm_clobbers = [
6334 \\ asm ("" ::: {
6335 \\ {}
6336 \\ })
6337 \\]T;
6338 \\const @"fn" = [
6339 \\ fn (({
6340 \\ {}
6341 \\ })) void
6342 \\]T;
6343 \\const array_type_len = [
6344 \\ [
6345 \\ {
6346 \\ {}
6347 \\ }
6348 \\ ]T
6349 \\]T;
6350 \\const array_type_type = [
6351 \\ [1]({
6352 \\ {}
6353 \\ })
6354 \\]T;
6355 \\const array_access_array = [
6356 \\ ({
6357 \\ {}
6358 \\ })[1]
6359 \\]T;
6360 \\const array_access_index = [
6361 \\ x[
6362 \\ {
6363 \\ {}
6364 \\ }
6365 \\ ]
6366 \\]T;
6367 \\const binop = [
6368 \\ 1 + {
6369 \\ {}
6370 \\ }
6371 \\]T;
6372 \\const unaryop = [
6373 \\ !{
6374 \\ {}
6375 \\ }
6376 \\]T;
6377 \\const destructure = [
6378 \\ while (true) : (x, {
6379 \\ {}
6380 \\ } = z) {}
6381 \\]T;
6382 \\
6383 );
6384
6385 try testCanonical(
6386 \\const oneline_access_block = x[{}];
6387 \\const oneline_block = [{}]T;
6388 \\const oneline_sentinel_block = [15:{}]T;
6389 \\const oneline_container = [enum { A }]T;
6390 \\const oneline_error_set = [error{A}]T;
6391 \\const oneline_switch = [switch (m) {}]T;
6392 \\const oneline_for = [for (a, 0..15) |_, _| {}]T;
6393 \\const oneline_call = [a(a)]T;
6394 \\const oneline_builtin_call = [@log2(a)]T;
6395 \\const oneline_array_init = [.{a}]T;
6396 \\const oneline_struct_init = [.{ .x = a }]T;
6397 \\const oneline_asm = [asm ("" ::: .{})]T;
6398 \\const onlinee_fn = [fn (usize) void]T;
6399 \\const oneline_array_type = [[{}]T]T;
6400 \\const oneline_array_access = [x[{}]]T;
6401 \\const online_binop = [1 + 1]T;
6402 \\const online_unaryop = [!false]T;
6403 \\const oneline_destructure = [while (true) : (x, y = z) {}]T;
6404 \\
6405 );
6406
6407 try testTransform(
6408 \\const a = [{
6409 \\}]T;
6410 \\
6411 \\const b = x[{
6412 \\}];
6413 \\
6414 \\const c = [
6415 \\ {
6416 \\ }
6417 \\]T;
6418 \\
6419 \\const d = x[
6420 \\ {
6421 \\ }
6422 \\];
6423 \\
6424 ,
6425 \\const a = [
6426 \\ {}
6427 \\]T;
6428 \\
6429 \\const b = x[
6430 \\ {}
6431 \\];
6432 \\
6433 \\const c = [
6434 \\ {}
6435 \\]T;
6436 \\
6437 \\const d = x[
6438 \\ {}
6439 \\];
6440 \\
6441 );
6442}
6443
6444test "zig fmt: whitespace with multiline strings" {
6445 try testCanonical(
6446 \\const a = .{
6447 \\ .b =
6448 \\ \\
6449 \\ ++ "",
6450 \\};
6451 \\const b = switch (a) {
6452 \\ a =>
6453 \\ \\
6454 \\ ++ "",
6455 \\};
6456 \\
6457 );
6458
6459 try testTransform(
6460 \\test {
6461 \\ a = \\
6462 \\ ;
6463 \\ b = \\
6464 \\ ();
6465 \\ c = x ++ \\
6466 \\ ;
6467 \\ d = x catch \\
6468 \\ ;
6469 \\ comptime \\
6470 \\ , \\
6471 \\ , \\
6472 \\ = \\
6473 \\ ;
6474 \\ e = if (x) \\
6475 \\ else y;
6476 \\ f = if (x) y else
6477 \\ \\
6478 \\ ;
6479 \\ comptime \\
6480 \\ ;
6481 \\ errdefer \\
6482 \\ ;
6483 \\ try \\
6484 \\ ;
6485 \\ return \\
6486 \\ ;
6487 \\ const a = asm (\\
6488 \\ ++ "": [a] "" (-> \\
6489 \\ ) :: \\
6490 \\ );
6491 \\ const a2 = asm ("" ::: \\
6492 \\ );
6493 \\ const b = x[1 + 1 .. \\
6494 \\ ];
6495 \\}
6496 \\/// tuple type
6497 \\comptime \\
6498 \\,
6499 \\a: \\
6500 \\align(\\
6501 \\)
6502 \\= \\
6503 \\,
6504 \\const A = .{
6505 \\ *volatile \\
6506 \\ ,
6507 \\ *const \\
6508 \\ ,
6509 \\ *addrspace( \\
6510 \\ ) \\
6511 \\ ,
6512 \\ *align( \\
6513 \\ : \\
6514 \\ : \\
6515 \\ ) \\
6516 \\ ,
6517 \\ *allowzero \\
6518 \\ ,
6519 \\ *\\
6520 \\ ,
6521 \\ **\\
6522 \\ ,
6523 \\ [*]\\
6524 \\ ,
6525 \\ [*: \\
6526 \\ ]\\
6527 \\ ,
6528 \\ [*c]\\
6529 \\ ,
6530 \\ []\\
6531 \\ ,
6532 \\ [: \\
6533 \\ ]\\
6534 \\ ,
6535 \\ *addrspace(a) align(a) \\
6536 \\ ,
6537 \\};
6538 \\const a = blk: {
6539 \\ break \\
6540 \\ ;
6541 \\ break :blk \\
6542 \\ ;
6543 \\ continue \\
6544 \\ ;
6545 \\ continue :blk \\
6546 \\ ;
6547 \\};
6548 \\const b = a(a, \\
6549 \\++ "");
6550 \\const c = @a(a, \\
6551 \\++ "");
6552 \\extern fn a(T,\\
6553 \\) \\
6554 \\;
6555 \\extern fn b(a: \\
6556 \\) align(a) callconv(a) \\
6557 \\;
6558 \\const d = switch (a) { \\
6559 \\ , 1,
6560 \\ \\
6561 \\ => {},
6562 \\ inline \\
6563 \\ => {},
6564 \\};
6565 \\
6566 ,
6567 \\test {
6568 \\ a =
6569 \\ \\
6570 \\ ;
6571 \\ b =
6572 \\ \\
6573 \\ ();
6574 \\ c = x ++
6575 \\ \\
6576 \\ ;
6577 \\ d = x catch
6578 \\ \\
6579 \\ ;
6580 \\ comptime
6581 \\ \\
6582 \\ ,
6583 \\ \\
6584 \\ ,
6585 \\ \\
6586 \\ =
6587 \\ \\
6588 \\ ;
6589 \\ e = if (x)
6590 \\ \\
6591 \\ else
6592 \\ y;
6593 \\ f = if (x) y else
6594 \\ \\
6595 \\ ;
6596 \\ comptime
6597 \\ \\
6598 \\ ;
6599 \\ errdefer
6600 \\ \\
6601 \\ ;
6602 \\ try
6603 \\ \\
6604 \\ ;
6605 \\ return
6606 \\ \\
6607 \\ ;
6608 \\ const a = asm (
6609 \\ \\
6610 \\ ++ ""
6611 \\ : [a] "" (->
6612 \\ \\
6613 \\ ),
6614 \\ :
6615 \\ :
6616 \\ \\
6617 \\ );
6618 \\ const a2 = asm ("" :::
6619 \\ \\
6620 \\ );
6621 \\ const b = x[1 + 1 ..
6622 \\ \\
6623 \\ ];
6624 \\}
6625 \\/// tuple type
6626 \\comptime
6627 \\\\
6628 \\,
6629 \\a:
6630 \\\\
6631 \\align(
6632 \\\\
6633 \\) =
6634 \\ \\
6635 \\,
6636 \\const A = .{
6637 \\ *volatile
6638 \\ \\
6639 \\ ,
6640 \\ *const
6641 \\ \\
6642 \\ ,
6643 \\ *addrspace(
6644 \\ \\
6645 \\ )
6646 \\ \\
6647 \\ ,
6648 \\ *align(
6649 \\ \\
6650 \\ :
6651 \\ \\
6652 \\ :
6653 \\ \\
6654 \\ )
6655 \\ \\
6656 \\ ,
6657 \\ *allowzero
6658 \\ \\
6659 \\ ,
6660 \\ *
6661 \\ \\
6662 \\ ,
6663 \\ **
6664 \\ \\
6665 \\ ,
6666 \\ [*]
6667 \\ \\
6668 \\ ,
6669 \\ [*:
6670 \\ \\
6671 \\ ]
6672 \\ \\
6673 \\ ,
6674 \\ [*c]
6675 \\ \\
6676 \\ ,
6677 \\ []
6678 \\ \\
6679 \\ ,
6680 \\ [:
6681 \\ \\
6682 \\ ]
6683 \\ \\
6684 \\ ,
6685 \\ *align(a) addrspace(a)
6686 \\ \\
6687 \\ ,
6688 \\};
6689 \\const a = blk: {
6690 \\ break
6691 \\ \\
6692 \\ ;
6693 \\ break :blk
6694 \\ \\
6695 \\ ;
6696 \\ continue
6697 \\ \\
6698 \\ ;
6699 \\ continue :blk
6700 \\ \\
6701 \\ ;
6702 \\};
6703 \\const b = a(a,
6704 \\ \\
6705 \\++ "");
6706 \\const c = @a(a,
6707 \\ \\
6708 \\++ "");
6709 \\extern fn a(T,
6710 \\\\
6711 \\)
6712 \\\\
6713 \\;
6714 \\extern fn b(a:
6715 \\\\
6716 \\) align(a) callconv(a)
6717 \\\\
6718 \\;
6719 \\const d = switch (a) {
6720 \\ \\
6721 \\ , 1,
6722 \\ \\
6723 \\ => {},
6724 \\ inline
6725 \\ \\
6726 \\ => {},
6727 \\};
6728 \\
6729 );
6730}
6731
6732test "zig fmt: doc comments on fn parameters" {
6733 try testTransform(
6734 \\extern fn foo(
6735 \\ /// Bitmap
6736 \\ active: u64
6737 \\) void;
6738 \\extern fn bar(
6739 \\ bits: u6,
6740 \\ /// Bitmap
6741 \\ active: u64
6742 \\) void;
6743 \\extern fn baz(
6744 \\ /// Bitmap
6745 \\ active: anytype
6746 \\) void;
6747 \\
6748 ,
6749 \\extern fn foo(
6750 \\ /// Bitmap
6751 \\ active: u64,
6752 \\) void;
6753 \\extern fn bar(
6754 \\ bits: u6,
6755 \\ /// Bitmap
6756 \\ active: u64,
6757 \\) void;
6758 \\extern fn baz(
6759 \\ /// Bitmap
6760 \\ active: anytype,
6761 \\) void;
6762 \\
6763 );
6764 try testCanonical(
6765 \\extern fn foo(x: struct {
6766 \\ /// Bitmap
6767 \\ active: u64,
6768 \\}) void;
6769 \\
6770 );
6771}
6772
6773test "zig fmt: array literal formatting when element becomes multiline" {
6774 try testTransform(
6775 \\const a = .{a,{{}},
6776 \\ b,c,};
6777 ,
6778 \\const a = .{
6779 \\ a,
6780 \\ {
6781 \\ {}
6782 \\ },
6783 \\ b,
6784 \\ c,
6785 \\};
6786 \\
6787 );
6788}
6789
6790test "zig fmt: proper tracking of indentation" {
6791 try testCanonical(
6792 \\const a = {
6793 \\ {}
6794 \\};
6795 \\const b = if (x) {};
6796 \\const c = .{
6797 \\ {
6798 \\ {}
6799 \\ } //
6800 \\ ,
6801 \\ if (x) {},
6802 \\};
6803 \\
6804 );
6805}
6806
6807test "zig fmt: canonicalize stray backslashes in identifiers" {
6808 try testTransform(
6809 \\const @"\x" = undefined;
6810 \\const @"\x3" = undefined;
6811 \\const @"\x3\x39" = undefined;
6812 \\const @"\u{" = undefined;
6813 \\const @"\?" = undefined;
6814 \\
6815 ,
6816 \\const @"\\x" = undefined;
6817 \\const @"\\x3" = undefined;
6818 \\const @"\\x39" = undefined;
6819 \\const @"\\u{" = undefined;
6820 \\const @"\\?" = undefined;
6821 \\
6822 );
6823}
6824
6825test "zig fmt: error set with extra newline before comma" {
6826 try testTransform(
6827 \\const E = error{
6828 \\ A
6829 \\
6830 \\ ,
6831 \\};
6832 \\
6833 ,
6834 \\const E = error{
6835 \\ A,
6836 \\};
6837 \\
6838 );
6839}
6840
6031test "recovery: top level" {6841test "recovery: top level" {
6032 try testError(6842 try testError(
6033 \\test "" {inline}6843 \\test "" {inline}
...@@ -6336,6 +7146,23 @@ test "ampersand" {...@@ -6336,6 +7146,23 @@ test "ampersand" {
6336 , &.{});7146 , &.{});
6337}7147}
63387148
7149test "Ast: pointer types with subexprs containing qualifiers" {
7150 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
7151 const allocator = fixed_allocator.allocator();
7152 var tree = try std.zig.Ast.parse(allocator, "**addrspace(*align(1)T)T", .zon);
7153 defer tree.deinit(allocator);
7154
7155 const regular_ptr_node = tree.nodeData(.root).node;
7156 const full_regular_ptr = tree.fullPtrType(regular_ptr_node) orelse return error.TestFailed;
7157 try std.testing.expect(full_regular_ptr.ast.addrspace_node == .none);
7158 try std.testing.expect(full_regular_ptr.ast.align_node == .none);
7159
7160 const special_ptr_node = full_regular_ptr.ast.child_type;
7161 const full_special_ptr = tree.fullPtrType(special_ptr_node) orelse return error.TestFailed;
7162 try std.testing.expect(full_special_ptr.ast.addrspace_node != .none);
7163 try std.testing.expect(full_special_ptr.ast.align_node == .none);
7164}
7165
6339var fixed_buffer_mem: [100 * 1024]u8 = undefined;7166var fixed_buffer_mem: [100 * 1024]u8 = undefined;
63407167
6341fn testParse(io: Io, source: [:0]const u8, allocator: Allocator, anything_changed: *bool) ![]u8 {7168fn 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 {...@@ -6426,3 +7253,263 @@ fn fuzzTestOneParse(_: void, smith: *std.testing.Smith) !void {
6426 var fba: std.heap.FixedBufferAllocator = .init(&fixed_buffer_mem);7253 var fba: std.heap.FixedBufferAllocator = .init(&fixed_buffer_mem);
6427 _ = std.zig.Ast.parseTokens(fba.allocator(), tokens.source(), tokens.list(), mode) catch return;7254 _ = std.zig.Ast.parseTokens(fba.allocator(), tokens.source(), tokens.list(), mode) catch return;
6428}7255}
7256
7257test "zig fmt: fuzz" {
7258 try std.testing.fuzz({}, fuzzRender, .{});
7259}
7260
7261fn parseTokens(
7262 fba: Allocator,
7263 source: [:0]const u8,
7264) error{ SkipZigTest, OutOfMemory }!struct {
7265 toks: std.zig.Ast.TokenList,
7266 maybe_rewritable: bool,
7267 skip_idempotency: bool,
7268} {
7269 @disableInstrumentation();
7270 // Byte-order marker is stripped
7271 var maybe_rewritable = std.mem.startsWith(u8, source, "\xEF\xBB\xBF");
7272 var skip_idempotency = false; // This should be able to be removed once all the bugs are fixed
7273
7274 var tokens: std.zig.Ast.TokenList = .{};
7275 try tokens.ensureTotalCapacity(fba, source.len / 2);
7276 var tokenizer: std.zig.Tokenizer = .init(source);
7277 while (true) {
7278 const tok = tokenizer.next();
7279 switch (tok.tag) {
7280 .invalid,
7281 .invalid_periodasterisks,
7282 => return error.SkipZigTest,
7283 // Extra colons can be removed
7284 .keyword_asm,
7285 // Qualifiers can be reordered
7286 // keyword_const is intentionally excluded since it is used in other contexts and
7287 // having only one qualifier will never lead to reordering.
7288 .keyword_addrspace,
7289 .keyword_align,
7290 .keyword_allowzero,
7291 .keyword_callconv,
7292 .keyword_linksection,
7293 .keyword_volatile,
7294 => maybe_rewritable = true,
7295 .builtin,
7296 // Pointer casts can be reordered
7297 => for ([_][]const u8{
7298 "ptrCast",
7299 "alignCast",
7300 "addrSpaceCast",
7301 "constCast",
7302 "volatileCast",
7303 }) |id| {
7304 if (std.mem.eql(u8, source[tok.loc.start + 1 .. tok.loc.end], id)) {
7305 maybe_rewritable = false;
7306 }
7307 },
7308 // Quoted identifiers can be unquoted
7309 .identifier => maybe_rewritable = maybe_rewritable or source[tok.loc.start] == '@',
7310 else => {},
7311 // #23754
7312 .container_doc_comment,
7313 => if (std.mem.endsWith(Token.Tag, tokens.items(.tag), &.{.l_brace})) {
7314 return error.SkipZigTest;
7315 },
7316 // #24507
7317 .keyword_inline,
7318 .keyword_for,
7319 .keyword_while,
7320 .l_brace,
7321 => if (std.mem.endsWith(Token.Tag, tokens.items(.tag), &.{ .identifier, .colon })) {
7322 maybe_rewritable = true;
7323 skip_idempotency = true;
7324 },
7325 }
7326 try tokens.append(fba, .{
7327 .tag = tok.tag,
7328 .start = @intCast(tok.loc.start),
7329 });
7330 if (tok.tag == .eof)
7331 break;
7332 }
7333 return .{
7334 .toks = tokens,
7335 .maybe_rewritable = maybe_rewritable,
7336 .skip_idempotency = skip_idempotency,
7337 };
7338}
7339
7340/// Checks equivelence of non-whitespace characters.
7341/// If there are commas in `source`, then it is checked they are also present
7342/// in `rendered`. Extra commas in `rendered` are ignored.
7343fn isRewritten(source: [:0]const u8, rendered: [:0]const u8) bool {
7344 @disableInstrumentation();
7345 var i: usize = 0;
7346 for (source[0 .. source.len + 1]) |c| switch (c) {
7347 ' ', '\r', '\t', '\n' => {},
7348 else => while (true) {
7349 defer i += 1;
7350 switch (rendered[i]) {
7351 ' ', '\n' => {},
7352 ',' => if (c == ',') break,
7353 else => |r| if (c != r) return false else break,
7354 }
7355 },
7356 };
7357 std.debug.assert(i >= rendered.len);
7358 return false;
7359}
7360
7361/// Checks that no line ends in whitespace
7362fn checkBetweenTokens(src: []const u8, fmt_on: *bool) error{
7363 TrailingLineWhitespace,
7364 DoubleEmptyLine,
7365}!void {
7366 @disableInstrumentation();
7367 var pos: usize = 0;
7368 while (true) {
7369 const nl_pos = std.mem.indexOfScalarPos(u8, src, pos, '\n');
7370 var check_trailing = fmt_on.*;
7371
7372 const line = src[pos .. nl_pos orelse src.len];
7373 if (std.mem.indexOfScalar(u8, line, '/')) |comment_start| {
7374 const comment_content = line[comment_start..][2..];
7375 const trimmed_comment = std.mem.trim(u8, comment_content, &std.ascii.whitespace);
7376 if (std.mem.eql(u8, trimmed_comment, "zig fmt: off")) {
7377 fmt_on.* = false;
7378 } else if (std.mem.eql(u8, trimmed_comment, "zig fmt: on")) {
7379 fmt_on.* = true;
7380 check_trailing = true;
7381 }
7382 }
7383
7384 pos = nl_pos orelse break;
7385 if (check_trailing and pos != 0) switch (src[pos - 1]) {
7386 ' ', '\t', '\r' => return error.TrailingLineWhitespace,
7387 '\n' => if (pos != 1 and src[pos - 2] == '\n') return error.DoubleEmptyLine,
7388 else => {},
7389 };
7390 pos += 1;
7391 }
7392}
7393
7394/// Ignores extre `.comma` tokens in `rendered`
7395fn reparseTokens(
7396 fba: Allocator,
7397 rendered: [:0]const u8,
7398 expected_tags: [:.eof]const Token.Tag,
7399) error{
7400 OutOfMemory,
7401 SameLineMultilineStringLiteral,
7402 TrailingLineWhitespace,
7403 DoubleEmptyLine,
7404}!struct {
7405 toks: std.zig.Ast.TokenList,
7406 rewritten: bool,
7407} {
7408 @disableInstrumentation();
7409 var rewritten = false;
7410 var tokens: std.zig.Ast.TokenList = .{};
7411 var last_token_end: usize = 0;
7412 var fmt_on = true;
7413
7414 try tokens.ensureTotalCapacity(fba, expected_tags.len + 2); // 1 for EOF and 1 for maybe a comma
7415 var tokenizer: std.zig.Tokenizer = .init(rendered);
7416 var i: usize = 0;
7417 while (true) {
7418 const tok = tokenizer.next();
7419 try tokens.append(fba, .{
7420 .tag = tok.tag,
7421 .start = @intCast(tok.loc.start),
7422 });
7423
7424 const between = rendered[last_token_end..tok.loc.start];
7425 last_token_end = tok.loc.end;
7426 try checkBetweenTokens(between, &fmt_on);
7427 if (tok.tag == .multiline_string_literal_line and fmt_on) blk: {
7428 if (tokens.len == 1)
7429 break :blk; // first token
7430 if (std.mem.indexOfScalar(u8, between, '\n') == null)
7431 return error.SameLineMultilineStringLiteral;
7432 }
7433 if (tok.tag == expected_tags[i]) {
7434 if (tok.tag == .eof)
7435 break;
7436 i += 1;
7437 } else if (tok.tag != .comma or !fmt_on) {
7438 rewritten = true;
7439 }
7440 }
7441 std.debug.assert(i == expected_tags.len);
7442 try checkBetweenTokens(rendered[last_token_end..], &fmt_on);
7443
7444 return .{ .toks = tokens, .rewritten = rewritten };
7445}
7446
7447fn fuzzRender(_: void, smith: *std.testing.Smith) !void {
7448 @disableInstrumentation();
7449
7450 var src_buf: [512]u8 = undefined;
7451 const src_len = smith.sliceWeighted(&src_buf, &.{
7452 .rangeLessThan(u32, 0, 32, 256),
7453 .rangeLessThan(u32, 32, 64, 64),
7454 .rangeLessThan(u32, 64, src_buf.len, 1),
7455 }, &.{
7456 .rangeAtMost(u8, 0x20, 0x7e, 8),
7457 .value(u8, '\n', 32),
7458 .value(u8, '\t', 8),
7459 .value(u8, '\r', 4),
7460 .rangeAtMost(u8, 0x7f, 0xff, 1),
7461 });
7462 src_buf[src_len] = 0;
7463
7464 var fba_ctx = std.heap.FixedBufferAllocator.init(&fixed_buffer_mem);
7465 fuzzRenderInner(src_buf[0..src_len :0], fba_ctx.allocator()) catch |e| return switch (e) {
7466 error.OutOfMemory => {},
7467 else => e,
7468 };
7469}
7470
7471fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void {
7472 @disableInstrumentation();
7473
7474 const src_toks = try parseTokens(fba, source);
7475 const src_tree = try std.zig.Ast.parseTokens(fba, source, src_toks.toks.slice(), .zig);
7476 if (src_tree.errors.len != 0)
7477 return;
7478 for (src_tree.nodes.items(.tag)) |tag| switch (tag) {
7479 // #24507 (`switch(x) { inline for (a) |a| a => {} }` to
7480 // `switch(x) { { inline for (a) |a| a => {} }` since
7481 // AST determines inline case token as one before the case expression's first)
7482 .switch_case_inline, .switch_case_inline_one => return error.SkipZigTest,
7483 else => {},
7484 };
7485
7486 var rendered_w: std.Io.Writer.Allocating = .init(fba);
7487 try rendered_w.ensureUnusedCapacity(source.len + source.len / 2);
7488 try src_tree.render(fba, &rendered_w.writer, .{});
7489 // `toOwnedSliceSentinel` is not used since it reallocates the entire
7490 // list to save space which is useless for fixed buffer allocators.
7491 try rendered_w.writer.writeByte(0);
7492 const rendered = rendered_w.written()[0 .. rendered_w.written().len - 1 :0];
7493
7494 // First check that the non-whitespace characters match. This ensures that
7495 // identifier names, numbers, comments, et cetera are preserved.
7496 if (!src_toks.maybe_rewritable and isRewritten(source, rendered))
7497 return error.Rewritten;
7498 // Next check that the tokens are the same since whitespace removal can change the tokens
7499 const src_tags = src_toks.toks.items(.tag);
7500 const rendered_toks = try reparseTokens(fba, rendered, src_tags[0 .. src_tags.len - 1 :.eof]);
7501 if (!src_toks.maybe_rewritable and rendered_toks.rewritten)
7502 return error.Rewritten;
7503
7504 // Rerender the tree to check idempotency and that new commas
7505 // and whitespace changes did not create an AST error.
7506 const rendered_tree = try std.zig.Ast.parseTokens(fba, rendered, rendered_toks.toks.slice(), .zig);
7507 if (rendered_tree.errors.len != 0)
7508 return error.Rewritten;
7509 if (!src_toks.skip_idempotency) {
7510 var rerendered_w: std.Io.Writer.Allocating = .init(fba);
7511 try rerendered_w.ensureUnusedCapacity(source.len);
7512 try rendered_tree.render(fba, &rerendered_w.writer, .{});
7513 try std.testing.expectEqualStrings(rendered, rerendered_w.written());
7514 }
7515}
src/codegen/sparc64/abi.zig+2-2
...@@ -38,10 +38,10 @@ const allocatable_regs = [_]Register{...@@ -38,10 +38,10 @@ const allocatable_regs = [_]Register{
38};38};
3939
40pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };40pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };
41pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };41pub const c_abi_int_param_regs_callee_view = [_]Register{ .i0, .i1, .i2, .i3, .i4, .i5 };
4242
43pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };43pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };
44pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" };44pub const c_abi_int_return_regs_callee_view = [_]Register{ .i0, .i1, .i2, .i3 };
4545
46pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);46pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
4747
src/link.zig+2-1
...@@ -2097,7 +2097,8 @@ fn resolveLibInput(...@@ -2097,7 +2097,8 @@ fn resolveLibInput(
2097 const test_path: Path = .{2097 const test_path: Path = .{
2098 .root_dir = lib_directory,2098 .root_dir = lib_directory,
2099 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{2099 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
2100 target.libPrefix(), lib_name, switch (link_mode) {2100 target.libPrefix(), lib_name,
2101 switch (link_mode) {
2101 .static => target.staticLibSuffix(),2102 .static => target.staticLibSuffix(),
2102 .dynamic => target.dynamicLibSuffix(),2103 .dynamic => target.dynamicLibSuffix(),
2103 },2104 },