authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2025-07-28 13:18:09-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-12 17:22:46-04:00
logfcc54ca981d30af0a187c9d187b2a63ce0195f3c
treef428617b308f61c961e1bd02de0ad26646d5608b
parent476c3a1f379eadbd200d568340a2f479d5b6d260

zig fmt: fix many bugs with multiline string literals

Adds two new space modes maybe_space and comma_maybe_space which only render the space when the next token is not a multiline string literal. This is used in a lot of places to avoid a trailing space on lines caused by multiline string literals forcing newlines. Fixes places where the node tag is compared to be a multiline string literal to instead check if the first token is a multiline string literal line. This fixes cases like `\\ ++ text`. This commit also rewrites quite a bit of code to be clearer / deduplicated.

2 files changed, 586 insertions(+), 311 deletions(-)

lib/std/zig/Ast/Render.zig+273-311
...@@ -346,20 +346,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -346,20 +346,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
346 const next_token_tag = tree.tokenTag(next_token);346 const next_token_tag = tree.tokenTag(next_token);
347347
348 // dedent the next thing that comes after a multiline string literal348 // dedent the next thing that comes after a multiline string literal
349 if (!ais.indentStackEmpty() and349 if (next_token_tag != .colon and
350 next_token_tag != .colon and350 !ais.indentStackEmpty() and
351 ((next_token_tag != .semicolon and next_token_tag != .comma) or351 ais.lastSpaceModeIndent() < ais.currentIndent())
352 ais.lastSpaceModeIndent() < ais.currentIndent()))
353 {352 {
354 ais.popIndent();353 const indent_top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
355 try ais.pushIndent(.normal);354 if (indent_top.realized) {
355 indent_top.realized = false;
356 ais.indent_count -= 1;
357 }
356 }358 }
357359
358 switch (space) {360 switch (space) {
359 .none, .space, .newline, .skip => {},361 .none, .space, .newline, .maybe_space, .skip => {},
360 .semicolon => if (next_token_tag == .semicolon) try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),362 .semicolon => if (next_token_tag == .semicolon)
361 .comma => if (next_token_tag == .comma) try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),363 try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
362 .comma_space => if (next_token_tag == .comma) try renderToken(r, next_token, .space),364 .comma => if (next_token_tag == .comma)
365 try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
366 .comma_space => if (next_token_tag == .comma)
367 try renderToken(r, next_token, .space),
368 .comma_maybe_space => if (next_token_tag == .comma)
369 try renderToken(r, next_token, .maybe_space),
363 }370 }
364 },371 },
365372
...@@ -384,11 +391,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -384,11 +391,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
384 const defer_token = tree.nodeMainToken(node);391 const defer_token = tree.nodeMainToken(node);
385 const maybe_payload_token, const expr = tree.nodeData(node).opt_token_and_node;392 const maybe_payload_token, const expr = tree.nodeData(node).opt_token_and_node;
386393
387 try renderToken(r, defer_token, .space);394 try renderToken(r, defer_token, .maybe_space);
388 if (maybe_payload_token.unwrap()) |payload_token| {395 if (maybe_payload_token.unwrap()) |payload_token| {
389 try renderToken(r, payload_token - 1, .none); // |396 try renderToken(r, payload_token - 1, .none); // |
390 try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier397 try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier
391 try renderToken(r, payload_token + 1, .space); // |398 try renderToken(r, payload_token + 1, .maybe_space); // |
392 }399 }
393 return renderExpression(r, expr, space);400 return renderExpression(r, expr, space);
394 },401 },
...@@ -400,7 +407,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -400,7 +407,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
400 => {407 => {
401 const main_token = tree.nodeMainToken(node);408 const main_token = tree.nodeMainToken(node);
402 const item = tree.nodeData(node).node;409 const item = tree.nodeData(node).node;
403 try renderToken(r, main_token, .space);410 try renderToken(r, main_token, .maybe_space);
404 return renderExpression(r, item, space);411 return renderExpression(r, item, space);
405 },412 },
406413
...@@ -409,8 +416,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -409,8 +416,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
409 const lhs, const rhs = tree.nodeData(node).node_and_node;416 const lhs, const rhs = tree.nodeData(node).node_and_node;
410 const fallback_first = tree.firstToken(rhs);417 const fallback_first = tree.firstToken(rhs);
411418
412 const same_line = tree.tokensOnSameLine(main_token, fallback_first);419 const seperate_line = !tree.tokensOnSameLine(main_token, fallback_first) or
413 const after_op_space = if (same_line) Space.space else Space.newline;420 tree.tokenTag(fallback_first) == .multiline_string_literal_line;
421 const after_op_space: Space = if (seperate_line) .newline else .space;
414422
415 try renderExpression(r, lhs, .space); // target423 try renderExpression(r, lhs, .space); // target
416424
...@@ -489,11 +497,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -489,11 +497,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
489 try renderExpression(r, lhs, .space);497 try renderExpression(r, lhs, .space);
490 const op_token = tree.nodeMainToken(node);498 const op_token = tree.nodeMainToken(node);
491 try ais.pushIndent(.after_equals);499 try ais.pushIndent(.after_equals);
492 if (tree.tokensOnSameLine(op_token, op_token + 1)) {500 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
493 try renderToken(r, op_token, .space);501 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
494 } else {502 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
495 try renderToken(r, op_token, .newline);
496 }
497 try renderExpression(r, rhs, space);503 try renderExpression(r, rhs, space);
498 ais.popIndent();504 ais.popIndent();
499 },505 },
...@@ -532,11 +538,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -532,11 +538,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
532 try renderExpression(r, lhs, .space);538 try renderExpression(r, lhs, .space);
533 const op_token = tree.nodeMainToken(node);539 const op_token = tree.nodeMainToken(node);
534 try ais.pushIndent(.binop);540 try ais.pushIndent(.binop);
535 if (tree.tokensOnSameLine(op_token, op_token + 1)) {541 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
536 try renderToken(r, op_token, .space);542 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
537 } else {543 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
538 try renderToken(r, op_token, .newline);
539 }
540 try renderExpression(r, rhs, space);544 try renderExpression(r, rhs, space);
541 ais.popIndent();545 ais.popIndent();
542 },546 },
...@@ -544,11 +548,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -544,11 +548,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
544 .assign_destructure => {548 .assign_destructure => {
545 const full = tree.assignDestructure(node);549 const full = tree.assignDestructure(node);
546 if (full.comptime_token) |comptime_token| {550 if (full.comptime_token) |comptime_token| {
547 try renderToken(r, comptime_token, .space);551 try renderToken(r, comptime_token, .maybe_space);
548 }552 }
549553
550 for (full.ast.variables, 0..) |variable_node, i| {554 for (full.ast.variables, 0..) |variable_node, i| {
551 const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;555 const variable_space: Space = if (i == full.ast.variables.len - 1) .maybe_space else .comma_maybe_space;
552 switch (tree.nodeTag(variable_node)) {556 switch (tree.nodeTag(variable_node)) {
553 .global_var_decl,557 .global_var_decl,
554 .local_var_decl,558 .local_var_decl,
...@@ -561,11 +565,10 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -561,11 +565,10 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
561 }565 }
562 }566 }
563 try ais.pushIndent(.after_equals);567 try ais.pushIndent(.after_equals);
564 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {568 const expr_seperate_line =
565 try renderToken(r, full.ast.equal_token, .space);569 !tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1) or
566 } else {570 tree.tokenTag(full.ast.equal_token + 1) == .multiline_string_literal_line;
567 try renderToken(r, full.ast.equal_token, .newline);571 try renderToken(r, full.ast.equal_token, if (expr_seperate_line) .newline else .space);
568 }
569 try renderExpression(r, full.ast.value_expr, space);572 try renderExpression(r, full.ast.value_expr, space);
570 ais.popIndent();573 ais.popIndent();
571 },574 },
...@@ -584,7 +587,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -584,7 +587,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
584 .@"try",587 .@"try",
585 .@"resume",588 .@"resume",
586 => {589 => {
587 try renderToken(r, tree.nodeMainToken(node), .space);590 try renderToken(r, tree.nodeMainToken(node), .maybe_space);
588 return renderExpression(r, tree.nodeData(node).node, space);591 return renderExpression(r, tree.nodeData(node).node, space);
589 },592 },
590593
...@@ -668,30 +671,23 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -668,30 +671,23 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
668 .@"break", .@"continue" => {671 .@"break", .@"continue" => {
669 const main_token = tree.nodeMainToken(node);672 const main_token = tree.nodeMainToken(node);
670 const opt_label_token, const opt_target = tree.nodeData(node).opt_token_and_opt_node;673 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) {674
672 try renderToken(r, main_token, space); // break/continue675 const before_target_space: Space = if (opt_target != .none) .maybe_space else space;
673 } else if (opt_label_token == .none and opt_target != .none) {676 const before_label_space: Space = if (opt_label_token != .none) .space else before_target_space;
674 const target = opt_target.unwrap().?;677
675 try renderToken(r, main_token, .space); // break/continue678 try renderToken(r, main_token, before_label_space);
676 try renderExpression(r, target, space);679 if (opt_label_token.unwrap()) |label_token| {
677 } else if (opt_label_token != .none and opt_target == .none) {
678 const label_token = opt_label_token.unwrap().?;
679 try renderToken(r, main_token, .space); // break/continue
680 try renderToken(r, label_token - 1, .none); // :
681 try renderIdentifier(r, label_token, space, .eagerly_unquote); // identifier
682 } else if (opt_label_token != .none and opt_target != .none) {
683 const label_token = opt_label_token.unwrap().?;
684 const target = opt_target.unwrap().?;
685 try renderToken(r, main_token, .space); // break/continue
686 try renderToken(r, label_token - 1, .none); // :680 try renderToken(r, label_token - 1, .none); // :
687 try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier681 try renderIdentifier(r, label_token, before_target_space, .eagerly_unquote); // identifier
682 }
683 if (opt_target.unwrap()) |target| {
688 try renderExpression(r, target, space);684 try renderExpression(r, target, space);
689 } else unreachable;685 }
690 },686 },
691687
692 .@"return" => {688 .@"return" => {
693 if (tree.nodeData(node).opt_node.unwrap()) |expr| {689 if (tree.nodeData(node).opt_node.unwrap()) |expr| {
694 try renderToken(r, tree.nodeMainToken(node), .space);690 try renderToken(r, tree.nodeMainToken(node), .maybe_space);
695 try renderExpression(r, expr, space);691 try renderExpression(r, expr, space);
696 } else {692 } else {
697 try renderToken(r, tree.nodeMainToken(node), space);693 try renderToken(r, tree.nodeMainToken(node), space);
...@@ -951,6 +947,7 @@ fn renderArrayType(...@@ -951,6 +947,7 @@ fn renderArrayType(
951fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {947fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
952 const tree = r.tree;948 const tree = r.tree;
953 const main_token = ptr_type.ast.main_token;949 const main_token = ptr_type.ast.main_token;
950
954 switch (ptr_type.size) {951 switch (ptr_type.size) {
955 .one => {952 .one => {
956 // Since ** tokens exist and the same token is shared by two953 // Since ** tokens exist and the same token is shared by two
...@@ -997,11 +994,36 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi...@@ -997,11 +994,36 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
997 },994 },
998 }995 }
999996
997 // .maybe_space cannot be used at the end of each qualifier since they may be reordered
998 const final_qual: enum {
999 @"volatile",
1000 @"const",
1001 @"addrspace",
1002 @"align",
1003 @"allowzero",
1004 none,
1005 } = if (ptr_type.volatile_token != null)
1006 .@"volatile"
1007 else if (ptr_type.const_token != null)
1008 .@"const"
1009 else if (ptr_type.ast.addrspace_node != .none)
1010 .@"addrspace"
1011 else if (ptr_type.ast.align_node != .none)
1012 .@"align"
1013 else if (ptr_type.allowzero_token != null)
1014 .@"allowzero"
1015 else
1016 .none;
1017 const final_qual_space: Space = if (tree.tokenTag(tree.firstToken(ptr_type.ast.child_type)) !=
1018 .multiline_string_literal_line) .space else .none;
1019
1000 if (ptr_type.allowzero_token) |allowzero_token| {1020 if (ptr_type.allowzero_token) |allowzero_token| {
1001 try renderToken(r, allowzero_token, .space);1021 const this_space: Space = if (final_qual == .@"allowzero") final_qual_space else .space;
1022 try renderToken(r, allowzero_token, this_space);
1002 }1023 }
10031024
1004 if (ptr_type.ast.align_node.unwrap()) |align_node| {1025 if (ptr_type.ast.align_node.unwrap()) |align_node| {
1026 const this_space: Space = if (final_qual == .@"align") final_qual_space else .space;
1005 const align_first = tree.firstToken(align_node);1027 const align_first = tree.firstToken(align_node);
1006 try renderToken(r, align_first - 2, .none); // align1028 try renderToken(r, align_first - 2, .none); // align
1007 try renderToken(r, align_first - 1, .none); // lparen1029 try renderToken(r, align_first - 1, .none); // lparen
...@@ -1012,26 +1034,29 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi...@@ -1012,26 +1034,29 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
1012 try renderExpression(r, bit_range_start, .none);1034 try renderExpression(r, bit_range_start, .none);
1013 try renderToken(r, tree.firstToken(bit_range_end) - 1, .none); // colon1035 try renderToken(r, tree.firstToken(bit_range_end) - 1, .none); // colon
1014 try renderExpression(r, bit_range_end, .none);1036 try renderExpression(r, bit_range_end, .none);
1015 try renderToken(r, tree.lastToken(bit_range_end) + 1, .space); // rparen1037 try renderToken(r, tree.lastToken(bit_range_end) + 1, this_space); // rparen
1016 } else {1038 } else {
1017 try renderToken(r, tree.lastToken(align_node) + 1, .space); // rparen1039 try renderToken(r, tree.lastToken(align_node) + 1, this_space); // rparen
1018 }1040 }
1019 }1041 }
10201042
1021 if (ptr_type.ast.addrspace_node.unwrap()) |addrspace_node| {1043 if (ptr_type.ast.addrspace_node.unwrap()) |addrspace_node| {
1044 const this_space: Space = if (final_qual == .@"addrspace") final_qual_space else .space;
1022 const addrspace_first = tree.firstToken(addrspace_node);1045 const addrspace_first = tree.firstToken(addrspace_node);
1023 try renderToken(r, addrspace_first - 2, .none); // addrspace1046 try renderToken(r, addrspace_first - 2, .none); // addrspace
1024 try renderToken(r, addrspace_first - 1, .none); // lparen1047 try renderToken(r, addrspace_first - 1, .none); // lparen
1025 try renderExpression(r, addrspace_node, .none);1048 try renderExpression(r, addrspace_node, .none);
1026 try renderToken(r, tree.lastToken(addrspace_node) + 1, .space); // rparen1049 try renderToken(r, tree.lastToken(addrspace_node) + 1, this_space); // rparen
1027 }1050 }
10281051
1029 if (ptr_type.const_token) |const_token| {1052 if (ptr_type.const_token) |const_token| {
1030 try renderToken(r, const_token, .space);1053 const this_space: Space = if (final_qual == .@"const") final_qual_space else .space;
1054 try renderToken(r, const_token, this_space);
1031 }1055 }
10321056
1033 if (ptr_type.volatile_token) |volatile_token| {1057 if (ptr_type.volatile_token) |volatile_token| {
1034 try renderToken(r, volatile_token, .space);1058 const this_space: Space = if (final_qual == .@"volatile") final_qual_space else unreachable;
1059 try renderToken(r, volatile_token, this_space);
1035 }1060 }
10361061
1037 try renderExpression(r, ptr_type.ast.child_type, space);1062 try renderExpression(r, ptr_type.ast.child_type, space);
...@@ -1044,12 +1069,14 @@ fn renderSlice(...@@ -1044,12 +1069,14 @@ fn renderSlice(
1044 space: Space,1069 space: Space,
1045) Error!void {1070) Error!void {
1046 const tree = r.tree;1071 const tree = r.tree;
1047 const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or1072 const space_around_dots = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
1048 if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;1073 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;1074 const after_start_space: Space = if (space_around_dots) .space else .none;
1050 const after_dots_space = if (slice.ast.end != .none)1075 const before_sentinel_space: Space = if (slice.ast.sentinel != .none) .space else .none;
1051 after_start_space1076 const after_dots_space: Space = if (slice.ast.end != .none)
1052 else if (slice.ast.sentinel != .none) Space.space else Space.none;1077 if (space_around_dots) .maybe_space else .none
1078 else
1079 before_sentinel_space;
10531080
1054 try renderExpression(r, slice.ast.sliced, .none);1081 try renderExpression(r, slice.ast.sliced, .none);
1055 try renderToken(r, slice.ast.lbracket, .none); // lbracket1082 try renderToken(r, slice.ast.lbracket, .none); // lbracket
...@@ -1059,8 +1086,7 @@ fn renderSlice(...@@ -1059,8 +1086,7 @@ fn renderSlice(
1059 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")1086 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")
10601087
1061 if (slice.ast.end.unwrap()) |end| {1088 if (slice.ast.end.unwrap()) |end| {
1062 const after_end_space = if (slice.ast.sentinel != .none) Space.space else Space.none;1089 try renderExpression(r, end, before_sentinel_space);
1063 try renderExpression(r, end, after_end_space);
1064 }1090 }
10651091
1066 if (slice.ast.sentinel.unwrap()) |sentinel| {1092 if (slice.ast.sentinel.unwrap()) |sentinel| {
...@@ -1088,7 +1114,7 @@ fn renderAsmOutput(...@@ -1088,7 +1114,7 @@ fn renderAsmOutput(
10881114
1089 if (tree.tokenTag(symbolic_name + 4) == .arrow) {1115 if (tree.tokenTag(symbolic_name + 4) == .arrow) {
1090 const type_expr, const rparen = tree.nodeData(asm_output).opt_node_and_token;1116 const type_expr, const rparen = tree.nodeData(asm_output).opt_node_and_token;
1091 try renderToken(r, symbolic_name + 4, .space); // ->1117 try renderToken(r, symbolic_name + 4, .maybe_space); // ->
1092 try renderExpression(r, type_expr.unwrap().?, Space.none);1118 try renderExpression(r, type_expr.unwrap().?, Space.none);
1093 return renderToken(r, rparen, space);1119 return renderToken(r, rparen, space);
1094 } else {1120 } else {
...@@ -1169,33 +1195,38 @@ fn renderVarDeclWithoutFixups(...@@ -1169,33 +1195,38 @@ fn renderVarDeclWithoutFixups(
11691195
1170 try renderToken(r, var_decl.ast.mut_token, .space); // var1196 try renderToken(r, var_decl.ast.mut_token, .space); // var
11711197
1172 if (var_decl.ast.type_node != .none or var_decl.ast.align_node != .none or1198 const last_component: enum {
1173 var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or1199 value,
1174 var_decl.ast.init_node != .none)1200 @"linksection",
1175 {1201 @"addrspace",
1176 const name_space = if (var_decl.ast.type_node == .none and1202 @"align",
1177 (var_decl.ast.align_node != .none or1203 type,
1178 var_decl.ast.addrspace_node != .none or1204 identifier,
1179 var_decl.ast.section_node != .none or1205 } = if (var_decl.ast.init_node != .none)
1180 var_decl.ast.init_node != .none))1206 .value
1181 Space.space1207 else if (var_decl.ast.section_node != .none)
1182 else1208 .@"linksection"
1183 Space.none;1209 else if (var_decl.ast.addrspace_node != .none)
11841210 .@"addrspace"
1185 try renderIdentifier(r, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name1211 else if (var_decl.ast.align_node != .none)
1186 } else {1212 .@"align"
1187 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name1213 else if (var_decl.ast.type_node != .none)
1214 .type
1215 else
1216 .identifier;
1217
1218 if (last_component == .identifier) {
1219 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing);
1188 }1220 }
1221 const after_ident_space: Space = if (var_decl.ast.type_node != .none) .none else .space;
1222 try renderIdentifier(r, var_decl.ast.mut_token + 1, after_ident_space, .preserve_when_shadowing);
11891223
1190 if (var_decl.ast.type_node.unwrap()) |type_node| {1224 if (var_decl.ast.type_node.unwrap()) |type_node| {
1191 try renderToken(r, var_decl.ast.mut_token + 2, Space.space); // :1225 try renderToken(r, var_decl.ast.mut_token + 2, .maybe_space); // :
1192 if (var_decl.ast.align_node != .none or var_decl.ast.addrspace_node != .none or1226 if (last_component == .type) {
1193 var_decl.ast.section_node != .none or var_decl.ast.init_node != .none)
1194 {
1195 try renderExpression(r, type_node, .space);
1196 } else {
1197 return renderExpression(r, type_node, space);1227 return renderExpression(r, type_node, space);
1198 }1228 }
1229 try renderExpression(r, type_node, .space);
1199 }1230 }
12001231
1201 if (var_decl.ast.align_node.unwrap()) |align_node| {1232 if (var_decl.ast.align_node.unwrap()) |align_node| {
...@@ -1205,13 +1236,10 @@ fn renderVarDeclWithoutFixups(...@@ -1205,13 +1236,10 @@ fn renderVarDeclWithoutFixups(
1205 try renderToken(r, align_kw, Space.none); // align1236 try renderToken(r, align_kw, Space.none); // align
1206 try renderToken(r, lparen, Space.none); // (1237 try renderToken(r, lparen, Space.none); // (
1207 try renderExpression(r, align_node, Space.none);1238 try renderExpression(r, align_node, Space.none);
1208 if (var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or1239 if (last_component == .@"align") {
1209 var_decl.ast.init_node != .none)
1210 {
1211 try renderToken(r, rparen, .space); // )
1212 } else {
1213 return renderToken(r, rparen, space); // )1240 return renderToken(r, rparen, space); // )
1214 }1241 }
1242 try renderToken(r, rparen, .space); // )
1215 }1243 }
12161244
1217 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {1245 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
...@@ -1221,12 +1249,10 @@ fn renderVarDeclWithoutFixups(...@@ -1221,12 +1249,10 @@ fn renderVarDeclWithoutFixups(
1221 try renderToken(r, addrspace_kw, Space.none); // addrspace1249 try renderToken(r, addrspace_kw, Space.none); // addrspace
1222 try renderToken(r, lparen, Space.none); // (1250 try renderToken(r, lparen, Space.none); // (
1223 try renderExpression(r, addrspace_node, Space.none);1251 try renderExpression(r, addrspace_node, Space.none);
1224 if (var_decl.ast.section_node != .none or var_decl.ast.init_node != .none) {1252 if (last_component == .@"addrspace") {
1225 try renderToken(r, rparen, .space); // )1253 return renderToken(r, rparen, space); // )
1226 } else {
1227 try renderToken(r, rparen, .none); // )
1228 return renderToken(r, rparen + 1, Space.newline); // ;
1229 }1254 }
1255 try renderToken(r, rparen, .space); // )
1230 }1256 }
12311257
1232 if (var_decl.ast.section_node.unwrap()) |section_node| {1258 if (var_decl.ast.section_node.unwrap()) |section_node| {
...@@ -1236,17 +1262,19 @@ fn renderVarDeclWithoutFixups(...@@ -1236,17 +1262,19 @@ fn renderVarDeclWithoutFixups(
1236 try renderToken(r, section_kw, Space.none); // linksection1262 try renderToken(r, section_kw, Space.none); // linksection
1237 try renderToken(r, lparen, Space.none); // (1263 try renderToken(r, lparen, Space.none); // (
1238 try renderExpression(r, section_node, Space.none);1264 try renderExpression(r, section_node, Space.none);
1239 if (var_decl.ast.init_node != .none) {1265 if (last_component == .@"linksection") {
1240 try renderToken(r, rparen, .space); // )
1241 } else {
1242 return renderToken(r, rparen, space); // )1266 return renderToken(r, rparen, space); // )
1243 }1267 }
1268 try renderToken(r, rparen, .space); // )
1244 }1269 }
12451270
1271 assert(last_component == .value);
1246 const init_node = var_decl.ast.init_node.unwrap().?;1272 const init_node = var_decl.ast.init_node.unwrap().?;
12471273
1248 const eq_token = tree.firstToken(init_node) - 1;1274 const eq_token = tree.firstToken(init_node) - 1;
1249 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;1275 const rhs_seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
1276 tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
1277 const eq_space: Space = if (rhs_seperate_line) .newline else .space;
1250 try ais.pushIndent(.after_equals);1278 try ais.pushIndent(.after_equals);
1251 try renderToken(r, eq_token, eq_space); // =1279 try renderToken(r, eq_token, eq_space); // =
1252 try renderExpression(r, init_node, space); // ;1280 try renderExpression(r, init_node, space); // ;
...@@ -1346,8 +1374,10 @@ fn renderThenElse(...@@ -1346,8 +1374,10 @@ fn renderThenElse(
1346 const tree = r.tree;1374 const tree = r.tree;
1347 const ais = r.ais;1375 const ais = r.ais;
1348 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));1376 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));
1377 const then_expr_first_token = tree.firstToken(then_expr);
1349 const indent_then_expr = !then_expr_is_block and1378 const indent_then_expr = !then_expr_is_block and
1350 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));1379 (!tree.tokensOnSameLine(last_prefix_token, then_expr_first_token) or
1380 tree.tokenTag(then_expr_first_token) == .multiline_string_literal_line);
13511381
1352 if (indent_then_expr) try ais.pushIndent(.normal);1382 if (indent_then_expr) try ais.pushIndent(.normal);
13531383
...@@ -1381,7 +1411,9 @@ fn renderThenElse(...@@ -1381,7 +1411,9 @@ fn renderThenElse(
13811411
1382 const indent_else_expr = indent_then_expr and1412 const indent_else_expr = indent_then_expr and
1383 !nodeIsBlock(tree.nodeTag(else_expr)) and1413 !nodeIsBlock(tree.nodeTag(else_expr)) and
1384 !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr));1414 !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr)) or
1415 tree.tokenTag(tree.firstToken(else_expr)) ==
1416 .multiline_string_literal_line;
1385 if (indent_else_expr) {1417 if (indent_else_expr) {
1386 try ais.pushIndent(.normal);1418 try ais.pushIndent(.normal);
1387 try renderToken(r, last_else_token, .newline);1419 try renderToken(r, last_else_token, .newline);
...@@ -1417,44 +1449,29 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {...@@ -1417,44 +1449,29 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
1417 try renderParamList(r, lparen, for_node.ast.inputs, .space);1449 try renderParamList(r, lparen, for_node.ast.inputs, .space);
14181450
1419 var cur = for_node.payload_token;1451 var cur = for_node.payload_token;
1420 const pipe = std.mem.findScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;1452 const pipe = std.mem.findScalarPos(Token.Tag, token_tags, cur, .pipe).?;
1421 if (tree.tokenTag(@intCast(pipe - 1)) == .comma) {1453 const capture_trailing_comma = token_tags[@intCast(pipe - 1)] == .comma;
1454
1455 if (capture_trailing_comma)
1422 try ais.pushIndent(.normal);1456 try ais.pushIndent(.normal);
1423 try renderToken(r, cur - 1, .newline); // |1457 try renderToken(r, cur - 1, if (capture_trailing_comma) .newline else .none); // |
1424 while (true) {1458 while (true) {
1425 if (tree.tokenTag(cur) == .asterisk) {1459 if (token_tags[cur] == .asterisk) {
1426 try renderToken(r, cur, .none); // *1460 try renderToken(r, cur, .none); // *
1427 cur += 1;
1428 }
1429 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1430 cur += 1;1461 cur += 1;
1431 if (tree.tokenTag(cur) == .comma) {
1432 try renderToken(r, cur, .newline); // ,
1433 cur += 1;
1434 }
1435 if (tree.tokenTag(cur) == .pipe) {
1436 break;
1437 }
1438 }1462 }
1439 ais.popIndent();1463 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1440 } else {1464 cur += 1;
1441 try renderToken(r, cur - 1, .none); // |1465 if (token_tags[cur] == .comma) {
1442 while (true) {1466 try renderToken(r, cur, if (capture_trailing_comma) .newline else .space); // ,
1443 if (tree.tokenTag(cur) == .asterisk) {
1444 try renderToken(r, cur, .none); // *
1445 cur += 1;
1446 }
1447 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1448 cur += 1;1467 cur += 1;
1449 if (tree.tokenTag(cur) == .comma) {1468 }
1450 try renderToken(r, cur, .space); // ,1469 if (token_tags[cur] == .pipe) {
1451 cur += 1;1470 break;
1452 }
1453 if (tree.tokenTag(cur) == .pipe) {
1454 break;
1455 }
1456 }1471 }
1457 }1472 }
1473 if (capture_trailing_comma)
1474 ais.popIndent();
14581475
1459 try renderThenElse(1476 try renderThenElse(
1460 r,1477 r,
...@@ -1483,97 +1500,80 @@ fn renderContainerField(...@@ -1483,97 +1500,80 @@ fn renderContainerField(
1483 };1500 };
14841501
1485 if (field.comptime_token) |t| {1502 if (field.comptime_token) |t| {
1486 try renderToken(r, t, .space); // comptime1503 try renderToken(r, t, .maybe_space); // comptime
1487 }1504 }
1488 if (field.ast.type_expr == .none and field.ast.value_expr == .none) {1505
1489 if (field.ast.align_expr.unwrap()) |align_expr| {1506 const last_component: enum {
1490 try renderIdentifier(r, field.ast.main_token, .space, quote); // name1507 value,
1491 const lparen_token = tree.firstToken(align_expr) - 1;1508 @"align",
1492 const align_kw = lparen_token - 1;1509 type,
1493 const rparen_token = tree.lastToken(align_expr) + 1;1510 identifier,
1494 try renderToken(r, align_kw, .none); // align1511 } = if (field.ast.value_expr != .none)
1495 try renderToken(r, lparen_token, .none); // (1512 .value
1496 try renderExpression(r, align_expr, .none); // alignment1513 else if (field.ast.align_expr != .none)
1497 return renderToken(r, rparen_token, .space); // )1514 .@"align"
1498 }1515 else if (field.ast.type_expr != .none)
1499 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name1516 .type
1500 }1517 else if (!field.ast.tuple_like)
1501 if (field.ast.type_expr != .none and field.ast.value_expr == .none) {1518 .identifier
1502 const type_expr = field.ast.type_expr.unwrap().?;1519 else
1503 if (!field.ast.tuple_like) {1520 unreachable;
1504 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1505 try renderToken(r, field.ast.main_token + 1, .space); // :
1506 }
15071521
1508 if (field.ast.align_expr.unwrap()) |align_expr| {1522 if (!field.ast.tuple_like) {
1509 try renderExpression(r, type_expr, .space); // type1523 if (last_component == .identifier) {
1510 const align_token = tree.firstToken(align_expr) - 2;1524 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
1511 try renderToken(r, align_token, .none); // align
1512 try renderToken(r, align_token + 1, .none); // (
1513 try renderExpression(r, align_expr, .none); // alignment
1514 const rparen = tree.lastToken(align_expr) + 1;
1515 return renderTokenComma(r, rparen, space); // )
1516 } else {
1517 return renderExpressionComma(r, type_expr, space); // type
1518 }1525 }
1526 const this_space: Space = if (field.ast.type_expr != .none) .none else .space;
1527 try renderIdentifier(r, field.ast.main_token, this_space, quote); // name
1519 }1528 }
1520 if (field.ast.type_expr == .none and field.ast.value_expr != .none) {
1521 const value_expr = field.ast.value_expr.unwrap().?;
15221529
1523 try renderIdentifier(r, field.ast.main_token, .space, quote); // name1530 if (field.ast.type_expr.unwrap()) |type_expr| {
1524 if (field.ast.align_expr.unwrap()) |align_expr| {1531 if (!field.ast.tuple_like) {
1525 const lparen_token = tree.firstToken(align_expr) - 1;1532 try renderToken(r, field.ast.main_token + 1, .maybe_space); // :
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 }
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 }
15401534
1541 const type_expr = field.ast.type_expr.unwrap().?;1535 if (last_component == .type) {
1542 const value_expr = field.ast.value_expr.unwrap().?;1536 return renderExpressionComma(r, type_expr, space); // type
15431537 }
1544 try renderExpression(r, type_expr, .space); // type1538 try renderExpression(r, type_expr, .space); // type
1539 }
15451540
1546 if (field.ast.align_expr.unwrap()) |align_expr| {1541 if (field.ast.align_expr.unwrap()) |align_expr| {
1547 const lparen_token = tree.firstToken(align_expr) - 1;1542 const align_token = tree.firstToken(align_expr) - 2;
1548 const align_kw = lparen_token - 1;1543 try renderToken(r, align_token, .none); // align
1549 const rparen_token = tree.lastToken(align_expr) + 1;1544 try renderToken(r, align_token + 1, .none); // (
1550 try renderToken(r, align_kw, .none); // align
1551 try renderToken(r, lparen_token, .none); // (
1552 try renderExpression(r, align_expr, .none); // alignment1545 try renderExpression(r, align_expr, .none); // alignment
1553 try renderToken(r, rparen_token, .space); // )1546 const rparen = tree.lastToken(align_expr) + 1;
1547 if (last_component == .@"align") {
1548 return renderTokenComma(r, rparen, space); // )
1549 }
1550 try renderToken(r, rparen, .space);
1554 }1551 }
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;
15571552
1558 try ais.pushIndent(.after_equals);1553 if (field.ast.value_expr.unwrap()) |value_expr| {
1559 try renderToken(r, eq_token, eq_space); // =1554 assert(last_component == .value);
1555 const eq_token = tree.firstToken(value_expr) - 1;
1556 const seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
1557 tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
1558 const eq_space: Space = if (seperate_line) .newline else .space;
15601559
1561 if (eq_space == .space) {1560 try ais.pushIndent(.after_equals);
1562 ais.popIndent();1561 try renderToken(r, eq_token, eq_space); // =
1563 try renderExpressionComma(r, value_expr, space); // value1562 if (eq_space == .space) {
1564 return;1563 ais.popIndent();
1565 }1564 return renderExpressionComma(r, value_expr, space); // value
15661565 }
1567 const maybe_comma = tree.lastToken(value_expr) + 1;
15681566
1569 if (tree.tokenTag(maybe_comma) == .comma) {1567 const maybe_comma = tree.lastToken(value_expr) + 1;
1570 try renderExpression(r, value_expr, .none); // value1568 if (tree.tokenTag(maybe_comma) == .comma) {
1571 ais.popIndent();1569 try renderExpression(r, value_expr, .none); // value
1572 try renderToken(r, maybe_comma, .newline);1570 ais.popIndent();
1573 } else {1571 try renderToken(r, maybe_comma, .newline);
1574 try renderExpression(r, value_expr, space); // value1572 } else {
1575 ais.popIndent();1573 try renderExpression(r, value_expr, space); // value
1576 }1574 ais.popIndent();
1575 }
1576 } else unreachable;
1577}1577}
15781578
1579fn renderBuiltinCall(1579fn renderBuiltinCall(
...@@ -1587,14 +1587,9 @@ fn renderBuiltinCall(...@@ -1587,14 +1587,9 @@ fn renderBuiltinCall(
15871587
1588 try renderToken(r, builtin_token, .none); // @name1588 try renderToken(r, builtin_token, .none); // @name
15891589
1590 if (params.len == 0) {
1591 try renderToken(r, builtin_token + 1, .none); // (
1592 return renderToken(r, builtin_token + 2, space); // )
1593 }
1594
1595 if (r.fixups.rebase_imported_paths) |prefix| {1590 if (r.fixups.rebase_imported_paths) |prefix| {
1596 const slice = tree.tokenSlice(builtin_token);1591 const slice = tree.tokenSlice(builtin_token);
1597 if (mem.eql(u8, slice, "@import")) f: {1592 if (params.len != 0 and mem.eql(u8, slice, "@import")) f: {
1598 const param = params[0];1593 const param = params[0];
1599 const str_lit_token = tree.nodeMainToken(param);1594 const str_lit_token = tree.nodeMainToken(param);
1600 assert(tree.tokenTag(str_lit_token) == .string_literal);1595 assert(tree.tokenTag(str_lit_token) == .string_literal);
...@@ -1613,45 +1608,7 @@ fn renderBuiltinCall(...@@ -1613,45 +1608,7 @@ fn renderBuiltinCall(
1613 }1608 }
1614 }1609 }
16151610
1616 const last_param = params[params.len - 1];1611 return renderParamList(r, builtin_token + 1, params, space);
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 }
1634
1635 if (i + 1 < params.len) {
1636 const comma_token = tree.lastToken(param_node) + 1;
1637 try renderToken(r, comma_token, .space); // ,
1638 }
1639 }
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();
1650 }
1651 ais.popIndent();
1652
1653 return renderToken(r, after_last_param_token + 1, space); // )
1654 }
1655}1612}
16561613
1657fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void {1614fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void {
...@@ -1743,7 +1700,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1743,7 +1700,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1743 },1700 },
1744 .r_paren => break,1701 .r_paren => break,
1745 .comma => {1702 .comma => {
1746 try renderToken(r, last_param_token, .space); // ,1703 try renderToken(r, last_param_token, .maybe_space); // ,
1747 continue;1704 continue;
1748 },1705 },
1749 else => {}, // Parameter type without a name.1706 else => {}, // Parameter type without a name.
...@@ -1753,7 +1710,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1753,7 +1710,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1753 {1710 {
1754 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name1711 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
1755 last_param_token = last_param_token + 1;1712 last_param_token = last_param_token + 1;
1756 try renderToken(r, last_param_token, .space); // :1713 try renderToken(r, last_param_token, .maybe_space); // :
1757 last_param_token += 1;1714 last_param_token += 1;
1758 }1715 }
1759 if (tree.tokenTag(last_param_token) == .keyword_anytype) {1716 if (tree.tokenTag(last_param_token) == .keyword_anytype) {
...@@ -1822,7 +1779,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1822,7 +1779,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1822 ais.popIndent();1779 ais.popIndent();
1823 }1780 }
18241781
1825 try renderToken(r, rparen, .space); // )1782 try renderToken(r, rparen, .maybe_space); // )
18261783
1827 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {1784 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
1828 const align_lparen = tree.firstToken(align_expr) - 1;1785 const align_lparen = tree.firstToken(align_expr) - 1;
...@@ -1831,7 +1788,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1831,7 +1788,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1831 try renderToken(r, align_lparen - 1, .none); // align1788 try renderToken(r, align_lparen - 1, .none); // align
1832 try renderToken(r, align_lparen, .none); // (1789 try renderToken(r, align_lparen, .none); // (
1833 try renderExpression(r, align_expr, .none);1790 try renderExpression(r, align_expr, .none);
1834 try renderToken(r, align_rparen, .space); // )1791 try renderToken(r, align_rparen, .maybe_space); // )
1835 }1792 }
18361793
1837 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {1794 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
...@@ -1841,7 +1798,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1841,7 +1798,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1841 try renderToken(r, align_lparen - 1, .none); // addrspace1798 try renderToken(r, align_lparen - 1, .none); // addrspace
1842 try renderToken(r, align_lparen, .none); // (1799 try renderToken(r, align_lparen, .none); // (
1843 try renderExpression(r, addrspace_expr, .none);1800 try renderExpression(r, addrspace_expr, .none);
1844 try renderToken(r, align_rparen, .space); // )1801 try renderToken(r, align_rparen, .maybe_space); // )
1845 }1802 }
18461803
1847 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {1804 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
...@@ -1851,7 +1808,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1851,7 +1808,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1851 try renderToken(r, section_lparen - 1, .none); // section1808 try renderToken(r, section_lparen - 1, .none); // section
1852 try renderToken(r, section_lparen, .none); // (1809 try renderToken(r, section_lparen, .none); // (
1853 try renderExpression(r, section_expr, .none);1810 try renderExpression(r, section_expr, .none);
1854 try renderToken(r, section_rparen, .space); // )1811 try renderToken(r, section_rparen, .maybe_space); // )
1855 }1812 }
18561813
1857 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {1814 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
...@@ -1865,7 +1822,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1865,7 +1822,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1865 try renderToken(r, callconv_lparen - 1, .none); // callconv1822 try renderToken(r, callconv_lparen - 1, .none); // callconv
1866 try renderToken(r, callconv_lparen, .none); // (1823 try renderToken(r, callconv_lparen, .none); // (
1867 try renderExpression(r, callconv_expr, .none);1824 try renderExpression(r, callconv_expr, .none);
1868 try renderToken(r, callconv_rparen, .space); // )1825 try renderToken(r, callconv_rparen, .maybe_space); // )
1869 }1826 }
1870 }1827 }
18711828
...@@ -1890,7 +1847,7 @@ fn renderSwitchCase(...@@ -1890,7 +1847,7 @@ fn renderSwitchCase(
18901847
1891 // render inline keyword1848 // render inline keyword
1892 if (switch_case.inline_token) |some| {1849 if (switch_case.inline_token) |some| {
1893 try renderToken(r, some, .space);1850 try renderToken(r, some, .maybe_space);
1894 }1851 }
18951852
1896 // Render everything before the arrow1853 // Render everything before the arrow
...@@ -1904,33 +1861,26 @@ fn renderSwitchCase(...@@ -1904,33 +1861,26 @@ fn renderSwitchCase(
1904 } else {1861 } else {
1905 // Render on one line1862 // Render on one line
1906 for (switch_case.ast.values) |value_expr| {1863 for (switch_case.ast.values) |value_expr| {
1907 try renderExpression(r, value_expr, .comma_space);1864 try renderExpression(r, value_expr, .comma_maybe_space);
1908 }1865 }
1909 }1866 }
19101867
1911 // Render the arrow and everything after it1868 try renderToken(r, switch_case.ast.arrow_token, .maybe_space); // =>
1912 const pre_target_space = if (tree.nodeTag(switch_case.ast.target_expr) == .multiline_string_literal)
1913 // Newline gets inserted when rendering the target expr.
1914 Space.none
1915 else
1916 Space.space;
1917 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
1918 try renderToken(r, switch_case.ast.arrow_token, after_arrow_space); // =>
19191869
1920 if (switch_case.payload_token) |payload_token| {1870 if (switch_case.payload_token) |payload_token| {
1921 try renderToken(r, payload_token - 1, .none); // pipe1871 try renderToken(r, payload_token - 1, .none); // pipe
1922 const ident = payload_token + @intFromBool(tree.tokenTag(payload_token) == .asterisk);1872 var ident = payload_token;
1923 if (tree.tokenTag(payload_token) == .asterisk) {1873 if (tree.tokenTag(ident) == .asterisk) {
1924 try renderToken(r, payload_token, .none); // asterisk1874 try renderToken(r, payload_token, .none); // asterisk
1875 ident += 1;
1925 }1876 }
1926 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier1877 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
1927 if (tree.tokenTag(ident + 1) == .comma) {1878 if (tree.tokenTag(ident + 1) == .comma) {
1928 try renderToken(r, ident + 1, .space); // ,1879 ident += 2;
1929 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // identifier1880 try renderToken(r, ident - 1, .space); // ,
1930 try renderToken(r, ident + 3, pre_target_space); // pipe1881 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
1931 } else {
1932 try renderToken(r, ident + 1, pre_target_space); // pipe
1933 }1882 }
1883 try renderToken(r, ident + 1, .maybe_space); // pipe
1934 }1884 }
19351885
1936 try renderExpression(r, switch_case.ast.target_expr, space);1886 try renderExpression(r, switch_case.ast.target_expr, space);
...@@ -2018,26 +1968,13 @@ fn renderStructInit(...@@ -2018,26 +1968,13 @@ fn renderStructInit(
2018 try ais.pushIndent(.normal);1968 try ais.pushIndent(.normal);
2019 try renderToken(r, struct_init.ast.lbrace, .newline);1969 try renderToken(r, struct_init.ast.lbrace, .newline);
20201970
2021 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .1971 for (0.., struct_init.ast.fields) |i, field_init| {
2022 try renderIdentifier(r, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
2023 // Don't output a space after the = if expression is a multiline string,
2024 // since then it will start on the next line.
2025 const field_node = struct_init.ast.fields[0];
2026 const expr = tree.nodeTag(field_node);
2027 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;
2028 try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =
2029
2030 try ais.pushSpace(.comma);
2031 try renderExpressionFixup(r, field_node, .comma);
2032 ais.popSpace();
2033
2034 for (struct_init.ast.fields[1..]) |field_init| {
2035 const init_token = tree.firstToken(field_init);1972 const init_token = tree.firstToken(field_init);
2036 try renderExtraNewlineToken(r, init_token - 3);1973 if (i != 0)
1974 try renderExtraNewlineToken(r, init_token - 3);
2037 try renderToken(r, init_token - 3, .none); // .1975 try renderToken(r, init_token - 3, .none); // .
2038 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name1976 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;1977 try renderToken(r, init_token - 1, .maybe_space); // =
2040 try renderToken(r, init_token - 1, space_after_equal); // =
20411978
2042 try ais.pushSpace(.comma);1979 try ais.pushSpace(.comma);
2043 try renderExpressionFixup(r, field_init, .comma);1980 try renderExpressionFixup(r, field_init, .comma);
...@@ -2053,8 +1990,7 @@ fn renderStructInit(...@@ -2053,8 +1990,7 @@ fn renderStructInit(
2053 const init_token = tree.firstToken(field_init);1990 const init_token = tree.firstToken(field_init);
2054 try renderToken(r, init_token - 3, .none); // .1991 try renderToken(r, init_token - 3, .none); // .
2055 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name1992 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;1993 try renderToken(r, init_token - 1, .maybe_space); // =
2057 try renderToken(r, init_token - 1, space_after_equal); // =
2058 try renderExpressionFixup(r, field_init, .comma_space);1994 try renderExpressionFixup(r, field_init, .comma_space);
2059 }1995 }
2060 }1996 }
...@@ -2436,7 +2372,7 @@ fn renderAsm(...@@ -2436,7 +2372,7 @@ fn renderAsm(
2436 const first_clobber = tree.firstToken(clobbers);2372 const first_clobber = tree.firstToken(clobbers);
2437 try renderToken(r, first_clobber - 3, .none);2373 try renderToken(r, first_clobber - 3, .none);
2438 try renderToken(r, first_clobber - 2, .none);2374 try renderToken(r, first_clobber - 2, .none);
2439 try renderToken(r, first_clobber - 1, .space);2375 try renderToken(r, first_clobber - 1, .maybe_space);
2440 try renderExpression(r, clobbers, .none);2376 try renderExpression(r, clobbers, .none);
2441 ais.popIndent();2377 ais.popIndent();
2442 return renderToken(r, asm_node.ast.rparen, space); // rparen2378 return renderToken(r, asm_node.ast.rparen, space); // rparen
...@@ -2450,6 +2386,9 @@ fn renderAsm(...@@ -2450,6 +2386,9 @@ fn renderAsm(
24502386
2451 try ais.forcePushIndent(.normal);2387 try ais.forcePushIndent(.normal);
2452 try renderExpression(r, asm_node.ast.template, .newline);2388 try renderExpression(r, asm_node.ast.template, .newline);
2389 ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2390 assert(ais.current_line_empty);
2391
2453 ais.setIndentDelta(asm_indent_delta);2392 ais.setIndentDelta(asm_indent_delta);
2454 const colon1 = tree.lastToken(asm_node.ast.template) + 1;2393 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
24552394
...@@ -2527,9 +2466,10 @@ fn renderAsm(...@@ -2527,9 +2466,10 @@ fn renderAsm(
2527 unreachable;2466 unreachable;
2528 };2467 };
25292468
2530 try renderToken(r, colon3, .space); // :2469 try renderToken(r, colon3, .maybe_space); // :
2531 const clobbers = asm_node.ast.clobbers.unwrap().?;2470 const clobbers = asm_node.ast.clobbers.unwrap().?;
2532 try renderExpression(r, clobbers, .none);2471 try renderExpression(r, clobbers, .none);
2472 ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2533 ais.setIndentDelta(indent_delta);2473 ais.setIndentDelta(indent_delta);
2534 ais.popIndent();2474 ais.popIndent();
2535 return renderToken(r, asm_node.ast.rparen, space); // rparen2475 return renderToken(r, asm_node.ast.rparen, space); // rparen
...@@ -2555,7 +2495,7 @@ fn renderParamList(...@@ -2555,7 +2495,7 @@ fn renderParamList(
25552495
2556 if (params.len == 0) {2496 if (params.len == 0) {
2557 try ais.pushIndent(.normal);2497 try ais.pushIndent(.normal);
2558 try renderToken(r, lparen, .none);2498 try renderToken(r, lparen, .none); // (
2559 ais.popIndent();2499 ais.popIndent();
2560 return renderToken(r, lparen + 1, space); // )2500 return renderToken(r, lparen + 1, space); // )
2561 }2501 }
...@@ -2590,10 +2530,7 @@ fn renderParamList(...@@ -2590,10 +2530,7 @@ fn renderParamList(
25902530
2591 if (i + 1 < params.len) {2531 if (i + 1 < params.len) {
2592 const comma = tree.lastToken(param_node) + 1;2532 const comma = tree.lastToken(param_node) + 1;
2593 const next_multiline_string =2533 try renderToken(r, comma, .maybe_space);
2594 tree.tokenTag(tree.firstToken(params[i + 1])) == .multiline_string_literal_line;
2595 const comma_space: Space = if (next_multiline_string) .none else .space;
2596 try renderToken(r, comma, comma_space);
2597 }2534 }
2598 }2535 }
2599 ais.popIndent();2536 ais.popIndent();
...@@ -2655,6 +2592,13 @@ const Space = enum {...@@ -2655,6 +2592,13 @@ const Space = enum {
2655 /// Additionally consume the next token if it is a semicolon.2592 /// Additionally consume the next token if it is a semicolon.
2656 /// In either case, a newline will be inserted afterwards.2593 /// In either case, a newline will be inserted afterwards.
2657 semicolon,2594 semicolon,
2595 /// If the next token is not a multiline string literal, this acts as .space,
2596 /// otherwise this acts as .none.
2597 maybe_space,
2598 /// Additionally consume the next token if it is a comma.
2599 /// In either case, a space will be inserted afterwards
2600 /// if the following token is not a multiline string literal.
2601 comma_maybe_space,
2658 /// Skip rendering whitespace and comments. If this is used, the caller2602 /// Skip rendering whitespace and comments. If this is used, the caller
2659 /// *must* handle whitespace and comments manually.2603 /// *must* handle whitespace and comments manually.
2660 skip,2604 skip,
...@@ -2719,6 +2663,16 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space...@@ -2719,6 +2663,16 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
2719 try ais.insertNewline();2663 try ais.insertNewline();
2720 },2664 },
27212665
2666 .maybe_space => if (!comment and next_token_tag != .multiline_string_literal_line) {
2667 try ais.writeByte(' ');
2668 },
2669
2670 .comma_maybe_space => if (next_token_tag == .comma) {
2671 try renderToken(r, token_index + 1, .maybe_space);
2672 } else if (!comment) {
2673 try ais.writeByte(' ');
2674 },
2675
2722 .skip => unreachable,2676 .skip => unreachable,
2723 }2677 }
2724}2678}
...@@ -2727,9 +2681,9 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {...@@ -2727,9 +2681,9 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {
2727 const ais = r.ais;2681 const ais = r.ais;
2728 switch (space) {2682 switch (space) {
2729 .none => {},2683 .none => {},
2730 .space => try ais.writeByte(' '),2684 .space, .maybe_space => try ais.writeByte(' '),
2731 .newline => try ais.insertNewline(),2685 .newline => try ais.insertNewline(),
2732 .comma => try ais.writeAll(",\n"),2686 .comma, .comma_maybe_space => try ais.writeAll(",\n"),
2733 .comma_space => try ais.writeAll(", "),2687 .comma_space => try ais.writeAll(", "),
2734 .semicolon => try ais.writeAll(";\n"),2688 .semicolon => try ais.writeAll(";\n"),
2735 .skip => unreachable,2689 .skip => unreachable,
...@@ -3477,11 +3431,19 @@ const AutoIndentingStream = struct {...@@ -3477,11 +3431,19 @@ const AutoIndentingStream = struct {
34773431
3478 pub fn popIndent(ais: *AutoIndentingStream) void {3432 pub fn popIndent(ais: *AutoIndentingStream) void {
3479 if (ais.indent_stack.pop().?.realized) {3433 if (ais.indent_stack.pop().?.realized) {
3480 assert(ais.indent_count > 0);
3481 ais.indent_count -= 1;3434 ais.indent_count -= 1;
3482 }3435 }
3483 }3436 }
34843437
3438 /// Forces the last pushed indent to be realized
3439 pub fn forceLastIndent(ais: *AutoIndentingStream) void {
3440 const top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
3441 if (!top.realized) {
3442 top.realized = true;
3443 ais.indent_count += 1;
3444 }
3445 }
3446
3485 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {3447 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {
3486 return ais.indent_stack.items.len == 0;3448 return ais.indent_stack.items.len == 0;
3487 }3449 }
lib/std/zig/parser_test.zig+313
...@@ -2543,6 +2543,19 @@ test "zig fmt: first line comment in struct initializer" {...@@ -2543,6 +2543,19 @@ test "zig fmt: first line comment in struct initializer" {
2543 );2543 );
2544}2544}
25452545
2546test "zig fmt: multiline string literals in struct initializer" {
2547 try testTransform(
2548 \\const a = .{ .a = \\
2549 \\+ 1};
2550 \\
2551 ,
2552 \\const a = .{ .a =
2553 \\ \\
2554 \\+ 1 };
2555 \\
2556 );
2557}
2558
2546test "zig fmt: doc comments before struct field" {2559test "zig fmt: doc comments before struct field" {
2547 try testCanonical(2560 try testCanonical(
2548 \\pub const Allocator = struct {2561 \\pub const Allocator = struct {
...@@ -4005,6 +4018,18 @@ test "zig fmt: multiline string in array" {...@@ -4005,6 +4018,18 @@ test "zig fmt: multiline string in array" {
4005 \\}4018 \\}
4006 \\4019 \\
4007 );4020 );
4021
4022 try testTransform(
4023 \\const a = .{ k, \\
4024 \\};
4025 \\
4026 ,
4027 \\const a = .{
4028 \\ k,
4029 \\ \\
4030 \\};
4031 \\
4032 );
4008}4033}
40094034
4010test "zig fmt: if type expr" {4035test "zig fmt: if type expr" {
...@@ -6029,6 +6054,294 @@ test "zig fmt: extern addrspace in struct" {...@@ -6029,6 +6054,294 @@ test "zig fmt: extern addrspace in struct" {
6029 );6054 );
6030}6055}
60316056
6057test "zig fmt: whitespace with multiline strings" {
6058 try testCanonical(
6059 \\const a = .{
6060 \\ .b =
6061 \\ \\
6062 \\ ++ "",
6063 \\};
6064 \\const b = switch (a) {
6065 \\ a =>
6066 \\ \\
6067 \\ ++ "",
6068 \\};
6069 \\
6070 );
6071
6072 try testTransform(
6073 \\test {
6074 \\ a = \\
6075 \\ ;
6076 \\ b = \\
6077 \\ ();
6078 \\ c = x ++ \\
6079 \\ ;
6080 \\ d = x catch \\
6081 \\ ;
6082 \\ comptime \\
6083 \\ , \\
6084 \\ , \\
6085 \\ = \\
6086 \\ ;
6087 \\ e = if (x) \\
6088 \\ else y;
6089 \\ f = if (x) y else
6090 \\ \\
6091 \\ ;
6092 \\ comptime \\
6093 \\ ;
6094 \\ errdefer \\
6095 \\ ;
6096 \\ try \\
6097 \\ ;
6098 \\ return \\
6099 \\ ;
6100 \\ const a = asm (\\
6101 \\ ++ "": [a] "" (-> \\
6102 \\ ) :: \\
6103 \\ );
6104 \\ const a2 = asm ("" ::: \\
6105 \\ );
6106 \\ const b = x[1 + 1 .. \\
6107 \\ ];
6108 \\}
6109 \\/// tuple type
6110 \\comptime \\
6111 \\,
6112 \\a: \\
6113 \\align(\\
6114 \\)
6115 \\= \\
6116 \\,
6117 \\const A = .{
6118 \\ *volatile \\
6119 \\ ,
6120 \\ *const \\
6121 \\ ,
6122 \\ *addrspace( \\
6123 \\ ) \\
6124 \\ ,
6125 \\ *align( \\
6126 \\ : \\
6127 \\ : \\
6128 \\ ) \\
6129 \\ ,
6130 \\ *allowzero \\
6131 \\ ,
6132 \\ *\\
6133 \\ ,
6134 \\ **\\
6135 \\ ,
6136 \\ [*]\\
6137 \\ ,
6138 \\ [*: \\
6139 \\ ]\\
6140 \\ ,
6141 \\ [*c]\\
6142 \\ ,
6143 \\ []\\
6144 \\ ,
6145 \\ [: \\
6146 \\ ]\\
6147 \\ ,
6148 \\ *addrspace(a) align(a) \\
6149 \\ ,
6150 \\};
6151 \\const a = blk: {
6152 \\ break \\
6153 \\ ;
6154 \\ break :blk \\
6155 \\ ;
6156 \\ continue \\
6157 \\ ;
6158 \\ continue :blk \\
6159 \\ ;
6160 \\};
6161 \\const b = a(a, \\
6162 \\++ "");
6163 \\const c = @a(a, \\
6164 \\++ "");
6165 \\extern fn a(T,\\
6166 \\) \\
6167 \\;
6168 \\extern fn b(a: \\
6169 \\) align(a) callconv(a) \\
6170 \\;
6171 \\const d = switch (a) { \\
6172 \\ , 1,
6173 \\ \\
6174 \\ => {},
6175 \\ inline \\
6176 \\ => {},
6177 \\};
6178 \\
6179 ,
6180 \\test {
6181 \\ a =
6182 \\ \\
6183 \\ ;
6184 \\ b =
6185 \\ \\
6186 \\ ();
6187 \\ c = x ++
6188 \\ \\
6189 \\ ;
6190 \\ d = x catch
6191 \\ \\
6192 \\ ;
6193 \\ comptime
6194 \\ \\
6195 \\ ,
6196 \\ \\
6197 \\ ,
6198 \\ \\
6199 \\ =
6200 \\ \\
6201 \\ ;
6202 \\ e = if (x)
6203 \\ \\
6204 \\ else
6205 \\ y;
6206 \\ f = if (x) y else
6207 \\ \\
6208 \\ ;
6209 \\ comptime
6210 \\ \\
6211 \\ ;
6212 \\ errdefer
6213 \\ \\
6214 \\ ;
6215 \\ try
6216 \\ \\
6217 \\ ;
6218 \\ return
6219 \\ \\
6220 \\ ;
6221 \\ const a = asm (
6222 \\ \\
6223 \\ ++ ""
6224 \\ : [a] "" (->
6225 \\ \\
6226 \\ ),
6227 \\ :
6228 \\ :
6229 \\ \\
6230 \\ );
6231 \\ const a2 = asm ("" :::
6232 \\ \\
6233 \\ );
6234 \\ const b = x[1 + 1 ..
6235 \\ \\
6236 \\ ];
6237 \\}
6238 \\/// tuple type
6239 \\comptime
6240 \\\\
6241 \\,
6242 \\a:
6243 \\\\
6244 \\align(
6245 \\\\
6246 \\) =
6247 \\ \\
6248 \\,
6249 \\const A = .{
6250 \\ *volatile
6251 \\ \\
6252 \\ ,
6253 \\ *const
6254 \\ \\
6255 \\ ,
6256 \\ *addrspace(
6257 \\ \\
6258 \\ )
6259 \\ \\
6260 \\ ,
6261 \\ *align(
6262 \\ \\
6263 \\ :
6264 \\ \\
6265 \\ :
6266 \\ \\
6267 \\ )
6268 \\ \\
6269 \\ ,
6270 \\ *allowzero
6271 \\ \\
6272 \\ ,
6273 \\ *
6274 \\ \\
6275 \\ ,
6276 \\ **
6277 \\ \\
6278 \\ ,
6279 \\ [*]
6280 \\ \\
6281 \\ ,
6282 \\ [*:
6283 \\ \\
6284 \\ ]
6285 \\ \\
6286 \\ ,
6287 \\ [*c]
6288 \\ \\
6289 \\ ,
6290 \\ []
6291 \\ \\
6292 \\ ,
6293 \\ [:
6294 \\ \\
6295 \\ ]
6296 \\ \\
6297 \\ ,
6298 \\ *align(a) addrspace(a)
6299 \\ \\
6300 \\ ,
6301 \\};
6302 \\const a = blk: {
6303 \\ break
6304 \\ \\
6305 \\ ;
6306 \\ break :blk
6307 \\ \\
6308 \\ ;
6309 \\ continue
6310 \\ \\
6311 \\ ;
6312 \\ continue :blk
6313 \\ \\
6314 \\ ;
6315 \\};
6316 \\const b = a(a,
6317 \\ \\
6318 \\++ "");
6319 \\const c = @a(a,
6320 \\ \\
6321 \\++ "");
6322 \\extern fn a(T,
6323 \\\\
6324 \\)
6325 \\\\
6326 \\;
6327 \\extern fn b(a:
6328 \\\\
6329 \\) align(a) callconv(a)
6330 \\\\
6331 \\;
6332 \\const d = switch (a) {
6333 \\ \\
6334 \\ , 1,
6335 \\ \\
6336 \\ => {},
6337 \\ inline
6338 \\ \\
6339 \\ => {},
6340 \\};
6341 \\
6342 );
6343}
6344
6032test "recovery: top level" {6345test "recovery: top level" {
6033 try testError(6346 try testError(
6034 \\test "" {inline}6347 \\test "" {inline}