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
17071707 std.debug.print("#include <...> search starts here:\n", .{});
17081708 }
17091709 std.debug.print(" {s}{s}\n", .{
1710 include.path, if (include.kind.isFramework())
1710 include.path,
1711 if (include.kind.isFramework())
17111712 " (framework directory)"
17121713 else
17131714 "",
lib/compiler/translate-c/ast.zig+4-2
......@@ -1213,7 +1213,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
12131213 .tag = .slice,
12141214 .main_token = l_bracket,
12151215 .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{
12171218 .start = start,
12181219 .end = end,
12191220 }),
......@@ -2720,7 +2721,8 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
27202721 .tag = .call,
27212722 .main_token = lparen,
27222723 .data = .{ .node_and_extra = .{
2723 lhs, try c.addExtra(NodeSubRange{
2724 lhs,
2725 try c.addExtra(NodeSubRange{
27242726 .start = span.start,
27252727 .end = span.end,
27262728 }),
lib/std/crypto/aes.zig+1-2
......@@ -8,8 +8,7 @@ const has_armaes = builtin.cpu.has(.aarch64, .aes);
88// C backend doesn't currently support passing vectors to inline asm.
99const impl = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c and has_aesni and has_avx) impl: {
1010 break :impl @import("aes/aesni.zig");
11} else if (builtin.cpu.arch == .aarch64 and builtin.zig_backend != .stage2_c and has_armaes)
12impl: {
11} else if (builtin.cpu.arch == .aarch64 and builtin.zig_backend != .stage2_c and has_armaes) impl: {
1312 break :impl @import("aes/armcrypto.zig");
1413} else impl: {
1514 break :impl @import("aes/soft.zig");
lib/std/unicode.zig+20-16
......@@ -1245,14 +1245,16 @@ test utf8ToUtf16Le {
12451245 const length = try utf8ToUtf16Le(utf16le[0..], "This string has been designed to test the vectorized implementat" ++
12461246 "ion by beginning with one hundred twenty-seven ASCII characters¡");
12471247 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,
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,
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,
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,
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,
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,
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,
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,
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,
1256 '¡',
1257 0,
12561258 }, mem.sliceAsBytes(utf16le[0..length]));
12571259 }
12581260}
......@@ -1317,14 +1319,16 @@ test utf8ToUtf16LeAllocZ {
13171319 "ion by beginning with one hundred twenty-seven ASCII characters¡");
13181320 defer testing.allocator.free(utf16);
13191321 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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
13281332 }, mem.sliceAsBytes(utf16));
13291333 }
13301334}
lib/std/zig/Ast.zig+10-8
......@@ -2136,10 +2136,12 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType
21362136 // here while looking for modifiers as that could result in false
21372137 // positives. Therefore, start after a sentinel if there is one and
21382138 // skip over any align node and bit range nodes.
2139 var i = if (info.sentinel.unwrap()) |sentinel| tree.lastToken(sentinel) + 1 else switch (size) {
2140 .many, .c => info.main_token + 1,
2141 else => info.main_token,
2142 };
2139 var i = (if (info.sentinel.unwrap()) |sentinel| tree.lastToken(sentinel) + 1 else switch (size) {
2140 .one => info.main_token,
2141 .slice => info.main_token + 1,
2142 .many => info.main_token + 2,
2143 .c => info.main_token + 3,
2144 }) + 1;
21432145 const end = tree.firstToken(info.child_type);
21442146 while (i < end) : (i += 1) {
21452147 switch (tree.tokenTag(i)) {
......@@ -2147,15 +2149,15 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType
21472149 .keyword_const => result.const_token = i,
21482150 .keyword_volatile => result.volatile_token = i,
21492151 .keyword_align => {
2150 const align_node = info.align_node.unwrap().?;
21512152 if (info.bit_range_end.unwrap()) |bit_range_end| {
21522153 assert(info.bit_range_start != .none);
21532154 i = tree.lastToken(bit_range_end) + 1;
2154 } else {
2155 } else if (info.align_node.unwrap()) |align_node| {
21552156 i = tree.lastToken(align_node) + 1;
2156 }
2157 } else unreachable;
21572158 },
2158 else => {},
2159 .keyword_addrspace => i = tree.lastToken(info.addrspace_node.unwrap().?) + 1,
2160 else => unreachable,
21592161 }
21602162 }
21612163 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 {
346346 const next_token_tag = tree.tokenTag(next_token);
347347
348348 // dedent the next thing that comes after a multiline string literal
349 if (!ais.indentStackEmpty() and
350 next_token_tag != .colon and
351 ((next_token_tag != .semicolon and next_token_tag != .comma) or
352 ais.lastSpaceModeIndent() < ais.currentIndent()))
349 if (next_token_tag != .colon and
350 !ais.indentStackEmpty() and
351 ais.lastSpaceModeIndent() < ais.currentIndent())
353352 {
354 ais.popIndent();
355 try ais.pushIndent(.normal);
353 const indent_top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
354 if (indent_top.realized) {
355 indent_top.realized = false;
356 ais.indent_count -= 1;
357 }
356358 }
357359
358360 switch (space) {
359 .none, .space, .newline, .skip => {},
360 .semicolon => if (next_token_tag == .semicolon) try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
361 .comma => if (next_token_tag == .comma) try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
362 .comma_space => if (next_token_tag == .comma) try renderToken(r, next_token, .space),
361 .none, .space, .newline, .maybe_space, .skip => {},
362 .semicolon => if (next_token_tag == .semicolon)
363 try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
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),
363370 }
364371 },
365372
......@@ -384,11 +391,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
384391 const defer_token = tree.nodeMainToken(node);
385392 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);
388395 if (maybe_payload_token.unwrap()) |payload_token| {
389396 try renderToken(r, payload_token - 1, .none); // |
390397 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); // |
392399 }
393400 return renderExpression(r, expr, space);
394401 },
......@@ -400,7 +407,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
400407 => {
401408 const main_token = tree.nodeMainToken(node);
402409 const item = tree.nodeData(node).node;
403 try renderToken(r, main_token, .space);
410 try renderToken(r, main_token, .maybe_space);
404411 return renderExpression(r, item, space);
405412 },
406413
......@@ -409,8 +416,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
409416 const lhs, const rhs = tree.nodeData(node).node_and_node;
410417 const fallback_first = tree.firstToken(rhs);
411418
412 const same_line = tree.tokensOnSameLine(main_token, fallback_first);
413 const after_op_space = if (same_line) Space.space else Space.newline;
419 const seperate_line = !tree.tokensOnSameLine(main_token, fallback_first) or
420 tree.tokenTag(fallback_first) == .multiline_string_literal_line;
421 const after_op_space: Space = if (seperate_line) .newline else .space;
414422
415423 try renderExpression(r, lhs, .space); // target
416424
......@@ -433,13 +441,18 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
433441 const dot_token = name_token - 1;
434442
435443 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.
440445 const lhs_last_token = tree.lastToken(lhs);
441446 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
444457 try renderToken(r, dot_token, .none);
445458
......@@ -489,11 +502,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
489502 try renderExpression(r, lhs, .space);
490503 const op_token = tree.nodeMainToken(node);
491504 try ais.pushIndent(.after_equals);
492 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
493 try renderToken(r, op_token, .space);
494 } else {
495 try renderToken(r, op_token, .newline);
496 }
505 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
506 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
507 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
497508 try renderExpression(r, rhs, space);
498509 ais.popIndent();
499510 },
......@@ -532,11 +543,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
532543 try renderExpression(r, lhs, .space);
533544 const op_token = tree.nodeMainToken(node);
534545 try ais.pushIndent(.binop);
535 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
536 try renderToken(r, op_token, .space);
537 } else {
538 try renderToken(r, op_token, .newline);
539 }
546 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
547 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
548 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
540549 try renderExpression(r, rhs, space);
541550 ais.popIndent();
542551 },
......@@ -544,11 +553,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
544553 .assign_destructure => {
545554 const full = tree.assignDestructure(node);
546555 if (full.comptime_token) |comptime_token| {
547 try renderToken(r, comptime_token, .space);
556 try renderToken(r, comptime_token, .maybe_space);
548557 }
549558
550559 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;
552561 switch (tree.nodeTag(variable_node)) {
553562 .global_var_decl,
554563 .local_var_decl,
......@@ -561,11 +570,10 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
561570 }
562571 }
563572 try ais.pushIndent(.after_equals);
564 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {
565 try renderToken(r, full.ast.equal_token, .space);
566 } else {
567 try renderToken(r, full.ast.equal_token, .newline);
568 }
573 const expr_seperate_line =
574 !tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1) or
575 tree.tokenTag(full.ast.equal_token + 1) == .multiline_string_literal_line;
576 try renderToken(r, full.ast.equal_token, if (expr_seperate_line) .newline else .space);
569577 try renderExpression(r, full.ast.value_expr, space);
570578 ais.popIndent();
571579 },
......@@ -584,7 +592,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
584592 .@"try",
585593 .@"resume",
586594 => {
587 try renderToken(r, tree.nodeMainToken(node), .space);
595 try renderToken(r, tree.nodeMainToken(node), .maybe_space);
588596 return renderExpression(r, tree.nodeData(node).node, space);
589597 },
590598
......@@ -637,7 +645,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
637645 const lhs, const rhs = tree.nodeData(node).node_and_node;
638646 const lbracket = tree.firstToken(rhs) - 1;
639647 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);
641650 const inner_space = if (one_line) Space.none else Space.newline;
642651 try renderExpression(r, lhs, .none);
643652 try ais.pushIndent(.normal);
......@@ -668,30 +677,23 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
668677 .@"break", .@"continue" => {
669678 const main_token = tree.nodeMainToken(node);
670679 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) {
672 try renderToken(r, main_token, space); // break/continue
673 } else if (opt_label_token == .none and opt_target != .none) {
674 const target = opt_target.unwrap().?;
675 try renderToken(r, main_token, .space); // break/continue
676 try renderExpression(r, target, space);
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
680
681 const before_target_space: Space = if (opt_target != .none) .maybe_space else space;
682 const before_label_space: Space = if (opt_label_token != .none) .space else before_target_space;
683
684 try renderToken(r, main_token, before_label_space);
685 if (opt_label_token.unwrap()) |label_token| {
686686 try renderToken(r, label_token - 1, .none); // :
687 try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier
687 try renderIdentifier(r, label_token, before_target_space, .eagerly_unquote); // identifier
688 }
689 if (opt_target.unwrap()) |target| {
688690 try renderExpression(r, target, space);
689 } else unreachable;
691 }
690692 },
691693
692694 .@"return" => {
693695 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);
695697 try renderExpression(r, expr, space);
696698 } else {
697699 try renderToken(r, tree.nodeMainToken(node), space);
......@@ -740,33 +742,36 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
740742 try renderToken(r, lbrace, .none);
741743 try renderIdentifier(r, lbrace + 1, .none, .eagerly_unquote); // identifier
742744 return renderToken(r, rbrace, space);
743 } else if (tree.tokenTag(rbrace - 1) == .comma) {
744 // There is a trailing comma so render each member on a new line.
745 } else if (!isOneLineErrorSetDecl(tree, lbrace, rbrace)) {
746 // Render each member on a new line.
745747 try ais.pushIndent(.normal);
746748 try renderToken(r, lbrace, .newline);
747749 var i = lbrace + 1;
748750 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 }
749756 if (i > lbrace + 1) try renderExtraNewlineToken(r, i);
750 switch (tree.tokenTag(i)) {
757 switch (tag) {
751758 .doc_comment => try renderToken(r, i, .newline),
752759 .identifier => {
753760 try ais.pushSpace(.comma);
754761 try renderIdentifier(r, i, .comma, .eagerly_unquote);
755762 ais.popSpace();
756763 },
757 .comma => {},
758764 else => unreachable,
759765 }
760766 }
761767 ais.popIndent();
762768 return renderToken(r, rbrace, space);
763769 } else {
764 // There is no trailing comma so render everything on one line.
770 // Render each member on one line.
765771 try renderToken(r, lbrace, .space);
766772 var i = lbrace + 1;
767773 while (i < rbrace) : (i += 1) {
768774 switch (tree.tokenTag(i)) {
769 .doc_comment => unreachable, // TODO
770775 .identifier => try renderIdentifier(r, i, .comma_space, .eagerly_unquote),
771776 .comma => {},
772777 else => unreachable,
......@@ -926,6 +931,382 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v
926931 }
927932}
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
9291310fn renderArrayType(
9301311 r: *Render,
9311312 array_type: Ast.full.ArrayType,
......@@ -934,7 +1315,7 @@ fn renderArrayType(
9341315 const tree = r.tree;
9351316 const ais = r.ais;
9361317 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);
9381319 const inner_space = if (one_line) Space.none else Space.newline;
9391320 try ais.pushIndent(.normal);
9401321 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
......@@ -951,6 +1332,7 @@ fn renderArrayType(
9511332fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
9521333 const tree = r.tree;
9531334 const main_token = ptr_type.ast.main_token;
1335
9541336 switch (ptr_type.size) {
9551337 .one => {
9561338 // 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
9971379 },
9981380 }
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
10001405 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);
10021408 }
10031409
10041410 if (ptr_type.ast.align_node.unwrap()) |align_node| {
1411 const this_space: Space = if (final_qual == .@"align") final_qual_space else .space;
10051412 const align_first = tree.firstToken(align_node);
10061413 try renderToken(r, align_first - 2, .none); // align
10071414 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
10121419 try renderExpression(r, bit_range_start, .none);
10131420 try renderToken(r, tree.firstToken(bit_range_end) - 1, .none); // colon
10141421 try renderExpression(r, bit_range_end, .none);
1015 try renderToken(r, tree.lastToken(bit_range_end) + 1, .space); // rparen
1422 try renderToken(r, tree.lastToken(bit_range_end) + 1, this_space); // rparen
10161423 } else {
1017 try renderToken(r, tree.lastToken(align_node) + 1, .space); // rparen
1424 try renderToken(r, tree.lastToken(align_node) + 1, this_space); // rparen
10181425 }
10191426 }
10201427
10211428 if (ptr_type.ast.addrspace_node.unwrap()) |addrspace_node| {
1429 const this_space: Space = if (final_qual == .@"addrspace") final_qual_space else .space;
10221430 const addrspace_first = tree.firstToken(addrspace_node);
10231431 try renderToken(r, addrspace_first - 2, .none); // addrspace
10241432 try renderToken(r, addrspace_first - 1, .none); // lparen
10251433 try renderExpression(r, addrspace_node, .none);
1026 try renderToken(r, tree.lastToken(addrspace_node) + 1, .space); // rparen
1434 try renderToken(r, tree.lastToken(addrspace_node) + 1, this_space); // rparen
10271435 }
10281436
10291437 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);
10311440 }
10321441
10331442 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);
10351445 }
10361446
10371447 try renderExpression(r, ptr_type.ast.child_type, space);
......@@ -1044,12 +1454,14 @@ fn renderSlice(
10441454 space: Space,
10451455) Error!void {
10461456 const tree = r.tree;
1047 const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
1457 const space_around_dots = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
10481458 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;
1050 const after_dots_space = if (slice.ast.end != .none)
1051 after_start_space
1052 else if (slice.ast.sentinel != .none) Space.space else Space.none;
1459 const after_start_space: Space = if (space_around_dots) .space else .none;
1460 const before_sentinel_space: Space = if (slice.ast.sentinel != .none) .space else .none;
1461 const after_dots_space: Space = if (slice.ast.end != .none)
1462 if (space_around_dots) .maybe_space else .none
1463 else
1464 before_sentinel_space;
10531465
10541466 try renderExpression(r, slice.ast.sliced, .none);
10551467 try renderToken(r, slice.ast.lbracket, .none); // lbracket
......@@ -1059,8 +1471,7 @@ fn renderSlice(
10591471 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")
10601472
10611473 if (slice.ast.end.unwrap()) |end| {
1062 const after_end_space = if (slice.ast.sentinel != .none) Space.space else Space.none;
1063 try renderExpression(r, end, after_end_space);
1474 try renderExpression(r, end, before_sentinel_space);
10641475 }
10651476
10661477 if (slice.ast.sentinel.unwrap()) |sentinel| {
......@@ -1088,7 +1499,7 @@ fn renderAsmOutput(
10881499
10891500 if (tree.tokenTag(symbolic_name + 4) == .arrow) {
10901501 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); // ->
10921503 try renderExpression(r, type_expr.unwrap().?, Space.none);
10931504 return renderToken(r, rparen, space);
10941505 } else {
......@@ -1169,33 +1580,38 @@ fn renderVarDeclWithoutFixups(
11691580
11701581 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 or
1173 var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or
1174 var_decl.ast.init_node != .none)
1175 {
1176 const name_space = if (var_decl.ast.type_node == .none and
1177 (var_decl.ast.align_node != .none or
1178 var_decl.ast.addrspace_node != .none or
1179 var_decl.ast.section_node != .none or
1180 var_decl.ast.init_node != .none))
1181 Space.space
1182 else
1183 Space.none;
1184
1185 try renderIdentifier(r, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
1186 } else {
1187 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name
1583 const last_component: enum {
1584 value,
1585 @"linksection",
1586 @"addrspace",
1587 @"align",
1588 type,
1589 identifier,
1590 } = if (var_decl.ast.init_node != .none)
1591 .value
1592 else if (var_decl.ast.section_node != .none)
1593 .@"linksection"
1594 else if (var_decl.ast.addrspace_node != .none)
1595 .@"addrspace"
1596 else if (var_decl.ast.align_node != .none)
1597 .@"align"
1598 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);
11881605 }
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
11901609 if (var_decl.ast.type_node.unwrap()) |type_node| {
1191 try renderToken(r, var_decl.ast.mut_token + 2, Space.space); // :
1192 if (var_decl.ast.align_node != .none or var_decl.ast.addrspace_node != .none or
1193 var_decl.ast.section_node != .none or var_decl.ast.init_node != .none)
1194 {
1195 try renderExpression(r, type_node, .space);
1196 } else {
1610 try renderToken(r, var_decl.ast.mut_token + 2, .maybe_space); // :
1611 if (last_component == .type) {
11971612 return renderExpression(r, type_node, space);
11981613 }
1614 try renderExpression(r, type_node, .space);
11991615 }
12001616
12011617 if (var_decl.ast.align_node.unwrap()) |align_node| {
......@@ -1205,13 +1621,10 @@ fn renderVarDeclWithoutFixups(
12051621 try renderToken(r, align_kw, Space.none); // align
12061622 try renderToken(r, lparen, Space.none); // (
12071623 try renderExpression(r, align_node, Space.none);
1208 if (var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or
1209 var_decl.ast.init_node != .none)
1210 {
1211 try renderToken(r, rparen, .space); // )
1212 } else {
1624 if (last_component == .@"align") {
12131625 return renderToken(r, rparen, space); // )
12141626 }
1627 try renderToken(r, rparen, .space); // )
12151628 }
12161629
12171630 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
......@@ -1221,12 +1634,10 @@ fn renderVarDeclWithoutFixups(
12211634 try renderToken(r, addrspace_kw, Space.none); // addrspace
12221635 try renderToken(r, lparen, Space.none); // (
12231636 try renderExpression(r, addrspace_node, Space.none);
1224 if (var_decl.ast.section_node != .none or var_decl.ast.init_node != .none) {
1225 try renderToken(r, rparen, .space); // )
1226 } else {
1227 try renderToken(r, rparen, .none); // )
1228 return renderToken(r, rparen + 1, Space.newline); // ;
1637 if (last_component == .@"addrspace") {
1638 return renderToken(r, rparen, space); // )
12291639 }
1640 try renderToken(r, rparen, .space); // )
12301641 }
12311642
12321643 if (var_decl.ast.section_node.unwrap()) |section_node| {
......@@ -1236,17 +1647,19 @@ fn renderVarDeclWithoutFixups(
12361647 try renderToken(r, section_kw, Space.none); // linksection
12371648 try renderToken(r, lparen, Space.none); // (
12381649 try renderExpression(r, section_node, Space.none);
1239 if (var_decl.ast.init_node != .none) {
1240 try renderToken(r, rparen, .space); // )
1241 } else {
1650 if (last_component == .@"linksection") {
12421651 return renderToken(r, rparen, space); // )
12431652 }
1653 try renderToken(r, rparen, .space); // )
12441654 }
12451655
1656 assert(last_component == .value);
12461657 const init_node = var_decl.ast.init_node.unwrap().?;
12471658
12481659 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;
12501663 try ais.pushIndent(.after_equals);
12511664 try renderToken(r, eq_token, eq_space); // =
12521665 try renderExpression(r, init_node, space); // ;
......@@ -1346,8 +1759,10 @@ fn renderThenElse(
13461759 const tree = r.tree;
13471760 const ais = r.ais;
13481761 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));
1762 const then_expr_first_token = tree.firstToken(then_expr);
13491763 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
13521767 if (indent_then_expr) try ais.pushIndent(.normal);
13531768
......@@ -1381,7 +1796,9 @@ fn renderThenElse(
13811796
13821797 const indent_else_expr = indent_then_expr and
13831798 !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;
13851802 if (indent_else_expr) {
13861803 try ais.pushIndent(.normal);
13871804 try renderToken(r, last_else_token, .newline);
......@@ -1417,44 +1834,29 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
14171834 try renderParamList(r, lparen, for_node.ast.inputs, .space);
14181835
14191836 var cur = for_node.payload_token;
1420 const pipe = std.mem.findScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;
1421 if (tree.tokenTag(@intCast(pipe - 1)) == .comma) {
1837 const pipe = std.mem.findScalarPos(Token.Tag, token_tags, cur, .pipe).?;
1838 const capture_trailing_comma = token_tags[@intCast(pipe - 1)] == .comma;
1839
1840 if (capture_trailing_comma)
14221841 try ais.pushIndent(.normal);
1423 try renderToken(r, cur - 1, .newline); // |
1424 while (true) {
1425 if (tree.tokenTag(cur) == .asterisk) {
1426 try renderToken(r, cur, .none); // *
1427 cur += 1;
1428 }
1429 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1842 try renderToken(r, cur - 1, if (capture_trailing_comma) .newline else .none); // |
1843 while (true) {
1844 if (token_tags[cur] == .asterisk) {
1845 try renderToken(r, cur, .none); // *
14301846 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 }
14381847 }
1439 ais.popIndent();
1440 } else {
1441 try renderToken(r, cur - 1, .none); // |
1442 while (true) {
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
1848 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1849 cur += 1;
1850 if (token_tags[cur] == .comma) {
1851 try renderToken(r, cur, if (capture_trailing_comma) .newline else .space); // ,
14481852 cur += 1;
1449 if (tree.tokenTag(cur) == .comma) {
1450 try renderToken(r, cur, .space); // ,
1451 cur += 1;
1452 }
1453 if (tree.tokenTag(cur) == .pipe) {
1454 break;
1455 }
1853 }
1854 if (token_tags[cur] == .pipe) {
1855 break;
14561856 }
14571857 }
1858 if (capture_trailing_comma)
1859 ais.popIndent();
14581860
14591861 try renderThenElse(
14601862 r,
......@@ -1483,97 +1885,80 @@ fn renderContainerField(
14831885 };
14841886
14851887 if (field.comptime_token) |t| {
1486 try renderToken(r, t, .space); // comptime
1487 }
1488 if (field.ast.type_expr == .none and field.ast.value_expr == .none) {
1489 if (field.ast.align_expr.unwrap()) |align_expr| {
1490 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
1491 const lparen_token = tree.firstToken(align_expr) - 1;
1492 const align_kw = lparen_token - 1;
1493 const rparen_token = tree.lastToken(align_expr) + 1;
1494 try renderToken(r, align_kw, .none); // align
1495 try renderToken(r, lparen_token, .none); // (
1496 try renderExpression(r, align_expr, .none); // alignment
1497 return renderToken(r, rparen_token, .space); // )
1888 try renderToken(r, t, .maybe_space); // comptime
1889 }
1890
1891 const last_component: enum {
1892 value,
1893 @"align",
1894 type,
1895 identifier,
1896 } = if (field.ast.value_expr != .none)
1897 .value
1898 else if (field.ast.align_expr != .none)
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
14981910 }
1499 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
1911 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
15001913 }
1501 if (field.ast.type_expr != .none and field.ast.value_expr == .none) {
1502 const type_expr = field.ast.type_expr.unwrap().?;
1914
1915 if (field.ast.type_expr.unwrap()) |type_expr| {
15031916 if (!field.ast.tuple_like) {
1504 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1505 try renderToken(r, field.ast.main_token + 1, .space); // :
1917 try renderToken(r, field.ast.main_token + 1, .maybe_space); // :
15061918 }
15071919
1508 if (field.ast.align_expr.unwrap()) |align_expr| {
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 {
1920 if (last_component == .type) {
15171921 return renderExpressionComma(r, type_expr, space); // type
15181922 }
1923 try renderExpression(r, type_expr, .space); // type
15191924 }
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
15461926 if (field.ast.align_expr.unwrap()) |align_expr| {
1547 const lparen_token = tree.firstToken(align_expr) - 1;
1548 const align_kw = lparen_token - 1;
1549 const rparen_token = tree.lastToken(align_expr) + 1;
1550 try renderToken(r, align_kw, .none); // align
1551 try renderToken(r, lparen_token, .none); // (
1927 const align_token = tree.firstToken(align_expr) - 2;
1928 try renderToken(r, align_token, .none); // align
1929 try renderToken(r, align_token + 1, .none); // (
15521930 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);
15541936 }
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);
1559 try renderToken(r, eq_token, eq_space); // =
1938 if (field.ast.value_expr.unwrap()) |value_expr| {
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) {
1562 ais.popIndent();
1563 try renderExpressionComma(r, value_expr, space); // value
1564 return;
1565 }
1566
1567 const maybe_comma = tree.lastToken(value_expr) + 1;
1945 try ais.pushIndent(.after_equals);
1946 try renderToken(r, eq_token, eq_space); // =
1947 if (eq_space == .space) {
1948 ais.popIndent();
1949 return renderExpressionComma(r, value_expr, space); // value
1950 }
15681951
1569 if (tree.tokenTag(maybe_comma) == .comma) {
1570 try renderExpression(r, value_expr, .none); // value
1571 ais.popIndent();
1572 try renderToken(r, maybe_comma, .newline);
1573 } else {
1574 try renderExpression(r, value_expr, space); // value
1575 ais.popIndent();
1576 }
1952 const maybe_comma = tree.lastToken(value_expr) + 1;
1953 if (tree.tokenTag(maybe_comma) == .comma) {
1954 try renderExpression(r, value_expr, .none); // value
1955 ais.popIndent();
1956 try renderToken(r, maybe_comma, .newline);
1957 } else {
1958 try renderExpression(r, value_expr, space); // value
1959 ais.popIndent();
1960 }
1961 } else unreachable;
15771962}
15781963
15791964fn renderBuiltinCall(
......@@ -1587,14 +1972,9 @@ fn renderBuiltinCall(
15871972
15881973 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
15951975 if (r.fixups.rebase_imported_paths) |prefix| {
15961976 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: {
15981978 const param = params[0];
15991979 const str_lit_token = tree.nodeMainToken(param);
16001980 assert(tree.tokenTag(str_lit_token) == .string_literal);
......@@ -1613,45 +1993,69 @@ fn renderBuiltinCall(
16131993 }
16141994 }
16151995
1616 const last_param = params[params.len - 1];
1617 const after_last_param_token = tree.lastToken(last_param) + 1;
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 }
1996 return renderParamList(r, builtin_token + 1, params, space);
1997}
16341998
1635 if (i + 1 < params.len) {
1636 const comma_token = tree.lastToken(param_node) + 1;
1637 try renderToken(r, comma_token, .space); // ,
1638 }
1999fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex {
2000 // These may appear in any order, so we have to check the token_starts array
2001 // to find out which is first.
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;
16392010 }
1640 return renderToken(r, after_last_param_token, space); // )
1641 } else {
1642 // Render one param per line.
1643 try ais.pushIndent(.normal);
1644 try renderToken(r, builtin_token + 1, Space.newline); // (
1645
1646 for (params) |param_node| {
1647 try ais.pushSpace(.comma);
1648 try renderExpression(r, param_node, .comma);
1649 ais.popSpace();
2011 }
2012 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
2013 const tok = tree.firstToken(addrspace_expr) - 3;
2014 const start = tree.tokenStart(tok);
2015 if (start < smallest_start) {
2016 rparen = tok;
2017 smallest_start = start;
2018 }
2019 }
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;
16502034 }
1651 ais.popIndent();
1652
1653 return renderToken(r, after_last_param_token + 1, space); // )
16542035 }
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);
16552059}
16562060
16572061fn 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
16712075
16722076 const return_type = fn_proto.ast.return_type.unwrap().?;
16732077 const maybe_bang = tree.firstToken(return_type) - 1;
1674 const rparen = blk: {
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);
2078 const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
17142079
17152080 // The params list is a sparse set that does *not* include anytype or ... parameters.
17162081
1717 const trailing_comma = tree.tokenTag(rparen - 1) == .comma;
1718 if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
2082 if (isOneLineFnProto(tree, fn_proto, lparen, rparen)) {
17192083 // Render all on one line, no trailing comma.
17202084 try renderToken(r, lparen, .none); // (
17212085
......@@ -1724,10 +2088,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
17242088 while (true) {
17252089 last_param_token += 1;
17262090 switch (tree.tokenTag(last_param_token)) {
1727 .doc_comment => {
1728 try renderToken(r, last_param_token, .newline);
1729 continue;
1730 },
2091 .doc_comment => unreachable,
17312092 .ellipsis3 => {
17322093 try renderToken(r, last_param_token, .none); // ...
17332094 break;
......@@ -1743,7 +2104,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
17432104 },
17442105 .r_paren => break,
17452106 .comma => {
1746 try renderToken(r, last_param_token, .space); // ,
2107 try renderToken(r, last_param_token, .maybe_space); // ,
17472108 continue;
17482109 },
17492110 else => {}, // Parameter type without a name.
......@@ -1753,7 +2114,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
17532114 {
17542115 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
17552116 last_param_token = last_param_token + 1;
1756 try renderToken(r, last_param_token, .space); // :
2117 try renderToken(r, last_param_token, .maybe_space); // :
17572118 last_param_token += 1;
17582119 }
17592120 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
18222183 ais.popIndent();
18232184 }
18242185
1825 try renderToken(r, rparen, .space); // )
2186 try renderToken(r, rparen, .maybe_space); // )
18262187
18272188 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
18282189 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
18312192 try renderToken(r, align_lparen - 1, .none); // align
18322193 try renderToken(r, align_lparen, .none); // (
18332194 try renderExpression(r, align_expr, .none);
1834 try renderToken(r, align_rparen, .space); // )
2195 try renderToken(r, align_rparen, .maybe_space); // )
18352196 }
18362197
18372198 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
18412202 try renderToken(r, align_lparen - 1, .none); // addrspace
18422203 try renderToken(r, align_lparen, .none); // (
18432204 try renderExpression(r, addrspace_expr, .none);
1844 try renderToken(r, align_rparen, .space); // )
2205 try renderToken(r, align_rparen, .maybe_space); // )
18452206 }
18462207
18472208 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
18512212 try renderToken(r, section_lparen - 1, .none); // section
18522213 try renderToken(r, section_lparen, .none); // (
18532214 try renderExpression(r, section_expr, .none);
1854 try renderToken(r, section_rparen, .space); // )
2215 try renderToken(r, section_rparen, .maybe_space); // )
18552216 }
18562217
18572218 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
18652226 try renderToken(r, callconv_lparen - 1, .none); // callconv
18662227 try renderToken(r, callconv_lparen, .none); // (
18672228 try renderExpression(r, callconv_expr, .none);
1868 try renderToken(r, callconv_rparen, .space); // )
2229 try renderToken(r, callconv_rparen, .maybe_space); // )
18692230 }
18702231 }
18712232
......@@ -1890,7 +2251,7 @@ fn renderSwitchCase(
18902251
18912252 // render inline keyword
18922253 if (switch_case.inline_token) |some| {
1893 try renderToken(r, some, .space);
2254 try renderToken(r, some, .maybe_space);
18942255 }
18952256
18962257 // Render everything before the arrow
......@@ -1904,33 +2265,26 @@ fn renderSwitchCase(
19042265 } else {
19052266 // Render on one line
19062267 for (switch_case.ast.values) |value_expr| {
1907 try renderExpression(r, value_expr, .comma_space);
2268 try renderExpression(r, value_expr, .comma_maybe_space);
19082269 }
19092270 }
19102271
1911 // Render the arrow and everything after it
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); // =>
2272 try renderToken(r, switch_case.ast.arrow_token, .maybe_space); // =>
19192273
19202274 if (switch_case.payload_token) |payload_token| {
19212275 try renderToken(r, payload_token - 1, .none); // pipe
1922 const ident = payload_token + @intFromBool(tree.tokenTag(payload_token) == .asterisk);
1923 if (tree.tokenTag(payload_token) == .asterisk) {
2276 var ident = payload_token;
2277 if (tree.tokenTag(ident) == .asterisk) {
19242278 try renderToken(r, payload_token, .none); // asterisk
2279 ident += 1;
19252280 }
19262281 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
19272282 if (tree.tokenTag(ident + 1) == .comma) {
1928 try renderToken(r, ident + 1, .space); // ,
1929 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // identifier
1930 try renderToken(r, ident + 3, pre_target_space); // pipe
1931 } else {
1932 try renderToken(r, ident + 1, pre_target_space); // pipe
2283 ident += 2;
2284 try renderToken(r, ident - 1, .space); // ,
2285 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
19332286 }
2287 try renderToken(r, ident + 1, .maybe_space); // pipe
19342288 }
19352289
19362290 try renderExpression(r, switch_case.ast.target_expr, space);
......@@ -2018,26 +2372,13 @@ fn renderStructInit(
20182372 try ais.pushIndent(.normal);
20192373 try renderToken(r, struct_init.ast.lbrace, .newline);
20202374
2021 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .
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| {
2375 for (0.., struct_init.ast.fields) |i, field_init| {
20352376 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);
20372379 try renderToken(r, init_token - 3, .none); // .
20382380 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;
2040 try renderToken(r, init_token - 1, space_after_equal); // =
2381 try renderToken(r, init_token - 1, .maybe_space); // =
20412382
20422383 try ais.pushSpace(.comma);
20432384 try renderExpressionFixup(r, field_init, .comma);
......@@ -2053,8 +2394,7 @@ fn renderStructInit(
20532394 const init_token = tree.firstToken(field_init);
20542395 try renderToken(r, init_token - 3, .none); // .
20552396 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;
2057 try renderToken(r, init_token - 1, space_after_equal); // =
2397 try renderToken(r, init_token - 1, .maybe_space); // =
20582398 try renderExpressionFixup(r, field_init, .comma_space);
20592399 }
20602400 }
......@@ -2122,174 +2462,176 @@ fn renderArrayInit(
21222462
21232463 try ais.pushIndent(.normal);
21242464 try renderToken(r, array_init.ast.lbrace, .newline);
2125
2126 var expr_index: usize = 0;
2127 while (true) {
2128 const row_size = rowSize(tree, array_init.ast.elements[expr_index..], rbrace);
2129 const row_exprs = array_init.ast.elements[expr_index..];
2130 // A place to store the width of each expression and its column's maximum
2131 const widths = try gpa.alloc(usize, row_exprs.len + row_size);
2132 defer gpa.free(widths);
2133 @memset(widths, 0);
2134
2135 const expr_newlines = try gpa.alloc(bool, row_exprs.len);
2136 defer gpa.free(expr_newlines);
2137 @memset(expr_newlines, false);
2138
2139 const expr_widths = widths[0..row_exprs.len];
2140 const column_widths = widths[row_exprs.len..];
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 = .{
2465 try ais.pushSpace(.comma);
2466
2467 const expr_widths = try gpa.alloc(enum(usize) {
2468 /// The expression contains non-printable characters (e.g. unicode / newlines)
2469 /// or has formatting disabled at the start or end.
2470 nonprint = std.math.maxInt(usize),
2471 _,
2472 }, array_init.ast.elements.len);
2473 defer gpa.free(expr_widths);
2474 {
2475 var buf: Writer.Allocating = .init(gpa);
2476 defer buf.deinit();
2477 var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta);
2478 sub_ais.disabled_offset = ais.disabled_offset;
2479 defer sub_ais.deinit();
2480 var sub_r: Render = .{
21792481 .gpa = r.gpa,
2180 .ais = &auto_indenting_stream,
2482 .ais = &sub_ais,
21812483 .tree = r.tree,
21822484 .fixups = r.fixups,
21832485 };
2184
2185 // Calculate size of columns in current section
2186 var column_counter: usize = 0;
2187 var single_line = true;
2188 var contains_newline = false;
2189 for (section_exprs, 0..) |expr, i| {
2190 const start = sub_expr_buffer.written().len;
2191 sub_expr_buffer_starts[i] = start;
2192
2193 if (i + 1 < section_exprs.len) {
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 }
2486 for (array_init.ast.elements, expr_widths) |e, *width| {
2487 const begin_disabled = sub_ais.disabled_offset != null;
2488 // `.skip` space so trailing commments aren't included
2489 try renderExpressionComma(&sub_r, e, .skip);
2490 if (!begin_disabled and sub_ais.disabled_offset == null) {
2491 const w = buf.written();
2492 width.* = for (w) |c| {
2493 if (!std.ascii.isPrint(c))
2494 break .nonprint;
2495 } else @enumFromInt(w.len - @intFromBool(w[w.len - 1] == ','));
22142496 } else {
2215 try ais.pushSpace(.comma);
2216 try renderExpression(&sub_render, expr, .comma);
2217 ais.popSpace();
2497 width.* = .nonprint;
2498 }
22182499
2219 const written = sub_expr_buffer.written();
2220 const width = written.len - start - 2;
2221 const this_contains_newline = mem.findScalar(u8, written[start .. written.len - 1], '\n') != null;
2222 contains_newline = contains_newline or this_contains_newline;
2223 expr_widths[i] = width;
2224 expr_newlines[i] = contains_newline;
2500 // Write trailing comments since they may enable/disable zig fmt
2501 buf.clearRetainingCapacity();
2502 var after_expr = tree.lastToken(e);
2503 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2504 try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none);
22252505
2226 if (!contains_newline) {
2227 const column = column_counter % row_size;
2228 column_widths[column] = @max(column_widths[column], width);
2229 }
2230 }
2506 buf.clearRetainingCapacity();
22312507 }
2232 sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.written().len;
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;
2508 }
22602509
2261 if (column_counter != row_size - 1) {
2262 if (!expr_newlines[i] and !expr_newlines[i + 1]) {
2263 // Neither the current or next expression is multiline
2264 try renderToken(r, comma, .space); // ,
2265 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
2266 const padding = column_widths[column_counter % row_size] - expr_widths[i];
2267 try ais.splatByteAll(' ', padding);
2510 var remaining_exprs = array_init.ast.elements;
2511 var remaining_widths = expr_widths;
2512 while (remaining_exprs.len != 0) {
2513 var row_size: usize = 1;
2514 for (1.., remaining_exprs, remaining_widths) |len, e, w| {
2515 if (w == .nonprint) break;
2516 row_size = len;
22682517
2269 column_counter += 1;
2270 continue;
2271 }
2272 }
2518 var after_expr = tree.lastToken(e);
2519 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2520 assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
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) {
2275 try renderToken(r, comma, .space); // ,
2276 continue;
2530 // Determine the size of this section
2531 const section_end = end: {
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;
22772539 }
22782540
2279 column_counter = 0;
2280 try renderToken(r, comma, .newline); // ,
2281 try renderExtraNewline(r, next_expr);
2541 var after_expr = tree.lastToken(e);
2542 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
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;
22822568 }
22832569 }
22842570
2285 if (expr_index == array_init.ast.elements.len)
2286 break;
2571 // Render each expression
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 }
22872588 }
22882589
2590 ais.popSpace();
22892591 ais.popIndent();
22902592 return renderToken(r, rbrace, space); // rbrace
22912593}
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
22932635fn renderContainerDecl(
22942636 r: *Render,
22952637 container_decl_node: Ast.Node.Index,
......@@ -2354,27 +2696,7 @@ fn renderContainerDecl(
23542696 }
23552697
23562698 const src_has_trailing_comma = tree.tokenTag(rbrace - 1) == .comma;
2357 if (!src_has_trailing_comma) one_line: {
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
2699 if (!src_has_trailing_comma and isOneLineContainerDecl(tree, container_decl, lbrace, rbrace)) {
23782700 // Print all the declarations on the same line.
23792701 try renderToken(r, lbrace, .space); // lbrace
23802702 for (container_decl.ast.members) |member| {
......@@ -2436,7 +2758,7 @@ fn renderAsm(
24362758 const first_clobber = tree.firstToken(clobbers);
24372759 try renderToken(r, first_clobber - 3, .none);
24382760 try renderToken(r, first_clobber - 2, .none);
2439 try renderToken(r, first_clobber - 1, .space);
2761 try renderToken(r, first_clobber - 1, .maybe_space);
24402762 try renderExpression(r, clobbers, .none);
24412763 ais.popIndent();
24422764 return renderToken(r, asm_node.ast.rparen, space); // rparen
......@@ -2450,6 +2772,9 @@ fn renderAsm(
24502772
24512773 try ais.forcePushIndent(.normal);
24522774 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
24532778 ais.setIndentDelta(asm_indent_delta);
24542779 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
24552780
......@@ -2527,9 +2852,10 @@ fn renderAsm(
25272852 unreachable;
25282853 };
25292854
2530 try renderToken(r, colon3, .space); // :
2855 try renderToken(r, colon3, .maybe_space); // :
25312856 const clobbers = asm_node.ast.clobbers.unwrap().?;
25322857 try renderExpression(r, clobbers, .none);
2858 ais.forceLastIndent(); // Might have been dedented by a multiline string literal
25332859 ais.setIndentDelta(indent_delta);
25342860 ais.popIndent();
25352861 return renderToken(r, asm_node.ast.rparen, space); // rparen
......@@ -2555,7 +2881,7 @@ fn renderParamList(
25552881
25562882 if (params.len == 0) {
25572883 try ais.pushIndent(.normal);
2558 try renderToken(r, lparen, .none);
2884 try renderToken(r, lparen, .none); // (
25592885 ais.popIndent();
25602886 return renderToken(r, lparen + 1, space); // )
25612887 }
......@@ -2590,10 +2916,7 @@ fn renderParamList(
25902916
25912917 if (i + 1 < params.len) {
25922918 const comma = tree.lastToken(param_node) + 1;
2593 const next_multiline_string =
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);
2919 try renderToken(r, comma, .maybe_space);
25972920 }
25982921 }
25992922 ais.popIndent();
......@@ -2655,6 +2978,13 @@ const Space = enum {
26552978 /// Additionally consume the next token if it is a semicolon.
26562979 /// In either case, a newline will be inserted afterwards.
26572980 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,
26582988 /// Skip rendering whitespace and comments. If this is used, the caller
26592989 /// *must* handle whitespace and comments manually.
26602990 skip,
......@@ -2719,6 +3049,16 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
27193049 try ais.insertNewline();
27203050 },
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
27223062 .skip => unreachable,
27233063 }
27243064}
......@@ -2727,9 +3067,9 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {
27273067 const ais = r.ais;
27283068 switch (space) {
27293069 .none => {},
2730 .space => try ais.writeByte(' '),
3070 .space, .maybe_space => try ais.writeByte(' '),
27313071 .newline => try ais.insertNewline(),
2732 .comma => try ais.writeAll(",\n"),
3072 .comma, .comma_maybe_space => try ais.writeAll(",\n"),
27333073 .comma_space => try ais.writeAll(", "),
27343074 .semicolon => try ais.writeAll(";\n"),
27353075 .skip => unreachable,
......@@ -2794,7 +3134,7 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote
27943134 },
27953135 .failure => return renderQuotedIdentifier(r, token_index, space, false),
27963136 }
2797 contents_i += esc_offset;
3137 contents_i = esc_offset;
27983138 continue;
27993139 },
28003140 else => return renderQuotedIdentifier(r, token_index, space, false),
......@@ -2884,6 +3224,9 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void
28843224 }
28853225 },
28863226 .failure => {
3227 // Escape the stray backslash
3228 // This also avoids cases like "\x3\x39" becoming "\x39"
3229 try ais.writeByte('\\');
28873230 try ais.writeAll(escape_sequence);
28883231 },
28893232 }
......@@ -2910,7 +3253,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex)
29103253 const token: Ast.TokenIndex = @intCast(i);
29113254 const start = tree.tokenStart(token) + tree.tokenSlice(token).len;
29123255 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;
29143257 }
29153258
29163259 return false;
......@@ -2926,6 +3269,16 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok
29263269 ) != null;
29273270}
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
29293282/// Assumes that start is the first byte past the previous token and
29303283/// that end is the last byte before the next token.
29313284fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
......@@ -3095,25 +3448,10 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 {
30953448 return ret;
30963449}
30973450
3098fn writeStringLiteralAsIdentifier(r: *Render, token_index: Ast.TokenIndex) !usize {
3099 const tree = r.tree;
3100 const ais = r.ais;
3101 assert(tree.tokenTag(token_index) == .string_literal);
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) {
3451fn hasTrailingComment(tree: Ast, t: Ast.TokenIndex) bool {
3452 const start = tree.tokenStart(t) + tree.tokenSlice(t).len;
3453 const between = tree.source[start..tree.tokenStart(t + 1)];
3454 for (between) |byte| switch (byte) {
31173455 '\n' => return false,
31183456 '/' => return true,
31193457 else => continue,
......@@ -3125,12 +3463,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {
31253463/// start_token and end_token.
31263464fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
31273465 if (start_token + 1 != end_token) return true;
3128 const between_source = tree.source[tree.tokenStart(start_token)..tree.tokenStart(start_token + 1)];
3129 for (between_source) |byte| switch (byte) {
3130 '/' => return true,
3131 else => continue,
3132 };
3133 return false;
3466 return hasComment(tree, start_token, end_token);
31343467}
31353468
31363469fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {
......@@ -3217,29 +3550,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
32173550 };
32183551}
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
32433553/// Automatically inserts indentation of written data by keeping
32443554/// track of the current indentation level
32453555///
......@@ -3477,11 +3787,19 @@ const AutoIndentingStream = struct {
34773787
34783788 pub fn popIndent(ais: *AutoIndentingStream) void {
34793789 if (ais.indent_stack.pop().?.realized) {
3480 assert(ais.indent_count > 0);
34813790 ais.indent_count -= 1;
34823791 }
34833792 }
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
34853803 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {
34863804 return ais.indent_stack.items.len == 0;
34873805 }
......@@ -3489,8 +3807,8 @@ const AutoIndentingStream = struct {
34893807 /// Writes ' ' bytes if the current line is empty
34903808 fn applyIndent(ais: *AutoIndentingStream) Error!void {
34913809 const current_indent = ais.currentIndent();
3492 if (ais.current_line_empty and current_indent > 0) {
3493 if (ais.disabled_offset == null) {
3810 if (ais.current_line_empty) {
3811 if (current_indent > 0 and ais.disabled_offset == null) {
34943812 try ais.underlying_writer.splatByteAll(' ', current_indent);
34953813 }
34963814 ais.applied_indent = current_indent;
lib/std/zig/BuiltinFn.zig+10-5
......@@ -384,28 +384,32 @@ pub const list = list: {
384384 },
385385 },
386386 .{
387 "@cVaArg", .{
387 "@cVaArg",
388 .{
388389 .tag = .c_va_arg,
389390 .param_count = 2,
390391 .illegal_outside_function = true,
391392 },
392393 },
393394 .{
394 "@cVaCopy", .{
395 "@cVaCopy",
396 .{
395397 .tag = .c_va_copy,
396398 .param_count = 1,
397399 .illegal_outside_function = true,
398400 },
399401 },
400402 .{
401 "@cVaEnd", .{
403 "@cVaEnd",
404 .{
402405 .tag = .c_va_end,
403406 .param_count = 1,
404407 .illegal_outside_function = true,
405408 },
406409 },
407410 .{
408 "@cVaStart", .{
411 "@cVaStart",
412 .{
409413 .tag = .c_va_start,
410414 .param_count = 0,
411415 .illegal_outside_function = true,
......@@ -1042,7 +1046,8 @@ pub const list = list: {
10421046 },
10431047 },
10441048 .{
1045 "@workItemId", .{
1049 "@workItemId",
1050 .{
10461051 .tag = .work_item_id,
10471052 .param_count = 1,
10481053 .illegal_outside_function = true,
lib/std/zig/Parse.zig+25-13
......@@ -885,7 +885,8 @@ fn expectContainerField(p: *Parse) !Node.Index {
885885 .tag = .container_field,
886886 .main_token = main_token,
887887 .data = .{ .node_and_extra = .{
888 type_expr, try p.addExtra(Node.ContainerField{
888 type_expr,
889 try p.addExtra(Node.ContainerField{
889890 .align_expr = align_expr.?,
890891 .value_expr = value_expr.?,
891892 }),
......@@ -919,11 +920,15 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
919920 } else {
920921 const assign = try p.expectAssignExpr();
921922 try p.expectSemicolon(.expected_semi_after_stmt, true);
922 return p.addNode(.{
923 .tag = .@"comptime",
924 .main_token = comptime_token,
925 .data = .{ .node = assign },
926 });
923 if (p.nodeTag(assign) != .assign_destructure) {
924 return p.addNode(.{
925 .tag = .@"comptime",
926 .main_token = comptime_token,
927 .data = .{ .node = assign },
928 });
929 } else {
930 return assign;
931 }
927932 }
928933 }
929934
......@@ -1184,7 +1189,8 @@ fn expectIfStatement(p: *Parse) !Node.Index {
11841189 .tag = .@"if",
11851190 .main_token = if_token,
11861191 .data = .{ .node_and_extra = .{
1187 condition, try p.addExtra(Node.If{
1192 condition,
1193 try p.addExtra(Node.If{
11881194 .then_expr = then_expr,
11891195 .else_expr = else_expr,
11901196 }),
......@@ -1365,7 +1371,8 @@ fn parseWhileStatement(p: *Parse) !?Node.Index {
13651371 .tag = .@"while",
13661372 .main_token = while_token,
13671373 .data = .{ .node_and_extra = .{
1368 condition, try p.addExtra(Node.While{
1374 condition,
1375 try p.addExtra(Node.While{
13691376 .cont_expr = .fromOptional(cont_expr),
13701377 .then_expr = then_expr,
13711378 .else_expr = else_expr,
......@@ -1933,7 +1940,8 @@ fn parseTypeExpr(p: *Parse) Error!?Node.Index {
19331940 .tag = .array_type_sentinel,
19341941 .main_token = lbracket,
19351942 .data = .{ .node_and_extra = .{
1936 len_expr.?, try p.addExtra(Node.ArrayTypeSentinel{
1943 len_expr.?,
1944 try p.addExtra(Node.ArrayTypeSentinel{
19371945 .sentinel = sentinel.?,
19381946 .elem_type = elem_type,
19391947 }),
......@@ -2728,7 +2736,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {
27282736 .tag = .while_cont,
27292737 .main_token = while_token,
27302738 .data = .{ .node_and_extra = .{
2731 condition, try p.addExtra(Node.WhileCont{
2739 condition,
2740 try p.addExtra(Node.WhileCont{
27322741 .cont_expr = cont_expr.?,
27332742 .then_expr = then_expr,
27342743 }),
......@@ -2742,7 +2751,8 @@ fn parseWhileTypeExpr(p: *Parse) !?Node.Index {
27422751 .tag = .@"while",
27432752 .main_token = while_token,
27442753 .data = .{ .node_and_extra = .{
2745 condition, try p.addExtra(Node.While{
2754 condition,
2755 try p.addExtra(Node.While{
27462756 .cont_expr = .fromOptional(cont_expr),
27472757 .then_expr = then_expr,
27482758 .else_expr = else_expr,
......@@ -3194,7 +3204,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {
31943204 .tag = .slice_sentinel,
31953205 .main_token = lbracket,
31963206 .data = .{ .node_and_extra = .{
3197 lhs, try p.addExtra(Node.SliceSentinel{
3207 lhs,
3208 try p.addExtra(Node.SliceSentinel{
31983209 .start = index_expr,
31993210 .end = .fromOptional(opt_end_expr),
32003211 .sentinel = sentinel,
......@@ -3217,7 +3228,8 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index {
32173228 .tag = .slice,
32183229 .main_token = lbracket,
32193230 .data = .{ .node_and_extra = .{
3220 lhs, try p.addExtra(Node.Slice{
3231 lhs,
3232 try p.addExtra(Node.Slice{
32213233 .start = index_expr,
32223234 .end = end_expr,
32233235 }),
lib/std/zig/parser_test.zig+1095-8
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const Io = std.Io;
33const Allocator = std.mem.Allocator;
4const Token = std.zig.Token;
45
56test "zig fmt: remove extra whitespace at start and end of file with comment between" {
67 try testTransform(
......@@ -1329,12 +1330,30 @@ test "zig fmt: comment to disable/enable zig fmt" {
13291330 \\const c = d;
13301331 \\// zig fmt: on
13311332 \\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 \\
13321342 ,
13331343 \\const a = b;
13341344 \\// zig fmt: off
13351345 \\const c = d;
13361346 \\// zig fmt: on
13371347 \\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 \\};
13381357 \\
13391358 );
13401359}
......@@ -1986,6 +2005,38 @@ test "zig fmt: array literal vertical column alignment" {
19862005 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
19872006 \\const a = [12]u8{
19882007 \\ 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 \\};
19892040 \\
19902041 ,
19912042 \\const a = []u8{
......@@ -2014,6 +2065,53 @@ test "zig fmt: array literal vertical column alignment" {
20142065 \\ 30,
20152066 \\ 31,
20162067 \\};
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 \\};
20172115 \\
20182116 );
20192117}
......@@ -2542,6 +2640,19 @@ test "zig fmt: first line comment in struct initializer" {
25422640 );
25432641}
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
25452656test "zig fmt: doc comments before struct field" {
25462657 try testCanonical(
25472658 \\pub const Allocator = struct {
......@@ -2872,6 +2983,7 @@ test "zig fmt: destructure" {
28722983 \\ comptime w, var y = .{ 3, 4 };
28732984 \\ comptime var z, x = .{ 5, 6 };
28742985 \\ comptime y, z = .{ 7, 8 };
2986 \\ if (false) unreachable else comptime a, b = .{ 9, 10 };
28752987 \\}
28762988 \\
28772989 );
......@@ -4004,6 +4116,18 @@ test "zig fmt: multiline string in array" {
40044116 \\}
40054117 \\
40064118 );
4119
4120 try testTransform(
4121 \\const a = .{ k, \\
4122 \\};
4123 \\
4124 ,
4125 \\const a = .{
4126 \\ k,
4127 \\ \\
4128 \\};
4129 \\
4130 );
40074131}
40084132
40094133test "zig fmt: if type expr" {
......@@ -4786,8 +4910,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
47864910 \\ 0,
47874911 \\ }}}}}}}};
47884912 \\ myFunc(.{
4789 \\ "aaaaaaa", "bbbbbb", "ccccc",
4790 \\ "dddd", ("eee"), ("fff"),
4913 \\ "aaaaaaa", "bbbbbb", "ccccc",
4914 \\ "dddd", ("eee"), ("fff"),
47914915 \\ ("gggg"),
47924916 \\ // Line comment
47934917 \\ \\Multiline String Literals can be quite long
......@@ -4816,11 +4940,9 @@ test "zig fmt: multiline string literals should play nice with array initializer
48164940 \\ (
48174941 \\ \\ xxx
48184942 \\ ),
4819 \\ "xxx",
4820 \\ "xxx",
4943 \\ "xxx", "xxx",
48214944 \\ },
4822 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4823 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4945 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
48244946 \\ "aaaaaaa", "bbbbbb", "ccccc", // -
48254947 \\ "dddd", ("eee"), ("fff"),
48264948 \\ .{
......@@ -4828,8 +4950,7 @@ test "zig fmt: multiline string literals should play nice with array initializer
48284950 \\ (
48294951 \\ \\ xxx
48304952 \\ ),
4831 \\ "xxxxxxxxxxxxxx",
4832 \\ "xxx",
4953 \\ "xxxxxxxxxxxxxx", "xxx",
48334954 \\ },
48344955 \\ .{
48354956 \\ (
......@@ -6028,6 +6149,695 @@ test "zig fmt: extern addrspace in struct" {
60286149 );
60296150}
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
60316841test "recovery: top level" {
60326842 try testError(
60336843 \\test "" {inline}
......@@ -6336,6 +7146,23 @@ test "ampersand" {
63367146 , &.{});
63377147}
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
63397166var fixed_buffer_mem: [100 * 1024]u8 = undefined;
63407167
63417168fn 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 {
64267253 var fba: std.heap.FixedBufferAllocator = .init(&fixed_buffer_mem);
64277254 _ = std.zig.Ast.parseTokens(fba.allocator(), tokens.source(), tokens.list(), mode) catch return;
64287255}
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{
3838};
3939
4040pub 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
4343pub 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
4646pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
4747
src/link.zig+2-1
......@@ -2097,7 +2097,8 @@ fn resolveLibInput(
20972097 const test_path: Path = .{
20982098 .root_dir = lib_directory,
20992099 .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) {
21012102 .static => target.staticLibSuffix(),
21022103 .dynamic => target.dynamicLibSuffix(),
21032104 },