const std = @import("std"); const testing = std.testing; const Ast = std.zig.Ast; const Allocator = std.mem.Allocator; const c = @cImport({ @cInclude("ast.h"); }); const zigToken = @import("./tokenizer_test.zig").zigToken; fn zigNode(token: c_uint) Ast.Node.Tag { return switch (token) { c.AST_NODE_ROOT => .root, c.AST_NODE_TEST_DECL => .test_decl, c.AST_NODE_GLOBAL_VAR_DECL => .global_var_decl, c.AST_NODE_LOCAL_VAR_DECL => .local_var_decl, c.AST_NODE_SIMPLE_VAR_DECL => .simple_var_decl, c.AST_NODE_ALIGNED_VAR_DECL => .aligned_var_decl, c.AST_NODE_ERRDEFER => .@"errdefer", c.AST_NODE_DEFER => .@"defer", c.AST_NODE_CATCH => .@"catch", c.AST_NODE_FIELD_ACCESS => .field_access, c.AST_NODE_UNWRAP_OPTIONAL => .unwrap_optional, c.AST_NODE_EQUAL_EQUAL => .equal_equal, c.AST_NODE_BANG_EQUAL => .bang_equal, c.AST_NODE_LESS_THAN => .less_than, c.AST_NODE_GREATER_THAN => .greater_than, c.AST_NODE_LESS_OR_EQUAL => .less_or_equal, c.AST_NODE_GREATER_OR_EQUAL => .greater_or_equal, c.AST_NODE_ASSIGN_MUL => .assign_mul, c.AST_NODE_ASSIGN_DIV => .assign_div, c.AST_NODE_ASSIGN_MOD => .assign_mod, c.AST_NODE_ASSIGN_ADD => .assign_add, c.AST_NODE_ASSIGN_SUB => .assign_sub, c.AST_NODE_ASSIGN_SHL => .assign_shl, c.AST_NODE_ASSIGN_SHL_SAT => .assign_shl_sat, c.AST_NODE_ASSIGN_SHR => .assign_shr, c.AST_NODE_ASSIGN_BIT_AND => .assign_bit_and, c.AST_NODE_ASSIGN_BIT_XOR => .assign_bit_xor, c.AST_NODE_ASSIGN_BIT_OR => .assign_bit_or, c.AST_NODE_ASSIGN_MUL_WRAP => .assign_mul_wrap, c.AST_NODE_ASSIGN_ADD_WRAP => .assign_add_wrap, c.AST_NODE_ASSIGN_SUB_WRAP => .assign_sub_wrap, c.AST_NODE_ASSIGN_MUL_SAT => .assign_mul_sat, c.AST_NODE_ASSIGN_ADD_SAT => .assign_add_sat, c.AST_NODE_ASSIGN_SUB_SAT => .assign_sub_sat, c.AST_NODE_ASSIGN => .assign, c.AST_NODE_ASSIGN_DESTRUCTURE => .assign_destructure, c.AST_NODE_MERGE_ERROR_SETS => .merge_error_sets, c.AST_NODE_MUL => .mul, c.AST_NODE_DIV => .div, c.AST_NODE_MOD => .mod, c.AST_NODE_ARRAY_MULT => .array_mult, c.AST_NODE_MUL_WRAP => .mul_wrap, c.AST_NODE_MUL_SAT => .mul_sat, c.AST_NODE_ADD => .add, c.AST_NODE_SUB => .sub, c.AST_NODE_ARRAY_CAT => .array_cat, c.AST_NODE_ADD_WRAP => .add_wrap, c.AST_NODE_SUB_WRAP => .sub_wrap, c.AST_NODE_ADD_SAT => .add_sat, c.AST_NODE_SUB_SAT => .sub_sat, c.AST_NODE_SHL => .shl, c.AST_NODE_SHL_SAT => .shl_sat, c.AST_NODE_SHR => .shr, c.AST_NODE_BIT_AND => .bit_and, c.AST_NODE_BIT_XOR => .bit_xor, c.AST_NODE_BIT_OR => .bit_or, c.AST_NODE_ORELSE => .@"orelse", c.AST_NODE_BOOL_AND => .bool_and, c.AST_NODE_BOOL_OR => .bool_or, c.AST_NODE_BOOL_NOT => .bool_not, c.AST_NODE_NEGATION => .negation, c.AST_NODE_BIT_NOT => .bit_not, c.AST_NODE_NEGATION_WRAP => .negation_wrap, c.AST_NODE_ADDRESS_OF => .address_of, c.AST_NODE_TRY => .@"try", c.AST_NODE_OPTIONAL_TYPE => .optional_type, c.AST_NODE_ARRAY_TYPE => .array_type, c.AST_NODE_ARRAY_TYPE_SENTINEL => .array_type_sentinel, c.AST_NODE_PTR_TYPE_ALIGNED => .ptr_type_aligned, c.AST_NODE_PTR_TYPE_SENTINEL => .ptr_type_sentinel, c.AST_NODE_PTR_TYPE => .ptr_type, c.AST_NODE_PTR_TYPE_BIT_RANGE => .ptr_type_bit_range, c.AST_NODE_SLICE_OPEN => .slice_open, c.AST_NODE_SLICE => .slice, c.AST_NODE_SLICE_SENTINEL => .slice_sentinel, c.AST_NODE_DEREF => .deref, c.AST_NODE_ARRAY_ACCESS => .array_access, c.AST_NODE_ARRAY_INIT_ONE => .array_init_one, c.AST_NODE_ARRAY_INIT_ONE_COMMA => .array_init_one_comma, c.AST_NODE_ARRAY_INIT_DOT_TWO => .array_init_dot_two, c.AST_NODE_ARRAY_INIT_DOT_TWO_COMMA => .array_init_dot_two_comma, c.AST_NODE_ARRAY_INIT_DOT => .array_init_dot, c.AST_NODE_ARRAY_INIT_DOT_COMMA => .array_init_dot_comma, c.AST_NODE_ARRAY_INIT => .array_init, c.AST_NODE_ARRAY_INIT_COMMA => .array_init_comma, c.AST_NODE_STRUCT_INIT_ONE => .struct_init_one, c.AST_NODE_STRUCT_INIT_ONE_COMMA => .struct_init_one_comma, c.AST_NODE_STRUCT_INIT_DOT_TWO => .struct_init_dot_two, c.AST_NODE_STRUCT_INIT_DOT_TWO_COMMA => .struct_init_dot_two_comma, c.AST_NODE_STRUCT_INIT_DOT => .struct_init_dot, c.AST_NODE_STRUCT_INIT_DOT_COMMA => .struct_init_dot_comma, c.AST_NODE_STRUCT_INIT => .struct_init, c.AST_NODE_STRUCT_INIT_COMMA => .struct_init_comma, c.AST_NODE_CALL_ONE => .call_one, c.AST_NODE_CALL_ONE_COMMA => .call_one_comma, c.AST_NODE_CALL => .call, c.AST_NODE_CALL_COMMA => .call_comma, c.AST_NODE_SWITCH => .@"switch", c.AST_NODE_SWITCH_COMMA => .switch_comma, c.AST_NODE_SWITCH_CASE_ONE => .switch_case_one, c.AST_NODE_SWITCH_CASE_INLINE_ONE => .switch_case_inline_one, c.AST_NODE_SWITCH_CASE => .switch_case, c.AST_NODE_SWITCH_CASE_INLINE => .switch_case_inline, c.AST_NODE_SWITCH_RANGE => .switch_range, c.AST_NODE_WHILE_SIMPLE => .while_simple, c.AST_NODE_WHILE_CONT => .while_cont, c.AST_NODE_WHILE => .@"while", c.AST_NODE_FOR_SIMPLE => .for_simple, c.AST_NODE_FOR => .@"for", c.AST_NODE_FOR_RANGE => .for_range, c.AST_NODE_IF_SIMPLE => .if_simple, c.AST_NODE_IF => .@"if", c.AST_NODE_SUSPEND => .@"suspend", c.AST_NODE_RESUME => .@"resume", c.AST_NODE_CONTINUE => .@"continue", c.AST_NODE_BREAK => .@"break", c.AST_NODE_RETURN => .@"return", c.AST_NODE_FN_PROTO_SIMPLE => .fn_proto_simple, c.AST_NODE_FN_PROTO_MULTI => .fn_proto_multi, c.AST_NODE_FN_PROTO_ONE => .fn_proto_one, c.AST_NODE_FN_PROTO => .fn_proto, c.AST_NODE_FN_DECL => .fn_decl, c.AST_NODE_ANYFRAME_TYPE => .anyframe_type, c.AST_NODE_ANYFRAME_LITERAL => .anyframe_literal, c.AST_NODE_CHAR_LITERAL => .char_literal, c.AST_NODE_NUMBER_LITERAL => .number_literal, c.AST_NODE_UNREACHABLE_LITERAL => .unreachable_literal, c.AST_NODE_IDENTIFIER => .identifier, c.AST_NODE_ENUM_LITERAL => .enum_literal, c.AST_NODE_STRING_LITERAL => .string_literal, c.AST_NODE_MULTILINE_STRING_LITERAL => .multiline_string_literal, c.AST_NODE_GROUPED_EXPRESSION => .grouped_expression, c.AST_NODE_BUILTIN_CALL_TWO => .builtin_call_two, c.AST_NODE_BUILTIN_CALL_TWO_COMMA => .builtin_call_two_comma, c.AST_NODE_BUILTIN_CALL => .builtin_call, c.AST_NODE_BUILTIN_CALL_COMMA => .builtin_call_comma, c.AST_NODE_ERROR_SET_DECL => .error_set_decl, c.AST_NODE_CONTAINER_DECL => .container_decl, c.AST_NODE_CONTAINER_DECL_TRAILING => .container_decl_trailing, c.AST_NODE_CONTAINER_DECL_TWO => .container_decl_two, c.AST_NODE_CONTAINER_DECL_TWO_TRAILING => .container_decl_two_trailing, c.AST_NODE_CONTAINER_DECL_ARG => .container_decl_arg, c.AST_NODE_CONTAINER_DECL_ARG_TRAILING => .container_decl_arg_trailing, c.AST_NODE_TAGGED_UNION => .tagged_union, c.AST_NODE_TAGGED_UNION_TRAILING => .tagged_union_trailing, c.AST_NODE_TAGGED_UNION_TWO => .tagged_union_two, c.AST_NODE_TAGGED_UNION_TWO_TRAILING => .tagged_union_two_trailing, c.AST_NODE_TAGGED_UNION_ENUM_TAG => .tagged_union_enum_tag, c.AST_NODE_TAGGED_UNION_ENUM_TAG_TRAILING => .tagged_union_enum_tag_trailing, c.AST_NODE_CONTAINER_FIELD_INIT => .container_field_init, c.AST_NODE_CONTAINER_FIELD_ALIGN => .container_field_align, c.AST_NODE_CONTAINER_FIELD => .container_field, c.AST_NODE_COMPTIME => .@"comptime", c.AST_NODE_NOSUSPEND => .@"nosuspend", c.AST_NODE_BLOCK_TWO => .block_two, c.AST_NODE_BLOCK_TWO_SEMICOLON => .block_two_semicolon, c.AST_NODE_BLOCK => .block, c.AST_NODE_BLOCK_SEMICOLON => .block_semicolon, c.AST_NODE_ASM_SIMPLE => .asm_simple, c.AST_NODE_ASM => .@"asm", c.AST_NODE_ASM_OUTPUT => .asm_output, c.AST_NODE_ASM_INPUT => .asm_input, c.AST_NODE_ERROR_VALUE => .error_value, c.AST_NODE_ERROR_UNION => .error_union, else => undefined, }; } fn toIndex(v: u32) Ast.Node.Index { return @enumFromInt(v); } fn toOptIndex(v: u32) Ast.Node.OptionalIndex { return if (v == 0) .none else @enumFromInt(v); } fn toExtraIndex(v: u32) Ast.ExtraIndex { return @enumFromInt(v); } fn toOptTokenIndex(v: u32) Ast.OptionalTokenIndex { return @enumFromInt(v); } fn zigData(tag: Ast.Node.Tag, lhs: u32, rhs: u32) Ast.Node.Data { return switch (tag) { // data unused .identifier, .string_literal, .char_literal, .number_literal, .unreachable_literal, .anyframe_literal, .enum_literal, .error_value, => .{ .opt_node_and_opt_node = .{ toOptIndex(lhs), toOptIndex(rhs) } }, // .node (single node index) .@"defer", .@"comptime", .@"nosuspend", .@"suspend", .@"resume", .bool_not, .negation, .bit_not, .negation_wrap, .address_of, .@"try", .deref, .optional_type, => .{ .node = toIndex(lhs) }, // .opt_node (single optional node) .@"return", => .{ .opt_node = toOptIndex(lhs) }, // .node_and_node .fn_decl, .container_field_align, .error_union, .@"catch", .equal_equal, .bang_equal, .less_than, .greater_than, .less_or_equal, .greater_or_equal, .assign_mul, .assign_div, .assign_mod, .assign_add, .assign_sub, .assign_shl, .assign_shl_sat, .assign_shr, .assign_bit_and, .assign_bit_xor, .assign_bit_or, .assign_mul_wrap, .assign_add_wrap, .assign_sub_wrap, .assign_mul_sat, .assign_add_sat, .assign_sub_sat, .assign, .merge_error_sets, .mul, .div, .mod, .array_mult, .mul_wrap, .mul_sat, .add, .sub, .array_cat, .add_wrap, .sub_wrap, .add_sat, .sub_sat, .shl, .shl_sat, .shr, .bit_and, .bit_xor, .bit_or, .@"orelse", .bool_and, .bool_or, .array_type, .array_access, .switch_range, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, // .opt_node_and_opt_node .fn_proto_simple, .simple_var_decl, .block_two, .block_two_semicolon, .builtin_call_two, .builtin_call_two_comma, .container_decl_two, .container_decl_two_trailing, .tagged_union_two, .tagged_union_two_trailing, .struct_init_dot_two, .struct_init_dot_two_comma, .array_init_dot_two, .array_init_dot_two_comma, => .{ .opt_node_and_opt_node = .{ toOptIndex(lhs), toOptIndex(rhs) } }, // .node_and_opt_node .call_one, .call_one_comma, .struct_init_one, .struct_init_one_comma, .container_field_init, .aligned_var_decl, => .{ .node_and_opt_node = .{ toIndex(lhs), toOptIndex(rhs) } }, // .node_and_node (array_init_one uses node_and_node, not // node_and_opt_node) .array_init_one, .array_init_one_comma, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, // .opt_node_and_node .ptr_type_aligned, .ptr_type_sentinel, .switch_case_one, .switch_case_inline_one, => .{ .opt_node_and_node = .{ toOptIndex(lhs), toIndex(rhs) } }, // .node_and_extra .call, .call_comma, .container_field, .array_type_sentinel, .slice, .slice_sentinel, .array_init, .array_init_comma, .struct_init, .struct_init_comma, .@"switch", .switch_comma, .container_decl_arg, .container_decl_arg_trailing, .tagged_union_enum_tag, .tagged_union_enum_tag_trailing, .@"asm", => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, // .extra_and_node .assign_destructure, .switch_case, .switch_case_inline, .ptr_type, .ptr_type_bit_range, => .{ .extra_and_node = .{ toExtraIndex(lhs), toIndex(rhs) } }, // .extra_and_opt_node .global_var_decl, .local_var_decl, .fn_proto_multi, .fn_proto_one, .fn_proto, => .{ .extra_and_opt_node = .{ toExtraIndex(lhs), toOptIndex(rhs) } }, // .extra_range (SubRange) .root, .block, .block_semicolon, .builtin_call, .builtin_call_comma, .container_decl, .container_decl_trailing, .tagged_union, .tagged_union_trailing, .array_init_dot, .array_init_dot_comma, .struct_init_dot, .struct_init_dot_comma, => .{ .extra_range = .{ .start = toExtraIndex(lhs), .end = toExtraIndex(rhs) } }, // .node_and_token .grouped_expression, .asm_input, .field_access, .unwrap_optional, => .{ .node_and_token = .{ toIndex(lhs), rhs } }, // .opt_node_and_token .asm_output, => .{ .opt_node_and_token = .{ toOptIndex(lhs), rhs } }, // .opt_token_and_node .test_decl, .@"errdefer", => .{ .opt_token_and_node = .{ toOptTokenIndex(lhs), toIndex(rhs) } }, // .opt_token_and_opt_node .@"break", .@"continue", => .{ .opt_token_and_opt_node = .{ toOptTokenIndex(lhs), toOptIndex(rhs) } }, // .token_and_token .error_set_decl, .multiline_string_literal, => .{ .token_and_token = .{ lhs, rhs } }, // .token_and_node .anyframe_type, => .{ .token_and_node = .{ lhs, toIndex(rhs) } }, // .node_and_node for slice_open (lhs[rhs..]) .slice_open, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, .while_simple, .for_simple, .if_simple, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, .while_cont, .@"while", .@"if", => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, .for_range, => .{ .node_and_opt_node = .{ toIndex(lhs), toOptIndex(rhs) } }, .@"for", => .{ .@"for" = .{ toExtraIndex(lhs), @bitCast(rhs) } }, .asm_simple, .asm_legacy, => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, }; } // zigAst converts a c.Ast to std.Zig.Ast. The resulting Ast should be freed with deinit(). fn zigAst(gpa: Allocator, c_ast: c.Ast) !Ast { var tokens = Ast.TokenList{}; try tokens.resize(gpa, c_ast.tokens.len); errdefer tokens.deinit(gpa); for (0..c_ast.tokens.len) |i| tokens.set(i, .{ .tag = zigToken(c_ast.tokens.tags[i]), .start = c_ast.tokens.starts[i], }); var nodes = Ast.NodeList{}; try nodes.resize(gpa, c_ast.nodes.len); errdefer nodes.deinit(gpa); for (0..c_ast.nodes.len) |i| { const tag = zigNode(c_ast.nodes.tags[i]); nodes.set(i, .{ .tag = tag, .main_token = c_ast.nodes.main_tokens[i], .data = zigData(tag, c_ast.nodes.datas[i].lhs, c_ast.nodes.datas[i].rhs), }); } const extra_data = try gpa.alloc(u32, c_ast.extra_data.len); errdefer gpa.free(extra_data); @memcpy(extra_data, c_ast.extra_data.arr[0..c_ast.extra_data.len]); // creating a dummy `errors` slice, so deinit can free it. const errors = try gpa.alloc(Ast.Error, 0); errdefer gpa.free(errors); return Ast{ .source = c_ast.source[0..c_ast.source_len :0], .mode = .zig, .tokens = tokens.slice(), .nodes = nodes.slice(), .extra_data = extra_data, .errors = errors, }; } test "Ast header smoke test" { try std.testing.expectEqual(zigNode(c.AST_NODE_IF), Ast.Node.Tag.@"if"); } // copy-past from parser_test.zig const mem = std.mem; const print = std.debug.print; const io = std.io; const maxInt = std.math.maxInt; var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 { var stderr_buf: [4096]u8 = undefined; var stderr_file_writer = std.fs.File.stderr().writer(&stderr_buf); const stderr = &stderr_file_writer.interface; //var tree = try std.zig.Ast.parse(allocator, source, .zig); var c_tree = c.astParse(source, @intCast(source.len)); defer c.astDeinit(&c_tree); var tree = try zigAst(allocator, c_tree); defer tree.deinit(allocator); for (tree.errors) |parse_error| { const loc = tree.tokenLocation(0, parse_error.token); try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); try tree.renderError(parse_error, stderr); try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { try stderr.writeAll(" "); } try stderr.writeAll("^"); } try stderr.writeAll("\n"); } if (tree.errors.len != 0) { return error.ParseError; } const formatted = try tree.renderAlloc(allocator); anything_changed.* = !mem.eql(u8, formatted, source); return formatted; } fn testTransformImpl(allocator: mem.Allocator, fba: *std.heap.FixedBufferAllocator, source: [:0]const u8, expected_source: []const u8) !void { // reset the fixed buffer allocator each run so that it can be re-used for each // iteration of the failing index fba.reset(); var anything_changed: bool = undefined; const result_source = try testParse(source, allocator, &anything_changed); try std.testing.expectEqualStrings(expected_source, result_source); const changes_expected = source.ptr != expected_source.ptr; if (anything_changed != changes_expected) { print("std.zig.render returned {} instead of {}\n", .{ anything_changed, changes_expected }); return error.TestFailed; } try std.testing.expect(anything_changed == changes_expected); allocator.free(result_source); } fn testTransform(source: [:0]const u8, expected_source: []const u8) !void { var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); return std.testing.checkAllAllocationFailures(fixed_allocator.allocator(), testTransformImpl, .{ &fixed_allocator, source, expected_source }); } fn testCanonical(source: [:0]const u8) !void { return testTransform(source, source); } test "zig fmt: remove extra whitespace at start and end of file with comment between" { try testTransform( \\ \\ \\// hello \\ \\ , \\// hello \\ ); } test "my function" { try testCanonical( \\pub fn main() void { \\ @panic("hello"); \\} \\ ); } test "zig fmt: tuple struct" { try testCanonical( \\const T = struct { \\ /// doc comment on tuple field \\ comptime comptime u32, \\ /// another doc comment on tuple field \\ *u32 = 1, \\ // needs to be wrapped in parentheses to not be parsed as a function decl \\ (fn () void) align(1), \\}; \\ ); } test "zig fmt: respect line breaks in struct field value declaration" { try testCanonical( \\const Foo = struct { \\ bar: u32 = \\ 42, \\ bar: u32 = \\ // a comment \\ 42, \\ bar: u32 = \\ 42, \\ // a comment \\ bar: []const u8 = \\ \\ foo \\ \\ bar \\ \\ baz \\ , \\ bar: u32 = \\ blk: { \\ break :blk 42; \\ }, \\}; \\ ); } test "zig fmt: respect line breaks before functions" { try testCanonical( \\const std = @import("std"); \\ \\inline fn foo() void {} \\ \\noinline fn foo() void {} \\ \\export fn foo() void {} \\ \\extern fn foo() void; \\ \\extern "foo" fn foo() void; \\ ); } test "zig fmt: simple top level comptime block" { try testCanonical( \\// line comment \\comptime {} \\ ); } test "zig fmt: two spaced line comments before decl" { try testCanonical( \\// line comment \\ \\// another \\comptime {} \\ ); } test "zig fmt: respect line breaks after var declarations" { try testCanonical( \\const crc = \\ lookup_tables[0][p[7]] ^ \\ lookup_tables[1][p[6]] ^ \\ lookup_tables[2][p[5]] ^ \\ lookup_tables[3][p[4]] ^ \\ lookup_tables[4][@as(u8, self.crc >> 24)] ^ \\ lookup_tables[5][@as(u8, self.crc >> 16)] ^ \\ lookup_tables[6][@as(u8, self.crc >> 8)] ^ \\ lookup_tables[7][@as(u8, self.crc >> 0)]; \\ ); } test "zig fmt: multiline string mixed with comments" { try testCanonical( \\const s1 = \\ //\\one \\ \\two) \\ \\three \\; \\const s2 = \\ \\one \\ \\two) \\ //\\three \\; \\const s3 = \\ \\one \\ //\\two) \\ \\three \\; \\const s4 = \\ \\one \\ //\\two \\ \\three \\ //\\four \\ \\five \\; \\const a = \\ 1; \\ ); } test "zig fmt: empty file" { try testCanonical( \\ ); } test "zig fmt: file ends in comment" { try testTransform( \\ //foobar , \\//foobar \\ ); } test "zig fmt: file ends in multi line comment" { try testTransform( \\ \\foobar , \\\\foobar \\ ); } test "zig fmt: file ends in comment after var decl" { try testTransform( \\const x = 42; \\ //foobar , \\const x = 42; \\//foobar \\ ); } test "zig fmt: top-level fields" { try testCanonical( \\a: did_you_know, \\b: all_files_are, \\structs: ?x, \\ ); } test "zig fmt: container declaration, single line" { try testCanonical( \\const X = struct { foo: i32 }; \\const X = struct { foo: i32, bar: i32 }; \\const X = struct { foo: i32 = 1, bar: i32 = 2 }; \\const X = struct { foo: i32 align(4), bar: i32 align(4) }; \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 }; \\ ); } test "zig fmt: container declaration, one item, multi line trailing comma" { try testCanonical( \\test "" { \\ comptime { \\ const X = struct { \\ x: i32, \\ }; \\ } \\} \\ ); } test "zig fmt: container declaration, no trailing comma on separate line" { try testTransform( \\test "" { \\ comptime { \\ const X = struct { \\ x: i32 \\ }; \\ } \\} \\ , \\test "" { \\ comptime { \\ const X = struct { x: i32 }; \\ } \\} \\ ); } test "zig fmt: container declaration, line break, no trailing comma" { try testTransform( \\const X = struct { \\ foo: i32, bar: i8 }; , \\const X = struct { foo: i32, bar: i8 }; \\ ); } test "zig fmt: container declaration, transform trailing comma" { try testTransform( \\const X = struct { \\ foo: i32, bar: i8, }; , \\const X = struct { \\ foo: i32, \\ bar: i8, \\}; \\ ); } test "zig fmt: container declaration, comment, add trailing comma" { try testTransform( \\const X = struct { \\ foo: i32, // foo \\ bar: i8 \\}; , \\const X = struct { \\ foo: i32, // foo \\ bar: i8, \\}; \\ ); try testTransform( \\const X = struct { \\ foo: i32 // foo \\}; , \\const X = struct { \\ foo: i32, // foo \\}; \\ ); } test "zig fmt: container declaration, multiline string, add trailing comma" { try testTransform( \\const X = struct { \\ foo: []const u8 = \\ \\ foo \\ , \\ bar: i8 \\}; , \\const X = struct { \\ foo: []const u8 = \\ \\ foo \\ , \\ bar: i8, \\}; \\ ); } test "zig fmt: container declaration, doc comment on member, add trailing comma" { try testTransform( \\pub const Pos = struct { \\ /// X-axis. \\ x: u32, \\ /// Y-axis. \\ y: u32 \\}; , \\pub const Pos = struct { \\ /// X-axis. \\ x: u32, \\ /// Y-axis. \\ y: u32, \\}; \\ ); } test "zig fmt: remove empty lines at start/end of container decl" { try testTransform( \\const X = struct { \\ \\ foo: i32, \\ \\ bar: i8, \\ \\}; \\ , \\const X = struct { \\ foo: i32, \\ \\ bar: i8, \\}; \\ ); } test "zig fmt: allow empty line before comment at start of block" { try testCanonical( \\test { \\ \\ // foo \\ const x = 42; \\} \\ ); } test "zig fmt: comptime struct field" { try testCanonical( \\const Foo = struct { \\ a: i32, \\ comptime b: i32 = 1234, \\}; \\ ); } test "zig fmt: break from block" { try testCanonical( \\const a = blk: { \\ break :blk 42; \\}; \\const b = blk: { \\ break :blk; \\}; \\const c = { \\ break 42; \\}; \\const d = { \\ break; \\}; \\ ); } test "zig fmt: grouped expressions (parentheses)" { try testCanonical( \\const r = (x + y) * (a + b); \\ ); } test "zig fmt: array types last token" { try testCanonical( \\test { \\ const x = [40]u32; \\} \\ \\test { \\ const x = [40:0]u32; \\} \\ ); } test "zig fmt: anon struct literal 0 element" { try testCanonical( \\test { \\ const x = .{}; \\} \\ ); } test "zig fmt: anon struct literal 1 element" { try testCanonical( \\test { \\ const x = .{ .a = b }; \\} \\ ); } test "zig fmt: anon struct literal 1 element comma" { try testCanonical( \\test { \\ const x = .{ \\ .a = b, \\ }; \\} \\ ); } test "zig fmt: anon struct literal 2 element" { try testCanonical( \\test { \\ const x = .{ .a = b, .c = d }; \\} \\ ); } test "zig fmt: anon struct literal 2 element comma" { try testCanonical( \\test { \\ const x = .{ \\ .a = b, \\ .c = d, \\ }; \\} \\ ); } test "zig fmt: anon struct literal 3 element" { try testCanonical( \\test { \\ const x = .{ .a = b, .c = d, .e = f }; \\} \\ ); } test "zig fmt: anon struct literal 3 element comma" { try testCanonical( \\test { \\ const x = .{ \\ .a = b, \\ .c = d, \\ .e = f, \\ }; \\} \\ ); } test "zig fmt: struct literal 0 element" { try testCanonical( \\test { \\ const x = X{}; \\} \\ ); } test "zig fmt: struct literal 1 element" { try testCanonical( \\test { \\ const x = X{ .a = b }; \\} \\ ); } test "zig fmt: struct literal 2 element" { try testCanonical( \\test { \\ const x = X{ .a = b, .c = d }; \\} \\ ); } test "zig fmt: struct literal 2 element comma" { try testCanonical( \\test { \\ const x = X{ \\ .a = b, \\ .c = d, \\ }; \\} \\ ); } test "zig fmt: struct literal 3 element" { try testCanonical( \\test { \\ const x = X{ .a = b, .c = d, .e = f }; \\} \\ ); } test "zig fmt: struct literal 3 element comma" { try testCanonical( \\test { \\ const x = X{ \\ .a = b, \\ .c = d, \\ .e = f, \\ }; \\} \\ ); } test "zig fmt: anon list literal 1 element" { try testCanonical( \\test { \\ const x = .{a}; \\} \\ ); } test "zig fmt: anon list literal 1 element comma" { try testCanonical( \\test { \\ const x = .{ \\ a, \\ }; \\} \\ ); } test "zig fmt: anon list literal 2 element" { try testCanonical( \\test { \\ const x = .{ a, b }; \\} \\ ); } test "zig fmt: anon list literal 2 element comma" { try testCanonical( \\test { \\ const x = .{ \\ a, \\ b, \\ }; \\} \\ ); } test "zig fmt: anon list literal 3 element" { try testCanonical( \\test { \\ const x = .{ a, b, c }; \\} \\ ); } test "zig fmt: anon list literal 3 element comma" { try testCanonical( \\test { \\ const x = .{ \\ a, \\ // foo \\ b, \\ \\ c, \\ }; \\} \\ ); } test "zig fmt: array literal 0 element" { try testCanonical( \\test { \\ const x = [_]u32{}; \\} \\ ); } test "zig fmt: array literal 1 element" { try testCanonical( \\test { \\ const x = [_]u32{a}; \\} \\ ); } test "zig fmt: array literal 1 element comma" { try testCanonical( \\test { \\ const x = [1]u32{ \\ a, \\ }; \\} \\ ); } test "zig fmt: array literal 2 element" { try testCanonical( \\test { \\ const x = [_]u32{ a, b }; \\} \\ ); } test "zig fmt: array literal 2 element comma" { try testCanonical( \\test { \\ const x = [2]u32{ \\ a, \\ b, \\ }; \\} \\ ); } test "zig fmt: array literal 3 element" { try testCanonical( \\test { \\ const x = [_]u32{ a, b, c }; \\} \\ ); } test "zig fmt: array literal 3 element comma" { try testCanonical( \\test { \\ const x = [3]u32{ \\ a, \\ b, \\ c, \\ }; \\} \\ ); } test "zig fmt: slices" { try testCanonical( \\const a = b[0..]; \\const c = d[0..1]; \\const d = f[0.. :0]; \\const e = f[0..1 :0]; \\ ); } test "zig fmt: tagged union with enum values" { try testCanonical( \\const MultipleChoice2 = union(enum(u32)) { \\ Unspecified1: i32, \\ A: f32 = 20, \\ Unspecified2: void, \\ B: bool = 40, \\ Unspecified3: i32, \\ C: i8 = 60, \\ Unspecified4: void, \\ D: void = 1000, \\ Unspecified5: i32, \\}; \\ ); } test "zig fmt: tagged union enum tag last token" { try testCanonical( \\test { \\ const U = union(enum(u32)) {}; \\} \\ \\test { \\ const U = union(enum(u32)) { foo }; \\} \\ \\test { \\ const U = union(enum(u32)) { \\ foo, \\ }; \\} \\ ); } test "zig fmt: empty enum decls" { try testCanonical( \\const A = enum {}; \\const B = enum(u32) {}; \\const C = extern enum(c_int) {}; \\const D = packed enum(u8) {}; \\ ); } test "zig fmt: empty union decls" { try testCanonical( \\const A = union {}; \\const B = union(enum) {}; \\const C = union(Foo) {}; \\const D = extern union {}; \\const E = packed union {}; \\ ); } test "zig fmt: enum literal" { try testCanonical( \\const x = .hi; \\ ); } test "zig fmt: character literal larger than u8" { try testCanonical( \\const x = '\u{01f4a9}'; \\ ); } test "zig fmt: infix operator and then multiline string literal" { try testCanonical( \\const x = "" ++ \\ \\ hi \\; \\ ); } test "zig fmt: infix operator and then multiline string literal over multiple lines" { try testCanonical( \\const x = "" ++ \\ \\ hi0 \\ \\ hi1 \\ \\ hi2 \\; \\ ); } test "zig fmt: correctly space struct fields with doc comments" { try testTransform( \\pub const S = struct { \\ /// A \\ a: u8, \\ /// B \\ /// B (cont) \\ b: u8, \\ \\ \\ /// C \\ c: u8, \\}; \\ , \\pub const S = struct { \\ /// A \\ a: u8, \\ /// B \\ /// B (cont) \\ b: u8, \\ \\ /// C \\ c: u8, \\}; \\ ); } test "zig fmt: aligned struct field" { try testCanonical( \\pub const S = struct { \\ f: i32 align(32), \\}; \\ ); try testCanonical( \\pub const S = struct { \\ f: i32 align(32) = 1, \\}; \\ ); } test "zig fmt: comment to disable/enable zig fmt first" { try testCanonical( \\// Test trailing comma syntax \\// zig fmt: off \\ \\const struct_trailing_comma = struct { x: i32, y: i32, }; ); } test "zig fmt: trailing comma in fn parameter list" { try testCanonical( \\pub fn f( \\ a: i32, \\ b: i32, \\) i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) align(8) i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) addrspace(.generic) i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) linksection(".text") i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) callconv(.c) i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) align(8) linksection(".text") i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) align(8) callconv(.c) i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) align(8) linksection(".text") callconv(.c) i32 {} \\pub fn f( \\ a: i32, \\ b: i32, \\) linksection(".text") callconv(.c) i32 {} \\ ); } test "zig fmt: enum literal inside array literal" { try testCanonical( \\test "enums in arrays" { \\ var colors = []Color{.Green}; \\ colors = []Colors{ .Green, .Cyan }; \\ colors = []Colors{ \\ .Grey, \\ .Green, \\ .Cyan, \\ }; \\} \\ ); } test "zig fmt: builtin call with trailing comma" { try testCanonical( \\pub fn main() void { \\ @breakpoint(); \\ _ = @intFromBool(a); \\ _ = @call( \\ a, \\ b, \\ c, \\ ); \\} \\ ); } test "zig fmt: comment to disable/enable zig fmt" { try testTransform( \\const a = b; \\// zig fmt: off \\const c = d; \\// zig fmt: on \\const e = f; , \\const a = b; \\// zig fmt: off \\const c = d; \\// zig fmt: on \\const e = f; \\ ); } test "zig fmt: line comment following 'zig fmt: off'" { try testCanonical( \\// zig fmt: off \\// Test \\const e = f; ); } test "zig fmt: doc comment following 'zig fmt: off'" { try testCanonical( \\// zig fmt: off \\/// test \\const e = f; ); } test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" { try testCanonical( \\// zig fmt: off \\// zig fmt: on \\// zig fmt: off \\const e = f; \\// zig fmt: off \\// zig fmt: on \\// zig fmt: off \\const a = b; \\// zig fmt: on \\const c = d; \\// zig fmt: on \\ ); } test "zig fmt: spaces around slice operator" { try testCanonical( \\var a = b[c..d]; \\var a = b[c..d :0]; \\var a = b[c + 1 .. d]; \\var a = b[c + 1 ..]; \\var a = b[c .. d + 1]; \\var a = b[c .. d + 1 :0]; \\var a = b[c.a..d.e]; \\var a = b[c.a..d.e :0]; \\ ); } test "zig fmt: respect line breaks after infix operators" { try testCanonical( \\comptime { \\ self.crc = \\ lookup_tables[0][p[7]] ^ \\ lookup_tables[1][p[6]] ^ \\ lookup_tables[2][p[5]] ^ \\ lookup_tables[3][p[4]] ^ \\ lookup_tables[4][@as(u8, self.crc >> 24)] ^ \\ lookup_tables[5][@as(u8, self.crc >> 16)] ^ \\ lookup_tables[6][@as(u8, self.crc >> 8)] ^ \\ lookup_tables[7][@as(u8, self.crc >> 0)]; \\} \\ ); } test "zig fmt: fn decl with trailing comma" { try testTransform( \\fn foo(a: i32, b: i32,) void {} , \\fn foo( \\ a: i32, \\ b: i32, \\) void {} \\ ); } test "zig fmt: enum decl with no trailing comma" { try testTransform( \\const StrLitKind = enum {Normal, C}; , \\const StrLitKind = enum { Normal, C }; \\ ); } test "zig fmt: struct literal no trailing comma" { try testTransform( \\const a = foo{ .x = 1, .y = 2 }; \\const a = foo{ .x = 1, \\ .y = 2 }; \\const a = foo{ .x = 1, \\ .y = 2, }; , \\const a = foo{ .x = 1, .y = 2 }; \\const a = foo{ .x = 1, .y = 2 }; \\const a = foo{ \\ .x = 1, \\ .y = 2, \\}; \\ ); } test "zig fmt: 2nd arg multiline string" { try testCanonical( \\comptime { \\ cases.addAsm("hello world linux x86_64", \\ \\.text \\ , "Hello, world!\n"); \\} \\ ); try testCanonical( \\comptime { \\ cases.addAsm("hello world linux x86_64", \\ \\.text \\ , "Hello, world!\n", "Hello, world!\n"); \\} \\ ); } test "zig fmt: final arg multiline string" { try testCanonical( \\comptime { \\ cases.addAsm("hello world linux x86_64", "Hello, world!\n", \\ \\.text \\ ); \\} \\ ); } test "zig fmt: function call with multiline argument" { try testCanonical( \\comptime { \\ self.user_input_options.put(name, UserInputOption{ \\ .name = name, \\ .used = false, \\ }); \\} \\ ); } test "zig fmt: if statement" { try testCanonical( \\test "" { \\ if (optional()) |some| \\ bar = some.foo(); \\} \\ ); } test "zig fmt: respect line breaks in if-else" { try testCanonical( \\comptime { \\ return if (cond) a else b; \\ return if (cond) \\ a \\ else \\ b; \\ return if (cond) \\ a \\ else if (cond) \\ b \\ else \\ c; \\} \\ ); } test "zig fmt: if nested" { try testCanonical( \\pub fn foo() void { \\ return if ((aInt & bInt) >= 0) \\ if (aInt < bInt) \\ GE_LESS \\ else if (aInt == bInt) \\ GE_EQUAL \\ else \\ GE_GREATER \\ // comment \\ else if (aInt > bInt) \\ GE_LESS \\ else if (aInt == bInt) \\ GE_EQUAL \\ else \\ GE_GREATER; \\ // comment \\} \\ ); } test "zig fmt: remove empty lines at start/end of block" { try testTransform( \\test { \\ \\ if (foo) { \\ foo(); \\ } \\ \\} \\ , \\test { \\ if (foo) { \\ foo(); \\ } \\} \\ ); } test "zig fmt: multiline string with backslash at end of line" { try testCanonical( \\comptime { \\ err( \\ \\\ \\ ); \\} \\ ); } test "zig fmt: multiline string parameter in fn call with trailing comma" { try testCanonical( \\fn foo() void { \\ try stdout.print( \\ \\ZIG_CMAKE_BINARY_DIR {s} \\ \\ZIG_C_HEADER_FILES {s} \\ \\ZIG_DIA_GUIDS_LIB {s} \\ \\ \\ , \\ std.mem.sliceTo(c.ZIG_CMAKE_BINARY_DIR, 0), \\ std.mem.sliceTo(c.ZIG_CXX_COMPILER, 0), \\ std.mem.sliceTo(c.ZIG_DIA_GUIDS_LIB, 0), \\ ); \\} \\ ); } test "zig fmt: trailing comma on fn call" { try testCanonical( \\comptime { \\ var module = try Module.create( \\ allocator, \\ zig_lib_dir, \\ full_cache_dir, \\ ); \\} \\ ); } test "zig fmt: multi line arguments without last comma" { try testTransform( \\pub fn foo( \\ a: usize, \\ b: usize, \\ c: usize, \\ d: usize \\) usize { \\ return a + b + c + d; \\} \\ , \\pub fn foo(a: usize, b: usize, c: usize, d: usize) usize { \\ return a + b + c + d; \\} \\ ); } test "zig fmt: empty block with only comment" { try testCanonical( \\comptime { \\ { \\ // comment \\ } \\} \\ ); } test "zig fmt: trailing commas on struct decl" { try testTransform( \\const RoundParam = struct { \\ k: usize, s: u32, t: u32 \\}; \\const RoundParam = struct { \\ k: usize, s: u32, t: u32, \\}; , \\const RoundParam = struct { k: usize, s: u32, t: u32 }; \\const RoundParam = struct { \\ k: usize, \\ s: u32, \\ t: u32, \\}; \\ ); } test "zig fmt: extra newlines at the end" { try testTransform( \\const a = b; \\ \\ \\ , \\const a = b; \\ ); } test "zig fmt: nested struct literal with one item" { try testCanonical( \\const a = foo{ \\ .item = bar{ .a = b }, \\}; \\ ); } test "zig fmt: if-else with comment before else" { try testCanonical( \\comptime { \\ // cexp(finite|nan +- i inf|nan) = nan + i nan \\ if ((hx & 0x7fffffff) != 0x7f800000) { \\ return Complex(f32).init(y - y, y - y); \\ } // cexp(-inf +- i inf|nan) = 0 + i0 \\ else if (hx & 0x80000000 != 0) { \\ return Complex(f32).init(0, 0); \\ } // cexp(+inf +- i inf|nan) = inf + i nan \\ else { \\ return Complex(f32).init(x, y - y); \\ } \\} \\ ); } test "zig fmt: nosuspend block" { try testCanonical( \\pub fn main() anyerror!void { \\ nosuspend { \\ var foo: Foo = .{ .bar = 42 }; \\ } \\} \\ ); } test "zig fmt: c pointer type" { try testCanonical( \\pub extern fn repro() [*c]const u8; \\ ); } test "zig fmt: sentinel array literal 1 element" { try testCanonical( \\test { \\ const x = [_:9000]u32{a}; \\} \\ ); } test "zig fmt: anon literal in array" { try testCanonical( \\var arr: [2]Foo = .{ \\ .{ .a = 2 }, \\ .{ .b = 3 }, \\}; \\ ); } test "zig fmt: Unicode code point literal larger than u8" { try testCanonical( \\test { \\ const x = X{ \\ .a = b, \\ }; \\} \\ ); } test "zig fmt: slices with spaces in bounds" { try testCanonical( \\const a = b[0 + 0 ..]; \\const c = d[0 + 0 .. 1]; \\const c = d[0 + 0 .. :0]; \\const e = f[0 .. 1 + 1 :0]; \\ ); } test "zig fmt: C pointers" { try testCanonical( \\const Ptr = [*c]i32; \\ ); } test "zig fmt: pointer-to-one with modifiers" { try testCanonical( \\const x: *u32 = undefined; \\const y: *allowzero align(8) addrspace(.generic) const volatile u32 = undefined; \\const z: *allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; \\ ); } test "zig fmt: pointer-to-many with modifiers" { try testCanonical( \\const x: [*]u32 = undefined; \\const y: [*]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; \\const z: [*]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; \\ ); } test "zig fmt: sentinel pointer with modifiers" { try testCanonical( \\const x: [*:42]u32 = undefined; \\const y: [*:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; \\const y: [*:42]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; \\ ); } test "zig fmt: c pointer with modifiers" { try testCanonical( \\const x: [*c]u32 = undefined; \\const y: [*c]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; \\const z: [*c]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; \\ ); } test "zig fmt: slice with modifiers" { try testCanonical( \\const x: []u32 = undefined; \\const y: []allowzero align(8) addrspace(.generic) const volatile u32 = undefined; \\ ); } test "zig fmt: sentinel slice with modifiers" { try testCanonical( \\const x: [:42]u32 = undefined; \\const y: [:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; \\ ); } test "zig fmt: allowzero pointer" { try testCanonical( \\const T = [*]allowzero const u8; \\ ); } test "zig fmt: threadlocal" { try testCanonical( \\threadlocal var x: i32 = 1234; \\ ); } test "zig fmt: linksection" { try testCanonical( \\export var aoeu: u64 linksection(".text.derp") = 1234; \\export fn _start() linksection(".text.boot") callconv(.naked) noreturn {} \\ ); } test "zig fmt: addrspace" { try testCanonical( \\export var python_length: u64 align(1) addrspace(.generic); \\export var python_color: Color addrspace(.generic) = .green; \\export var python_legs: u0 align(8) addrspace(.generic) linksection(".python") = 0; \\export fn python_hiss() align(8) addrspace(.generic) linksection(".python") void; \\ ); } test "zig fmt: alignment" { try testCanonical( \\var foo: c_int align(1); \\ ); } test "zig fmt: C main" { try testCanonical( \\fn main(argc: c_int, argv: **u8) c_int { \\ const a = b; \\} \\ ); } test "zig fmt: return" { try testCanonical( \\fn foo(argc: c_int, argv: **u8) c_int { \\ return 0; \\} \\ \\fn bar() void { \\ return; \\} \\ ); } test "zig fmt: arrays" { try testCanonical( \\test "arrays" { \\ const a: [2]u32 = .{ 1, 2 }; \\ const b = a ++ a; \\ const c = a[0..]; \\ _ = c; \\} \\ ); } test "zig fmt: blocks" { try testCanonical( \\test { \\ { \\ const a = b; \\ } \\ const c = d; \\} \\ ); } test "zig fmt: container doc comments" { try testCanonical( \\//! tld 1 \\//! tld 2 \\//! tld 3 \\const a = b; \\ ); } test "zig fmt: comments before global variables" { try testCanonical( \\/// comment \\var foo: i32 = undefined; \\ ); } test "zig fmt: comments before test decl" { try testCanonical( \\/// top level doc comment \\test "hi" {} \\ ); } test "zig fmt: decimal float literals with underscore separators" { try testCanonical( \\const x = 1_234_567.89_10_11; \\const y = 1_234_567.89_10_11e1_213_14; \\const z = 1_234_567; \\ ); } test "zig fmt: comptime" { try testCanonical( \\fn foo() void { \\ comptime { \\ bar(); \\ } \\} \\ ); } test "zig fmt: comptime block in container" { try testCanonical( \\const Foo = struct { \\ comptime { \\ @compileLog("hello comptime"); \\ } \\}; \\ ); } test "zig fmt: comments before var decl in struct" { try testCanonical( \\const Foo = struct { \\ /// comment \\ bar: bool = true, \\}; \\ ); } test "zig fmt: block with same line comment after end brace" { try testCanonical( \\test { \\ { \\ const a = b; \\ } // end of block \\} \\ ); } test "zig fmt: comment after empty comment" { try testCanonical( \\// \\/// A doc comment \\const a = b; \\ ); } test "zig fmt: comment after params" { try testCanonical( \\fn foo( \\ a: i32, // comment \\ b: i32, // comment \\) void {} \\ ); } test "zig fmt: sentinel-terminated array type" { try testCanonical( \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 { \\ return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); \\} \\ ); } test "zig fmt: sentinel-terminated slice type" { try testCanonical( \\pub fn toSlice(self: Buffer) [:0]u8 { \\ return self.list.toSlice()[0..self.len()]; \\} \\ ); } test "zig fmt: top-level tuple function call type" { try testCanonical( \\foo() \\ ); } test "zig fmt: top-level bare asterisk+identifier" { try testCanonical( \\*x \\ ); } test "zig fmt: top-level bare asterisk+asterisk+identifier" { try testCanonical( \\**x \\ ); } test "zig fmt: errdefer with payload" { try testCanonical( \\pub fn main() anyerror!void { \\ errdefer |a| x += 1; \\ errdefer |a| {} \\ errdefer |a| { \\ x += 1; \\ } \\} \\ ); }