authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-09 20:04:36+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-09 15:51:44-07:00
log4ae62410d232d36f1ef72532d59d87cc1485ef76
tree324c1d136742fdfd9196206e795e68668dbc454d
parent96001011ecf85c1b08d4de843ce38a15a68ad6b5

Merge pull request 'zig fmt: optimize array inits' (#35987) from gooncreeper/zig:fmt-optimize-array into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35987 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

2 files changed, 393 insertions(+), 109 deletions(-)

lib/std/zig/Ast/Render.zig+270-109
...@@ -14,6 +14,8 @@ gpa: Allocator,...@@ -14,6 +14,8 @@ gpa: Allocator,
14ais: *AutoIndentingStream,14ais: *AutoIndentingStream,
15tree: Ast,15tree: Ast,
16fixups: Fixups,16fixups: Fixups,
17checking_newlines: bool,
18scratch_array_init: std.ArrayList(usize),
1719
18const indent_delta = 4;20const indent_delta = 4;
19const asm_indent_delta = 2;21const asm_indent_delta = 2;
...@@ -92,7 +94,10 @@ pub fn renderTree(gpa: Allocator, w: *Writer, tree: Ast, fixups: Fixups) Error!v...@@ -92,7 +94,10 @@ pub fn renderTree(gpa: Allocator, w: *Writer, tree: Ast, fixups: Fixups) Error!v
92 .ais = &auto_indenting_stream,94 .ais = &auto_indenting_stream,
93 .tree = tree,95 .tree = tree,
94 .fixups = fixups,96 .fixups = fixups,
97 .checking_newlines = false,
98 .scratch_array_init = .empty,
95 };99 };
100 defer r.scratch_array_init.deinit(gpa);
96101
97 // Render all the line comments at the beginning of the file.102 // Render all the line comments at the beginning of the file.
98 const comment_end_loc = tree.tokenStart(0);103 const comment_end_loc = tree.tokenStart(0);
...@@ -983,6 +988,8 @@ fn rendersMultiline(r: *const Render, node: Ast.Node.Index) error{OutOfMemory}!b...@@ -983,6 +988,8 @@ fn rendersMultiline(r: *const Render, node: Ast.Node.Index) error{OutOfMemory}!b
983 .ais = &sub_ais,988 .ais = &sub_ais,
984 .tree = r.tree,989 .tree = r.tree,
985 .fixups = r.fixups,990 .fixups = r.fixups,
991 .checking_newlines = true,
992 .scratch_array_init = undefined, // unused since `checking_newlines` is `true`
986 };993 };
987994
988 renderExpression(&sub_r, node, .none) catch |err| return switch (err) {995 renderExpression(&sub_r, node, .none) catch |err| return switch (err) {
...@@ -2101,6 +2108,21 @@ fn renderStructInit(...@@ -2101,6 +2108,21 @@ fn renderStructInit(
2101 return renderToken(r, rbrace, space);2108 return renderToken(r, rbrace, space);
2102}2109}
21032110
2111fn renderCommentsBetweenTokens(r: *Render, start: Ast.TokenIndex, end: Ast.TokenIndex) Error!void {
2112 const tree = r.tree;
2113
2114 var next_start = tree.tokenStart(start);
2115 for (start + 1..end) |token_i| {
2116 const token_start = next_start;
2117 next_start = tree.tokenStart(@intCast(token_i + 1));
2118 _ = try renderComments(
2119 r,
2120 token_start + tokenSliceForRender(tree, @intCast(token_i)).len,
2121 next_start,
2122 );
2123 }
2124}
2125
2104fn renderArrayInit(2126fn renderArrayInit(
2105 r: *Render,2127 r: *Render,
2106 array_init: Ast.full.ArrayInit,2128 array_init: Ast.full.ArrayInit,
...@@ -2157,137 +2179,276 @@ fn renderArrayInit(...@@ -2157,137 +2179,276 @@ fn renderArrayInit(
2157 }2179 }
2158 }2180 }
2159 return renderToken(r, last_elem_token + 1, space); // rbrace2181 return renderToken(r, last_elem_token + 1, space); // rbrace
2182 } else if (r.checking_newlines) {
2183 // To avoid taking quadratic time while rendering nested arrays, just write a newline
2184 // since it is already gauraunteed. However, comments still need to be written since they
2185 // may enable/disable zig fmt.
2186 try r.renderCommentsBetweenTokens(array_init.ast.lbrace, rbrace);
2187 return r.ais.insertNewline();
2160 }2188 }
21612189
2162 try ais.pushIndent(.normal);2190 try ais.pushIndent(.normal);
2163 try renderToken(r, array_init.ast.lbrace, .newline);2191 try renderToken(r, array_init.ast.lbrace, .newline);
2164 try ais.pushSpace(.comma);2192 try ais.pushSpace(.comma);
21652193
2166 const expr_widths = try gpa.alloc(enum(usize) {2194 const scratch = &r.scratch_array_init;
2167 /// The expression contains non-printable characters (e.g. unicode / newlines)2195 const scratch_start = scratch.items.len;
2168 /// or has formatting disabled at the start or end.2196 defer assert(scratch.items.len == scratch_start);
2169 nonprint = std.math.maxInt(usize),
2170 _,
2171 }, array_init.ast.elements.len);
2172 defer gpa.free(expr_widths);
2173 {
2174 var buf: Writer.Allocating = .init(gpa);
2175 defer buf.deinit();
2176 var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta);
2177 sub_ais.disabled_offset = ais.disabled_offset;
2178 defer sub_ais.deinit();
2179 var sub_r: Render = .{
2180 .gpa = r.gpa,
2181 .ais = &sub_ais,
2182 .tree = r.tree,
2183 .fixups = r.fixups,
2184 };
2185 for (array_init.ast.elements, expr_widths) |e, *width| {
2186 const begin_disabled = sub_ais.disabled_offset != null;
2187 // `.skip` space so trailing commments aren't included
2188 try renderExpressionComma(&sub_r, e, .skip);
2189 if (!begin_disabled and sub_ais.disabled_offset == null) {
2190 const w = buf.written();
2191 width.* = for (w) |c| {
2192 if (!std.ascii.isPrint(c))
2193 break .nonprint;
2194 } else @fromBackingInt(@intCast(w.len - @intFromBool(w[w.len - 1] == ',')));
2195 } else {
2196 width.* = .nonprint;
2197 }
21982197
2199 // Write trailing comments since they may enable/disable zig fmt2198 var sub_buf: Writer.Allocating = .init(gpa);
2200 buf.clearRetainingCapacity();2199 defer sub_buf.deinit();
2201 var after_expr = tree.lastToken(e);2200 var sub_ais: AutoIndentingStream = .init(gpa, &sub_buf.writer, indent_delta);
2202 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);2201 defer sub_ais.deinit();
2203 try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none);2202 var sub_r: Render = .{
22042203 .gpa = r.gpa,
2205 buf.clearRetainingCapacity();2204 .ais = &sub_ais,
2206 // The following are needed to make sure isLineOverIndented is not influenced by2205 .tree = r.tree,
2207 // the previous element.2206 .fixups = r.fixups,
2208 sub_ais.indent_count = 0;2207 .checking_newlines = true,
2209 sub_ais.applied_indent = 0;2208 .scratch_array_init = undefined, // unused since `checking_newlines` is `true`
2210 }2209 };
2211 }
22122210
2213 var remaining_exprs = array_init.ast.elements;2211 var remaining_exprs = array_init.ast.elements;
2214 var remaining_widths = expr_widths;2212 section: while (remaining_exprs.len != 0) {
2215 while (remaining_exprs.len != 0) {2213 defer scratch.items.len = scratch_start;
2216 var row_size: usize = 1;2214 // May not already be equal due to a section end and when `sub_ais` is initialized.
2217 for (1.., remaining_exprs, remaining_widths) |len, e, w| {2215 sub_ais.disabled_offset = ais.disabled_offset;
2218 if (w == .nonprint) break;2216
2219 row_size = len;2217 var n_section_exprs: usize = 0;
22202218 var n_cols: usize = 0;
2221 var after_expr = tree.lastToken(e);2219 var line_col: usize = 0;
2222 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);2220 var first_line: bool = true;
2223 assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);2221 // `scratch` will contain the width of the first `n_section_exprs`;
2224 if (!tree.tokensOnSameLine(after_expr, after_expr + 1))2222 // zero indicates the expression is on its own line.
2225 break;2223 while (true) {
2226 } else {2224 assert(scratch.items[scratch_start..].len == n_section_exprs);
2227 // All the expressions are on the same line.2225
2228 // However, if there is a trailing comma, we put them each on their own line.2226 const expr = remaining_exprs[n_section_exprs];
2229 if (tree.tokenTag(rbrace - 1) == .comma)2227 const final_expr = n_section_exprs + 1 == remaining_exprs.len;
2230 row_size = 1;2228 var elem_last = tree.lastToken(expr);
2231 }2229 elem_last += @intFromBool(tree.tokenTag(elem_last + 1) == .comma);
22322230 assert(tree.tokenTag(elem_last) == .comma or elem_last + 1 == rbrace);
2233 // Determine the size of this section2231
2234 const section_end = end: {2232 const own_line = ends_line: {
2235 var line_start = row_size; // Start after the first row to ignore comments on it2233 const ends_line = final_expr or !tree.tokensOnSameLine(elem_last, elem_last + 1);
2236 break :end for (line_start.., remaining_exprs[line_start..]) |i, e| {2234 if (ends_line and n_cols == 0) {
2237 const expr_first = tree.firstToken(e);2235 // Avoid doing extra work below since the width will not be required.
2238 // Any nonprint character terminates the line because they are always put on their2236 break :ends_line false;
2239 // own line, so they will not end up on the same line as the trailing comment.2237 } // Otherwise a correct value for `own_line` is required.
2240 if (expr_widths[i - 1] == .nonprint or !tree.tokensOnSameLine(expr_first - 1, expr_first)) {2238 // This is the case even for `n_cols == 1` since it may become `2` below.
2241 line_start = i;2239
2240 if (sub_ais.disabled_offset != null) {
2241 if (n_cols >= 2) {
2242 // Comments still need rendered since they may enable/disable zig fmt
2243 // for later expressions.
2244 try renderCommentsBetweenTokens(&sub_r, tree.firstToken(expr), elem_last + 1);
2245 sub_buf.clearRetainingCapacity();
2246 }
2247 break :ends_line true;
2248 }
2249 // `.skip` space so trailing commments aren't included
2250 try renderExpressionComma(&sub_r, expr, .skip);
2251 var own_line = sub_ais.disabled_offset != null;
2252 for (sub_buf.written()) |c| {
2253 if (!std.ascii.isPrint(c)) {
2254 // The expression contained a newline or unicode character so it is
2255 // put on its own line since there is no reasonable way to align it.
2256 own_line = true;
2257 }
2258 }
2259 const width = sub_buf.written().len -
2260 @intFromBool(sub_buf.written()[sub_buf.written().len - 1] == ',');
2261 sub_buf.clearRetainingCapacity();
2262
2263 // Write trailing comments since they may enable/disable zig fmt.
2264 _ = try renderComments(
2265 &sub_r,
2266 tree.tokenStart(elem_last) + tokenSliceForRender(tree, elem_last).len,
2267 tree.tokenStart(elem_last + 1),
2268 );
2269 sub_buf.clearRetainingCapacity();
2270
2271 // Make sure isLineOverIndented is not influenced for future elements.
2272 sub_ais.indent_count = 0;
2273 sub_ais.applied_indent = 0;
2274
2275 if (own_line) {
2276 break :ends_line true;
2242 }2277 }
22432278
2244 var after_expr = tree.lastToken(e);2279 // This is a valid element to be put on a line with others.
2245 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);2280 assert(width != 0);
2246 assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
2247 if (hasTrailingComment(tree, after_expr))
2248 break line_start;
2249 } else remaining_exprs.len;
2250 };
2251 const section_exprs = remaining_exprs[0..section_end];
2252 const section_widths = remaining_widths[0..section_end];
2253 remaining_exprs = remaining_exprs[section_end..];
2254 remaining_widths = remaining_widths[section_end..];
22552281
2256 // Determine the width of each column2282 try scratch.append(r.gpa, width);
2257 var col_widths = try gpa.alloc(usize, row_size);2283 line_col += 1;
2258 defer gpa.free(col_widths);2284 n_cols += @intFromBool(first_line);
2259 @memset(col_widths, 0);2285 n_section_exprs += 1;
2286
2287 if (ends_line) {
2288 break :ends_line false;
2289 }
22602290
2261 var col: usize = 0;
2262 for (section_widths) |w| {
2263 if (w == .nonprint) {
2264 col = 0;
2265 continue;2291 continue;
2292 };
2293
2294 if (n_cols <= 1 and first_line) {
2295 // Since there is only one column, there is no need to render each expression to
2296 // find its width. However, the section ending still needs to be accounted for, so
2297 // below we find the number of expressions that can be rendered before the section
2298 // might end.
2299 n_section_exprs = 1;
2300 n_cols = 1;
2301 line_col = 0;
2302 scratch.items.len = scratch_start;
2303
2304 while (n_section_exprs != remaining_exprs.len) {
2305 const s_expr = remaining_exprs[n_section_exprs];
2306 var s_elem_last = tree.lastToken(s_expr);
2307 s_elem_last += @intFromBool(tree.tokenTag(s_elem_last + 1) == .comma);
2308 assert(tree.tokenTag(s_elem_last) == .comma or s_elem_last + 1 == rbrace);
2309
2310 // `line_col` here does not account for expressions being put on their own line.
2311 if (tree.tokensOnSameLine(s_elem_last, s_elem_last + 1)) {
2312 line_col += 1;
2313 } else if (hasTrailingComment(tree, s_elem_last)) {
2314 n_section_exprs -= line_col;
2315 break;
2316 } else {
2317 line_col = 0;
2318 }
2319 n_section_exprs += 1;
2320 } else {
2321 line_col = 0;
2322 }
2323
2324 for (remaining_exprs[0..n_section_exprs]) |s_expr| {
2325 if (s_expr != array_init.ast.elements[0]) {
2326 try renderExtraNewline(r, s_expr);
2327 }
2328 try renderExpression(r, s_expr, .comma);
2329 }
2330 remaining_exprs = remaining_exprs[n_section_exprs..];
2331
2332 if (line_col != 0) {
2333 assert(remaining_exprs.len != 0);
2334
2335 // Since the final expressions may be forced on their own line, and so still
2336 // part of this section, the final row is handled by the slower path.
2337 sub_ais.disabled_offset = ais.disabled_offset;
2338 n_section_exprs = 0;
2339 line_col = 0;
2340 first_line = false;
2341 continue;
2342 }
2343
2344 continue :section;
2266 }2345 }
2267 col_widths[col] = @max(col_widths[col], @backingInt(w));2346
2268 col += 1;2347 first_line &= !own_line or n_section_exprs == 0;
2269 if (col == row_size) {2348
2270 col = 0;2349 if (!first_line and hasTrailingComment(tree, elem_last)) {
2350 // End of the section
2351 if (own_line) {
2352 line_col = 0;
2353 }
2354 n_section_exprs -= line_col;
2355 scratch.items.len -= line_col;
2356
2357 break;
2271 }2358 }
2359
2360 if (own_line) {
2361 // The expression has no width, so it is not yet present in `scratch`.
2362 try scratch.append(r.gpa, 0);
2363 n_section_exprs += 1;
2364 }
2365
2366 if (final_expr) {
2367 break;
2368 }
2369
2370 line_col = 0;
2371 first_line = false;
2272 }2372 }
2373 line_col = undefined;
22732374
2274 // Render each expression2375 const section_exprs = remaining_exprs[0..n_section_exprs];
2275 col = 0;2376 remaining_exprs = remaining_exprs[n_section_exprs..];
2276 for (0.., section_exprs, section_widths) |i, e, w| {2377
2277 if (i + 1 == section_end or col + 1 == row_size or2378 if (n_cols == 1 or
2278 w == .nonprint or section_widths[i + 1] == .nonprint)2379 // If all the expressions are on the same line with a
2279 {2380 // trailing comma, they are each put on their own line.
2280 try renderExpression(r, e, .comma);2381 (n_cols == array_init.ast.elements.len and
2281 col = 0;2382 tree.tokenTag(rbrace - 1) == .comma and tree.tokensOnSameLine(rbrace - 1, rbrace)))
2282 if (i + 1 != section_end) {2383 {
2283 try renderExtraNewline(r, section_exprs[i + 1]);2384 scratch.items.len = scratch_start;
2385 for (section_exprs) |expr| {
2386 if (expr != array_init.ast.elements[0]) {
2387 try renderExtraNewline(r, expr);
2388 } else {
2389 // Can only be the case if `n_cols` became `1` at the end (one line with a
2390 // trailing comma) since otherwise the first rows were already rendered.
2391 assert(n_section_exprs == array_init.ast.elements.len);
2284 }2392 }
2285 } else {2393 try renderExpression(r, expr, .comma);
2286 try renderExpression(r, e, .comma_space);2394 }
2287 try ais.splatByteAll(' ', col_widths[col] - @backingInt(w));2395 continue;
2396 }
2397
2398 {
2399 assert(scratch.items[scratch_start..].len == n_section_exprs);
2400 // Determine the width of each column. This cannot be done with the width calculations
2401 // above since the section may have ended, removing elements already iterated over.
2402 //
2403 // The first `n_section_exprs` of `scratch` will contain zero at each end-of-line.
2404 // The remaining `n_cols - 1` will contain the widths of each column.
2405 const col_widths = try scratch.addManyAsSlice(gpa, n_cols - 1);
2406
2407 @memcpy(col_widths, scratch.items[scratch_start..][0 .. n_cols - 1]);
2408 scratch.items[scratch_start + n_cols - 1] = 0;
2409
2410 var col: usize = 0;
2411 for (
2412 scratch_start + n_cols..,
2413 scratch.items[scratch_start..][n_cols..n_section_exprs],
2414 ) |i, width| {
2415 if (width == 0) {
2416 scratch.items[i - 1] = 0;
2417 col = 0;
2418 } else if (col + 1 == n_cols) {
2419 scratch.items[i] = 0;
2420 col = 0;
2421 } else {
2422 col_widths[col] = @max(col_widths[col], width);
2423 col += 1;
2424 }
2425 }
2426
2427 scratch.items[scratch_start + n_section_exprs - 1] = 0;
2428 }
2429
2430 var col: usize = 0;
2431 for (0.., section_exprs) |i, expr| {
2432 assert(col < n_cols);
2433
2434 if (col == 0 and expr != array_init.ast.elements[0]) {
2435 try renderExtraNewline(r, expr);
2436 }
2437
2438 const width = scratch.items[scratch_start + i];
2439 if (width != 0) {
2440 try renderExpression(r, expr, .comma_space);
2441
2442 const col_width = scratch.items[scratch_start + n_section_exprs + col];
2443 try ais.splatByteAll(' ', col_width - width);
2444
2288 col += 1;2445 col += 1;
2446 } else {
2447 try renderExpression(r, expr, .comma);
2448 col = 0;
2289 }2449 }
2290 }2450 }
2451 assert(col == 0);
2291 }2452 }
22922453
2293 ais.popSpace();2454 ais.popSpace();
lib/std/zig/parser_test.zig+123
...@@ -1937,6 +1937,12 @@ test "zig fmt: array literal with hint" {...@@ -1937,6 +1937,12 @@ test "zig fmt: array literal with hint" {
1937 \\ 5, 6, //1937 \\ 5, 6, //
1938 \\ 7, 8, //1938 \\ 7, 8, //
1939 \\};1939 \\};
1940 \\const a = .{
1941 \\ 1,
1942 \\ 2, 3, '±', //
1943 \\ 4, 5,
1944 \\ 6, 7, //
1945 \\};
1940 ,1946 ,
1941 \\const a = []u8{1947 \\const a = []u8{
1942 \\ 1, 2, //1948 \\ 1, 2, //
...@@ -1973,6 +1979,15 @@ test "zig fmt: array literal with hint" {...@@ -1973,6 +1979,15 @@ test "zig fmt: array literal with hint" {
1973 \\ 5, 6, //1979 \\ 5, 6, //
1974 \\ 7, 8, //1980 \\ 7, 8, //
1975 \\};1981 \\};
1982 \\const a = .{
1983 \\ 1,
1984 \\ 2,
1985 \\ 3,
1986 \\ '±', //
1987 \\ 4,
1988 \\ 5,
1989 \\ 6, 7, //
1990 \\};
1976 \\1991 \\
1977 );1992 );
1978}1993}
...@@ -5006,6 +5021,10 @@ test "zig fmt: single argument trailing commas in @builtins()" {...@@ -5006,6 +5021,10 @@ test "zig fmt: single argument trailing commas in @builtins()" {
5006test "zig fmt: trailing comma should force multiline 1 column" {5021test "zig fmt: trailing comma should force multiline 1 column" {
5007 try testTransform(5022 try testTransform(
5008 \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,};5023 \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,};
5024 \\pub const UUID_NULL: uuid_t = [16]u8{
5025 \\0,0,0,0, };
5026 \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,
5027 \\};
5009 \\5028 \\
5010 ,5029 ,
5011 \\pub const UUID_NULL: uuid_t = [16]u8{5030 \\pub const UUID_NULL: uuid_t = [16]u8{
...@@ -5014,6 +5033,15 @@ test "zig fmt: trailing comma should force multiline 1 column" {...@@ -5014,6 +5033,15 @@ test "zig fmt: trailing comma should force multiline 1 column" {
5014 \\ 0,5033 \\ 0,
5015 \\ 0,5034 \\ 0,
5016 \\};5035 \\};
5036 \\pub const UUID_NULL: uuid_t = [16]u8{
5037 \\ 0,
5038 \\ 0,
5039 \\ 0,
5040 \\ 0,
5041 \\};
5042 \\pub const UUID_NULL: uuid_t = [16]u8{
5043 \\ 0, 0, 0, 0,
5044 \\};
5017 \\5045 \\
5018 );5046 );
5019}5047}
...@@ -7051,6 +7079,101 @@ test "zig fmt: canonicalize @enumFromInt(x) to @fromBackingInt(@intCast(x))" {...@@ -7051,6 +7079,101 @@ test "zig fmt: canonicalize @enumFromInt(x) to @fromBackingInt(@intCast(x))" {
7051 );7079 );
7052}7080}
70537081
7082test "zig fmt: linear time for nested array inits" {
7083 const source = comptime source: {
7084 var source: []const u8 = "const a = .{\n";
7085 var indent: []const u8 = " ";
7086
7087 for (0..32) |_| {
7088 source = source ++ indent ++ ".{},\n" ++ indent ++ ".{\n";
7089 indent = indent ++ " ";
7090 }
7091 source = source ++ indent ++ ".{},\n";
7092 for (0..32) |_| {
7093 indent = indent[4..];
7094 source = source ++ indent ++ "},\n";
7095 }
7096
7097 break :source source ++ "};\n";
7098 };
7099 try testCanonical(source);
7100}
7101
7102test "zig fmt: on/off with nested array inits" {
7103 try testCanonical(
7104 \\const a = .{
7105 \\ .{
7106 \\ 0, // zig fmt: off
7107 \\ },
7108 \\ 1, 2, // zig fmt: on
7109 \\ 3,
7110 \\ 4,
7111 \\};
7112 \\const b = .{
7113 \\ // zig fmt: off
7114 \\ .{
7115 \\ 0, // zig fmt: on
7116 \\ },
7117 \\ 1, 2, //
7118 \\ 3, 4,
7119 \\};
7120 \\const c = .{
7121 \\ .{
7122 \\ // zig fmt: off
7123 \\ 0, // zig fmt: on
7124 \\ },
7125 \\ 1, 2, //
7126 \\ 3, 4,
7127 \\};
7128 \\
7129 );
7130}
7131
7132test "zig fmt: empty lines in array inits" {
7133 try testTransform(
7134 \\const a = .{
7135 \\ 1,
7136 \\
7137 \\ 2, //
7138 \\ 3,
7139 \\
7140 \\ 4, 5, //
7141 \\ 6,
7142 \\
7143 \\ 7,
7144 \\
7145 \\ 8,
7146 \\};
7147 \\
7148 ,
7149 \\const a = .{
7150 \\ 1,
7151 \\
7152 \\ 2, //
7153 \\ 3,
7154 \\
7155 \\ 4, 5, //
7156 \\ 6, 7,
7157 \\
7158 \\ 8,
7159 \\};
7160 \\
7161 );
7162}
7163
7164test "zig fmt: nested array init with asm" {
7165 try testCanonical(
7166 \\const a = .{
7167 \\ asm (.{
7168 \\ 0,
7169 \\ }
7170 \\ : [_] "" (_),
7171 \\ ),
7172 \\};
7173 \\
7174 );
7175}
7176
7054test "recovery: top level" {7177test "recovery: top level" {
7055 try testError(7178 try testError(
7056 \\test "" {inline}7179 \\test "" {inline}