authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2025-07-28 16:41:48-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-12 17:23:21-04:00
loge03851e522738b6ed2259e742e5f2e2f4676ef75
treec99090beb135d6bc5a45fe379abfaac0d60369c3
parentb0db0ef4e197eb44faa0e343874dfc8f810324ca

zig fmt: fix array size indenting when expr becomes multiline

Adds a new function becomesMultilineExpr to determine this and extracts the logic from several functions.

2 files changed, 692 insertions(+), 63 deletions(-)

lib/std/zig/Ast/Render.zig+450-63
......@@ -645,7 +645,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
645645 const lhs, const rhs = tree.nodeData(node).node_and_node;
646646 const lbracket = tree.firstToken(rhs) - 1;
647647 const rbracket = tree.lastToken(rhs) + 1;
648 const one_line = tree.tokensOnSameLine(lbracket, rbracket);
648 const one_line = tree.tokensOnSameLine(lbracket, rbracket) and
649 !becomesMultilineExpr(tree, rhs);
649650 const inner_space = if (one_line) Space.none else Space.newline;
650651 try renderExpression(r, lhs, .none);
651652 try ais.pushIndent(.normal);
......@@ -925,6 +926,382 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v
925926 }
926927}
927928
929/// Same as becomesMultilineExpr, but returns false when `node == .none`
930fn optBecomesMultilineExpr(tree: Ast, node: Ast.Node.OptionalIndex) bool {
931 return if (node.unwrap()) |payload| becomesMultilineExpr(tree, payload) else false;
932}
933
934/// May return false if `node` is already multiline
935fn becomesMultilineExpr(tree: Ast, node: Ast.Node.Index) bool {
936 // Conditions related to comments, doc comments, and multiline string literals are ignored
937 // since they always go to the end of the line, which already make them a multi-line
938 // expression (since they contain a newline).
939 switch (tree.nodeTag(node)) {
940 .identifier,
941 .number_literal,
942 .char_literal,
943 .unreachable_literal,
944 .anyframe_literal,
945 .string_literal,
946 .multiline_string_literal,
947 .error_value,
948 .enum_literal,
949 => return false,
950 .container_decl_trailing,
951 .container_decl_arg_trailing,
952 .container_decl_two_trailing,
953 .tagged_union_trailing,
954 .tagged_union_enum_tag_trailing,
955 .tagged_union_two_trailing,
956 .switch_comma,
957 .builtin_call_two_comma,
958 .builtin_call_comma,
959 .call_one_comma,
960 .call_comma,
961 .struct_init_one_comma,
962 .struct_init_dot_two_comma,
963 .struct_init_dot_comma,
964 .struct_init_comma,
965 .array_init_one_comma,
966 .array_init_dot_two_comma,
967 .array_init_dot_comma,
968 .array_init_comma,
969 // The following always have a non-zero amount of members
970 // which is also the condition for them to be multi-line.
971 .block,
972 .block_semicolon,
973 => return true,
974 .block_two,
975 .block_two_semicolon,
976 => return tree.nodeData(node).opt_node_and_opt_node[0] != .none,
977 .container_decl,
978 .container_decl_arg,
979 .container_decl_two,
980 .tagged_union,
981 .tagged_union_enum_tag,
982 .tagged_union_two,
983 => {
984 var buf: [2]Ast.Node.Index = undefined;
985 const full = tree.fullContainerDecl(&buf, node).?;
986 if (full.ast.arg.unwrap()) |arg| {
987 if (becomesMultilineExpr(tree, arg))
988 return true;
989 }
990 // This does the same checks as `isOneLineContainerDecl`, however it avoids unnecessary
991 // checks related to comments and multiline strings, which would mean the container is
992 // already multiple lines.
993 for (full.ast.members) |member| {
994 if (tree.fullContainerField(member)) |field_full| {
995 for ([_]Ast.Node.OptionalIndex{
996 field_full.ast.type_expr,
997 field_full.ast.align_expr,
998 field_full.ast.value_expr,
999 }) |opt_expr| {
1000 if (opt_expr.unwrap()) |expr| {
1001 if (becomesMultilineExpr(tree, expr))
1002 return true;
1003 }
1004 }
1005 } else return true;
1006 }
1007 return false;
1008 },
1009 .error_set_decl => {
1010 const lbrace, const rbrace = tree.nodeData(node).token_and_token;
1011 return !isOneLineErrorSetDecl(tree, lbrace, rbrace);
1012 },
1013 .@"switch" => {
1014 const op, const extra_index = tree.nodeData(node).node_and_extra;
1015 const case_range = tree.extraData(extra_index, Ast.Node.SubRange);
1016 return @intFromEnum(case_range.end) - @intFromEnum(case_range.start) != 0 or
1017 becomesMultilineExpr(tree, op);
1018 },
1019 .for_simple, .@"for" => {
1020 const full = tree.fullFor(node).?;
1021 if (becomesMultilineExpr(tree, full.ast.then_expr) or
1022 optBecomesMultilineExpr(tree, full.ast.else_expr))
1023 return true;
1024
1025 for (full.ast.inputs) |expr| {
1026 if (if (tree.nodeTag(expr) == .for_range) blk: {
1027 const lhs, const rhs = tree.nodeData(expr).node_and_opt_node;
1028 break :blk becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, rhs);
1029 } else becomesMultilineExpr(tree, expr))
1030 return true;
1031 }
1032 const final_input_expr = full.ast.inputs[full.ast.inputs.len - 1];
1033 if (tree.tokenTag(tree.lastToken(final_input_expr) + 1) == .comma)
1034 return true;
1035
1036 const token_tags = tree.tokens.items(.tag);
1037 const payload = full.payload_token;
1038 const pipe = std.mem.indexOfScalarPos(Token.Tag, token_tags, payload, .pipe).?;
1039 return token_tags[@intCast(pipe - 1)] == .comma;
1040 },
1041 .while_simple,
1042 .while_cont,
1043 .@"while",
1044 => {
1045 const full = tree.fullWhile(node).?;
1046 return becomesMultilineExpr(tree, full.ast.cond_expr) or
1047 becomesMultilineExpr(tree, full.ast.then_expr) or
1048 optBecomesMultilineExpr(tree, full.ast.cont_expr) or
1049 optBecomesMultilineExpr(tree, full.ast.else_expr);
1050 },
1051 .if_simple,
1052 .@"if",
1053 => {
1054 const full = tree.fullIf(node).?;
1055 return becomesMultilineExpr(tree, full.ast.cond_expr) or
1056 becomesMultilineExpr(tree, full.ast.then_expr) or
1057 optBecomesMultilineExpr(tree, full.ast.else_expr);
1058 },
1059 .fn_proto_simple,
1060 .fn_proto_multi,
1061 .fn_proto_one,
1062 .fn_proto,
1063 => {
1064 var buf: [1]Ast.Node.Index = undefined;
1065 const fn_proto = tree.fullFnProto(&buf, node).?;
1066
1067 for ([_]Ast.Node.OptionalIndex{
1068 fn_proto.ast.return_type,
1069 fn_proto.ast.align_expr,
1070 fn_proto.ast.addrspace_expr,
1071 fn_proto.ast.section_expr,
1072 fn_proto.ast.callconv_expr,
1073 }) |opt_expr| {
1074 if (opt_expr.unwrap()) |expr| {
1075 if (becomesMultilineExpr(tree, expr))
1076 return true;
1077 }
1078 }
1079 for (fn_proto.ast.params) |expr| {
1080 if (becomesMultilineExpr(tree, expr))
1081 return true;
1082 }
1083
1084 const lparen = fn_proto.ast.fn_token + 1;
1085 const return_type = fn_proto.ast.return_type.unwrap().?;
1086 const maybe_bang = tree.firstToken(return_type) - 1;
1087 const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
1088 return !isOneLineFnProto(tree, fn_proto, lparen, rparen);
1089 },
1090 .asm_simple,
1091 => {
1092 const lhs = tree.nodeData(node).node_and_token[0];
1093 return becomesMultilineExpr(tree, lhs);
1094 },
1095 .@"asm",
1096 => {
1097 const lhs, const extra_index = tree.nodeData(node).node_and_extra;
1098 const asm_extra = tree.extraData(extra_index, Ast.Node.Asm);
1099 return @intFromEnum(asm_extra.items_end) - @intFromEnum(asm_extra.items_start) != 0 or
1100 becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, asm_extra.clobbers);
1101 },
1102 .array_type, .array_type_sentinel => {
1103 const array_type = tree.fullArrayType(node).?;
1104 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
1105 return !isOneLineArrayType(tree, array_type, rbracket) or
1106 becomesMultilineExpr(tree, array_type.ast.elem_type);
1107 },
1108 .array_access => {
1109 const lhs, const rhs = tree.nodeData(node).node_and_node;
1110 const lbracket = tree.firstToken(rhs) - 1;
1111 const rbracket = tree.lastToken(rhs) + 1;
1112 return !tree.tokensOnSameLine(lbracket, rbracket) or
1113 becomesMultilineExpr(tree, lhs) or
1114 becomesMultilineExpr(tree, rhs);
1115 },
1116 .call_one,
1117 .call,
1118 .builtin_call_two,
1119 .builtin_call,
1120 .array_init_one,
1121 .array_init_dot_two,
1122 .array_init_dot,
1123 .array_init,
1124 .struct_init_one,
1125 .struct_init_dot_two,
1126 .struct_init_dot,
1127 .struct_init,
1128 => |tag| {
1129 var buf: [2]Ast.Node.Index = undefined;
1130 const opt_lhs: Ast.Node.OptionalIndex, const items = switch (tag) {
1131 .call_one, .call => blk: {
1132 const full = tree.fullCall(buf[0..1], node).?;
1133 break :blk .{ full.ast.fn_expr.toOptional(), full.ast.params };
1134 },
1135 .builtin_call_two, .builtin_call => .{ .none, tree.builtinCallParams(&buf, node).? },
1136 .array_init_one,
1137 .array_init_dot_two,
1138 .array_init_dot,
1139 .array_init,
1140 => blk: {
1141 const full = tree.fullArrayInit(&buf, node).?;
1142 break :blk .{ full.ast.type_expr, full.ast.elements };
1143 },
1144 .struct_init_one,
1145 .struct_init_dot_two,
1146 .struct_init_dot,
1147 .struct_init,
1148 => blk: {
1149 const full = tree.fullStructInit(&buf, node).?;
1150 break :blk .{ full.ast.type_expr, full.ast.fields };
1151 },
1152 else => unreachable,
1153 };
1154 if (opt_lhs.unwrap()) |lhs| {
1155 if (becomesMultilineExpr(tree, lhs))
1156 return true;
1157 }
1158 for (items) |expr| {
1159 if (becomesMultilineExpr(tree, expr))
1160 return true;
1161 }
1162 return false;
1163 },
1164 .assign_destructure => {
1165 const full = tree.assignDestructure(node);
1166 for (full.ast.variables) |expr| {
1167 if (becomesMultilineExpr(tree, expr))
1168 return true;
1169 }
1170 return becomesMultilineExpr(tree, full.ast.value_expr);
1171 },
1172 .ptr_type_aligned,
1173 .ptr_type_sentinel,
1174 .ptr_type,
1175 .ptr_type_bit_range,
1176 => {
1177 const full = tree.fullPtrType(node).?;
1178 return becomesMultilineExpr(tree, full.ast.child_type) or
1179 optBecomesMultilineExpr(tree, full.ast.sentinel) or
1180 optBecomesMultilineExpr(tree, full.ast.align_node) or
1181 optBecomesMultilineExpr(tree, full.ast.addrspace_node) or
1182 optBecomesMultilineExpr(tree, full.ast.bit_range_start) or
1183 optBecomesMultilineExpr(tree, full.ast.bit_range_end);
1184 },
1185 .slice_open,
1186 .slice,
1187 .slice_sentinel,
1188 => {
1189 const full = tree.fullSlice(node).?;
1190 return becomesMultilineExpr(tree, full.ast.sliced) or
1191 becomesMultilineExpr(tree, full.ast.start) or
1192 optBecomesMultilineExpr(tree, full.ast.end) or
1193 optBecomesMultilineExpr(tree, full.ast.sentinel);
1194 },
1195 .@"comptime",
1196 .@"nosuspend",
1197 .@"suspend",
1198 .@"resume",
1199 .bit_not,
1200 .bool_not,
1201 .negation,
1202 .negation_wrap,
1203 .optional_type,
1204 .address_of,
1205 .deref,
1206 .@"try",
1207 => return becomesMultilineExpr(tree, tree.nodeData(node).node),
1208 .@"return" => return optBecomesMultilineExpr(tree, tree.nodeData(node).opt_node),
1209 .field_access,
1210 .unwrap_optional,
1211 .grouped_expression,
1212 => return becomesMultilineExpr(tree, tree.nodeData(node).node_and_token[0]),
1213 .add,
1214 .add_wrap,
1215 .add_sat,
1216 .array_cat,
1217 .array_mult,
1218 .bang_equal,
1219 .bit_and,
1220 .bit_or,
1221 .shl,
1222 .shl_sat,
1223 .shr,
1224 .bit_xor,
1225 .bool_and,
1226 .bool_or,
1227 .div,
1228 .equal_equal,
1229 .greater_or_equal,
1230 .greater_than,
1231 .less_or_equal,
1232 .less_than,
1233 .merge_error_sets,
1234 .mod,
1235 .mul,
1236 .mul_wrap,
1237 .mul_sat,
1238 .sub,
1239 .sub_wrap,
1240 .sub_sat,
1241 .@"orelse",
1242 .@"catch",
1243 .error_union,
1244 .assign,
1245 .assign_bit_and,
1246 .assign_bit_or,
1247 .assign_shl,
1248 .assign_shl_sat,
1249 .assign_shr,
1250 .assign_bit_xor,
1251 .assign_div,
1252 .assign_sub,
1253 .assign_sub_wrap,
1254 .assign_sub_sat,
1255 .assign_mod,
1256 .assign_add,
1257 .assign_add_wrap,
1258 .assign_add_sat,
1259 .assign_mul,
1260 .assign_mul_wrap,
1261 .assign_mul_sat,
1262 => {
1263 const lhs, const rhs = tree.nodeData(node).node_and_node;
1264 return becomesMultilineExpr(tree, lhs) or becomesMultilineExpr(tree, rhs);
1265 },
1266 .@"break", .@"continue" => {
1267 const opt_expr = tree.nodeData(node).opt_token_and_opt_node[1];
1268 return optBecomesMultilineExpr(tree, opt_expr);
1269 },
1270 .anyframe_type => return becomesMultilineExpr(tree, tree.nodeData(node).token_and_node[1]),
1271 .@"errdefer",
1272 .@"defer",
1273 .for_range,
1274 .switch_range,
1275 .switch_case_one,
1276 .switch_case_inline_one,
1277 .switch_case,
1278 .switch_case_inline,
1279 .asm_output,
1280 .asm_input,
1281 .fn_decl,
1282 .container_field,
1283 .container_field_init,
1284 .container_field_align,
1285 .root,
1286 .global_var_decl,
1287 .local_var_decl,
1288 .simple_var_decl,
1289 .aligned_var_decl,
1290 .test_decl,
1291 => unreachable,
1292 }
1293}
1294
1295fn isOneLineArrayType(
1296 tree: Ast,
1297 array_type: Ast.full.ArrayType,
1298 rbracket: Ast.TokenIndex,
1299) bool {
1300 return tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and
1301 !becomesMultilineExpr(tree, array_type.ast.elem_count) and
1302 !optBecomesMultilineExpr(tree, array_type.ast.sentinel);
1303}
1304
9281305fn renderArrayType(
9291306 r: *Render,
9301307 array_type: Ast.full.ArrayType,
......@@ -933,7 +1310,7 @@ fn renderArrayType(
9331310 const tree = r.tree;
9341311 const ais = r.ais;
9351312 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
936 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);
1313 const one_line = isOneLineArrayType(tree, array_type, rbracket);
9371314 const inner_space = if (one_line) Space.none else Space.newline;
9381315 try ais.pushIndent(.normal);
9391316 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
......@@ -1614,6 +1991,47 @@ fn renderBuiltinCall(
16141991 return renderParamList(r, builtin_token + 1, params, space);
16151992}
16161993
1994fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex {
1995 // These may appear in any order, so we have to check the token_starts array
1996 // to find out which is first.
1997 var rparen = if (tree.tokenTag(maybe_bang) == .bang) maybe_bang - 1 else maybe_bang;
1998 var smallest_start = tree.tokenStart(maybe_bang);
1999 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
2000 const tok = tree.firstToken(align_expr) - 3;
2001 const start = tree.tokenStart(tok);
2002 if (start < smallest_start) {
2003 rparen = tok;
2004 smallest_start = start;
2005 }
2006 }
2007 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
2008 const tok = tree.firstToken(addrspace_expr) - 3;
2009 const start = tree.tokenStart(tok);
2010 if (start < smallest_start) {
2011 rparen = tok;
2012 smallest_start = start;
2013 }
2014 }
2015 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
2016 const tok = tree.firstToken(section_expr) - 3;
2017 const start = tree.tokenStart(tok);
2018 if (start < smallest_start) {
2019 rparen = tok;
2020 smallest_start = start;
2021 }
2022 }
2023 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
2024 const tok = tree.firstToken(callconv_expr) - 3;
2025 const start = tree.tokenStart(tok);
2026 if (start < smallest_start) {
2027 rparen = tok;
2028 smallest_start = start;
2029 }
2030 }
2031 assert(tree.tokenTag(rparen) == .r_paren);
2032 return rparen;
2033}
2034
16172035fn isOneLineFnProto(
16182036 tree: Ast,
16192037 fn_proto: Ast.full.FnProto,
......@@ -1652,46 +2070,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
16522070
16532071 const return_type = fn_proto.ast.return_type.unwrap().?;
16542072 const maybe_bang = tree.firstToken(return_type) - 1;
1655 const rparen = blk: {
1656 // These may appear in any order, so we have to check the token_starts array
1657 // to find out which is first.
1658 var rparen = if (tree.tokenTag(maybe_bang) == .bang) maybe_bang - 1 else maybe_bang;
1659 var smallest_start = tree.tokenStart(maybe_bang);
1660 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
1661 const tok = tree.firstToken(align_expr) - 3;
1662 const start = tree.tokenStart(tok);
1663 if (start < smallest_start) {
1664 rparen = tok;
1665 smallest_start = start;
1666 }
1667 }
1668 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
1669 const tok = tree.firstToken(addrspace_expr) - 3;
1670 const start = tree.tokenStart(tok);
1671 if (start < smallest_start) {
1672 rparen = tok;
1673 smallest_start = start;
1674 }
1675 }
1676 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
1677 const tok = tree.firstToken(section_expr) - 3;
1678 const start = tree.tokenStart(tok);
1679 if (start < smallest_start) {
1680 rparen = tok;
1681 smallest_start = start;
1682 }
1683 }
1684 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
1685 const tok = tree.firstToken(callconv_expr) - 3;
1686 const start = tree.tokenStart(tok);
1687 if (start < smallest_start) {
1688 rparen = tok;
1689 smallest_start = start;
1690 }
1691 }
1692 break :blk rparen;
1693 };
1694 assert(tree.tokenTag(rparen) == .r_paren);
2073 const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
16952074
16962075 // The params list is a sparse set that does *not* include anytype or ... parameters.
16972076
......@@ -2258,6 +2637,34 @@ fn isOneLineErrorSetDecl(
22582637 !hasComment(tree, lbrace, rbrace);
22592638}
22602639
2640fn isOneLineContainerDecl(
2641 tree: Ast,
2642 container_decl: Ast.full.ContainerDecl,
2643 lbrace: Ast.TokenIndex,
2644 rbrace: Ast.TokenIndex,
2645) bool {
2646 // We print all the members in one-line unless one of the following conditions are true:
2647
2648 // 1. The container has comments or multiline strings.
2649 if (hasComment(tree, lbrace, rbrace) or hasMultilineString(tree, lbrace, rbrace)) {
2650 return false;
2651 }
2652
2653 // 2. The container has a container comment.
2654 if (tree.tokenTag(lbrace + 1) == .container_doc_comment) return false;
2655
2656 // 3. A member of the container has a doc comment.
2657 if (hasDocComment(tree, lbrace + 1, rbrace))
2658 return false;
2659
2660 // 4. The container has non-field members.
2661 for (container_decl.ast.members) |member| {
2662 if (tree.fullContainerField(member) == null) return false;
2663 }
2664
2665 return true;
2666}
2667
22612668fn renderContainerDecl(
22622669 r: *Render,
22632670 container_decl_node: Ast.Node.Index,
......@@ -2322,27 +2729,7 @@ fn renderContainerDecl(
23222729 }
23232730
23242731 const src_has_trailing_comma = tree.tokenTag(rbrace - 1) == .comma;
2325 if (!src_has_trailing_comma) one_line: {
2326 // We print all the members in-line unless one of the following conditions are true:
2327
2328 // 1. The container has comments or multiline strings.
2329 if (hasComment(tree, lbrace, rbrace) or hasMultilineString(tree, lbrace, rbrace)) {
2330 break :one_line;
2331 }
2332
2333 // 2. The container has a container comment.
2334 if (tree.tokenTag(lbrace + 1) == .container_doc_comment) break :one_line;
2335
2336 // 3. A member of the container has a doc comment.
2337 for (tree.tokens.items(.tag)[lbrace + 1 .. rbrace - 1]) |tag| {
2338 if (tag == .doc_comment) break :one_line;
2339 }
2340
2341 // 4. The container has non-field members.
2342 for (container_decl.ast.members) |member| {
2343 if (tree.fullContainerField(member) == null) break :one_line;
2344 }
2345
2732 if (!src_has_trailing_comma and isOneLineContainerDecl(tree, container_decl, lbrace, rbrace)) {
23462733 // Print all the declarations on the same line.
23472734 try renderToken(r, lbrace, .space); // lbrace
23482735 for (container_decl.ast.members) |member| {
lib/std/zig/parser_test.zig+242
......@@ -6094,6 +6094,248 @@ test "zig fmt: field accesses on number literals" {
60946094 );
60956095}
60966096
6097test "zig fmt: array indent when inner becomes multi-line" {
6098 try testTransform(
6099 \\const access_block = x[{{}}];
6100 \\
6101 \\const block = [{{}}]T;
6102 \\const sentinel_block = [15:{{}}]T;
6103 \\const container = [enum { A, }]T;
6104 \\const container_arg = [union({{}}) {}]T;
6105 \\const error_set = [error{ A, }]T;
6106 \\const @"switch" = [switch (m) { 0 => {}, }]T;
6107 \\const switch_op = [switch ({{}}) {}]T;
6108 \\const for_capture = [for (a) |_,| {}]T;
6109 \\const for_expr = [for ({{}}) |_| {}]T;
6110 \\const for_range = [for ({{}}..15) |_| {}]T;
6111 \\const for_input_comma = [for (0..15,) |_| {}]T;
6112 \\const call_param = [a({{}})]T;
6113 \\const call_fn = [({{}})()]T;
6114 \\const builtin_call = [@log2({{}})]T;
6115 \\const array_init = [.{{{}}}]T;
6116 \\const struct_init = [.{.x = {{}}}]T;
6117 \\const @"asm" = [asm ("" : [x] "" (-> T))]T;
6118 \\const asm_template = [asm ({{}})]T;
6119 \\const asm_clobbers = [asm ("" ::: {{}})]T;
6120 \\const @"fn" = [fn (({{}})) void]T;
6121 \\const array_type_len = [[{{}}]T]T;
6122 \\const array_type_type = [[1]({{}})]T;
6123 \\const array_access_array = [({{}})[1]]T;
6124 \\const array_access_index = [x[{{}}]]T;
6125 \\const binop = [1 + {{}}]T;
6126 \\const unaryop = [!{{}}]T;
6127 \\const destructure = [while (true) : (x, {{}} = z) {}]T;
6128 \\
6129 ,
6130 \\const access_block = x[
6131 \\ {
6132 \\ {}
6133 \\ }
6134 \\];
6135 \\
6136 \\const block = [
6137 \\ {
6138 \\ {}
6139 \\ }
6140 \\]T;
6141 \\const sentinel_block = [
6142 \\ 15
6143 \\ :
6144 \\ {
6145 \\ {}
6146 \\ }
6147 \\]T;
6148 \\const container = [
6149 \\ enum {
6150 \\ A,
6151 \\ }
6152 \\]T;
6153 \\const container_arg = [
6154 \\ union({
6155 \\ {}
6156 \\ }) {}
6157 \\]T;
6158 \\const error_set = [
6159 \\ error{
6160 \\ A,
6161 \\ }
6162 \\]T;
6163 \\const @"switch" = [
6164 \\ switch (m) {
6165 \\ 0 => {},
6166 \\ }
6167 \\]T;
6168 \\const switch_op = [
6169 \\ switch ({
6170 \\ {}
6171 \\ }) {}
6172 \\]T;
6173 \\const for_capture = [
6174 \\ for (a) |
6175 \\ _,
6176 \\ | {}
6177 \\]T;
6178 \\const for_expr = [
6179 \\ for ({
6180 \\ {}
6181 \\ }) |_| {}
6182 \\]T;
6183 \\const for_range = [
6184 \\ for ({
6185 \\ {}
6186 \\ }..15) |_| {}
6187 \\]T;
6188 \\const for_input_comma = [
6189 \\ for (
6190 \\ 0..15,
6191 \\ ) |_| {}
6192 \\]T;
6193 \\const call_param = [
6194 \\ a({
6195 \\ {}
6196 \\ })
6197 \\]T;
6198 \\const call_fn = [
6199 \\ ({
6200 \\ {}
6201 \\ })()
6202 \\]T;
6203 \\const builtin_call = [
6204 \\ @log2({
6205 \\ {}
6206 \\ })
6207 \\]T;
6208 \\const array_init = [
6209 \\ .{{
6210 \\ {}
6211 \\ }}
6212 \\]T;
6213 \\const struct_init = [
6214 \\ .{ .x = {
6215 \\ {}
6216 \\ } }
6217 \\]T;
6218 \\const @"asm" = [
6219 \\ asm (""
6220 \\ : [x] "" (-> T),
6221 \\ )
6222 \\]T;
6223 \\const asm_template = [
6224 \\ asm ({
6225 \\ {}
6226 \\ })
6227 \\]T;
6228 \\const asm_clobbers = [
6229 \\ asm ("" ::: {
6230 \\ {}
6231 \\ })
6232 \\]T;
6233 \\const @"fn" = [
6234 \\ fn (({
6235 \\ {}
6236 \\ })) void
6237 \\]T;
6238 \\const array_type_len = [
6239 \\ [
6240 \\ {
6241 \\ {}
6242 \\ }
6243 \\ ]T
6244 \\]T;
6245 \\const array_type_type = [
6246 \\ [1]({
6247 \\ {}
6248 \\ })
6249 \\]T;
6250 \\const array_access_array = [
6251 \\ ({
6252 \\ {}
6253 \\ })[1]
6254 \\]T;
6255 \\const array_access_index = [
6256 \\ x[
6257 \\ {
6258 \\ {}
6259 \\ }
6260 \\ ]
6261 \\]T;
6262 \\const binop = [
6263 \\ 1 + {
6264 \\ {}
6265 \\ }
6266 \\]T;
6267 \\const unaryop = [
6268 \\ !{
6269 \\ {}
6270 \\ }
6271 \\]T;
6272 \\const destructure = [
6273 \\ while (true) : (x, {
6274 \\ {}
6275 \\ } = z) {}
6276 \\]T;
6277 \\
6278 );
6279
6280 try testCanonical(
6281 \\const oneline_access_block = x[{}];
6282 \\const oneline_block = [{}]T;
6283 \\const oneline_sentinel_block = [15:{}]T;
6284 \\const oneline_container = [enum { A }]T;
6285 \\const oneline_error_set = [error{A}]T;
6286 \\const oneline_switch = [switch (m) {}]T;
6287 \\const oneline_for = [for (a, 0..15) |_, _| {}]T;
6288 \\const oneline_call = [a(a)]T;
6289 \\const oneline_builtin_call = [@log2(a)]T;
6290 \\const oneline_array_init = [.{a}]T;
6291 \\const oneline_struct_init = [.{ .x = a }]T;
6292 \\const oneline_asm = [asm ("" ::: .{})]T;
6293 \\const onlinee_fn = [fn (usize) void]T;
6294 \\const oneline_array_type = [[{}]T]T;
6295 \\const oneline_array_access = [x[{}]]T;
6296 \\const online_binop = [1 + 1]T;
6297 \\const online_unaryop = [!false]T;
6298 \\const oneline_destructure = [while (true) : (x, y = z) {}]T;
6299 \\
6300 );
6301
6302 try testTransform(
6303 \\const a = [{
6304 \\}]T;
6305 \\
6306 \\const b = x[{
6307 \\}];
6308 \\
6309 \\const c = [
6310 \\ {
6311 \\ }
6312 \\]T;
6313 \\
6314 \\const d = x[
6315 \\ {
6316 \\ }
6317 \\];
6318 \\
6319 ,
6320 \\const a = [
6321 \\ {}
6322 \\]T;
6323 \\
6324 \\const b = x[
6325 \\ {}
6326 \\];
6327 \\
6328 \\const c = [
6329 \\ {}
6330 \\]T;
6331 \\
6332 \\const d = x[
6333 \\ {}
6334 \\];
6335 \\
6336 );
6337}
6338
60976339test "zig fmt: whitespace with multiline strings" {
60986340 try testCanonical(
60996341 \\const a = .{