diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e5961621bdd5..91f3c99e6eb3 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -6107,6 +6107,91 @@ test "zig fmt: indentation of comments within catch, else, orelse" { ); } +test "zig fmt: nested switch in struct initialization" { + try testTransform( + \\const bar = .{ .{ switch ({}) { else => {}, } }, .{}, .{}, .{}, }; + \\ + , + \\const bar = .{ + \\ .{switch ({}) { + \\ else => {}, + \\ }}, + \\ .{}, + \\ .{}, + \\ .{}, + \\}; + \\ + ); +} + +test "zig fmt: nested switch in struct initialization is deterministic" { + // This test verifies that the formatter produces the same output + // when run multiple times on the same input + const formatted = + \\const bar = .{ + \\ .{switch ({}) { + \\ else => {}, + \\ }}, + \\ .{}, + \\ .{}, + \\ .{}, + \\}; + \\ + ; + try testCanonical(formatted); +} + +test "zig fmt: nested switch in struct initialization with multiple cases" { + try testTransform( + \\const foo = .{ .{ switch (x) { 0 => {}, 1 => {}, else => {}, } }, .{}, .{}, }; + \\ + , + \\const foo = .{ + \\ .{switch (x) { + \\ 0 => {}, + \\ 1 => {}, + \\ else => {}, + \\ }}, + \\ .{}, + \\ .{}, + \\}; + \\ + ); +} + +test "zig fmt: nested switch in middle of struct initialization" { + try testTransform( + \\const baz = .{ .{}, .{ switch (y) { true => 1, false => 2, } }, .{}, }; + \\ + , + \\const baz = .{ + \\ .{}, + \\ .{switch (y) { + \\ true => 1, + \\ false => 2, + \\ }}, + \\ .{}, + \\}; + \\ + ); +} + +test "zig fmt: deeply nested switch in struct initialization" { + try testTransform( + \\const complex = .{ .{ .{ switch (z) { .a => 10, .b => 20, } } }, .{}, }; + \\ + , + \\const complex = .{ + \\ .{.{switch (z) { + \\ .a => 10, + \\ .b => 20, + \\ }}}, + \\ .{}, + \\}; + \\ + ); +} + test "recovery: top level" { try testError( \\test "" {inline} diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 586bb9696b08..794a1442a929 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -3223,6 +3223,12 @@ fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usi const maybe_comma = rtoken - 1; if (tree.tokenTag(maybe_comma) == .comma) return 1; + + // Check if the first expression contains a switch statement + // If it does, we want to put it on its own line + if (containsComplexExpression(tree, exprs[0])) + return 1; + return exprs.len; // no newlines } @@ -3231,6 +3237,12 @@ fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usi if (i + 1 < exprs.len) { const expr_last_token = tree.lastToken(expr) + 1; if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count; + + // If this expression contains a complex expression like a switch, + // we want to start a new row after it + if (containsComplexExpression(tree, expr)) + return count; + count += 1; } else { return count; @@ -3239,6 +3251,26 @@ fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usi unreachable; } +// Helper function to check if a node contains a complex expression like a switch +fn containsComplexExpression(tree: Ast, node: Ast.Node.Index) bool { + const tag = tree.nodeTag(node); + return switch (tag) { + .@"switch" => true, + + // For struct initializations, check if any of their fields contain a switch + .struct_init_one, .struct_init_one_comma, .struct_init_dot_two, .struct_init_dot_two_comma, .struct_init_dot, .struct_init_dot_comma, .struct_init, .struct_init_comma => { + var buf: [2]Ast.Node.Index = undefined; + const struct_init = tree.fullStructInit(&buf, node).?; + for (struct_init.ast.fields) |field| { + if (containsComplexExpression(tree, field)) return true; + } + return false; + }, + + else => false, + }; +} + /// Automatically inserts indentation of written data by keeping /// track of the current indentation level ///