motiejus/zig

fork of https://codeberg.org/ziglang/zig
git clone https://git.jakstys.lt/motiejus/zig.git
Log | Tree | Refs | README | LICENSE

commit 885581323ca2f78fde42a1d6a10b44f047a5a69a (tree)
parent 80f4342dd1e6b5f62be418d6bc917897d6433edf
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date:   Wed, 11 Feb 2026 06:11:57 +0000

reorder tests better

Diffstat:
MREADME.md | 6+-----
Mparser_test.zig | 4372++++++++++++++++++++++++++++++++++++++++---------------------------------------
2 files changed, 2188 insertions(+), 2190 deletions(-)

diff --git a/README.md b/README.md @@ -4,11 +4,7 @@ zig0 aspires to be an interpreter of zig 0.15.1 written in C. Quick test: - zig build - -If it complains about formatting, here is a mutable command that will fix it: - - zig build fmt + zig build fmt && zig build # Debugging tips diff --git a/parser_test.zig b/parser_test.zig @@ -1,550 +1,8 @@ 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_LEGACY => .asm_legacy, - 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, - .asm_simple, - .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_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, - }; -} - - -// 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); -} - - - - +const mem = std.mem; +const print = std.debug.print; +const io = std.io; +const maxInt = std.math.maxInt; test "zig fmt: remove extra whitespace at start and end of file with comment between" { try testTransform( @@ -807,6 +265,13 @@ test "zig fmt: top-level tuple function call type" { ); } +test "zig fmt: top-level enum missing 'const name ='" { + try testError( + \\enum(u32) + \\ + , &[_]Error{.expected_token}); +} + test "zig fmt: top-level for/while loop" { try testCanonical( \\for (foo) |_| foo @@ -3405,2126 +2870,2663 @@ test "zig fmt: precedence" { ); } -test "zig fmt: prefix operators" { +test "zig fmt: prefix operators" { + try testCanonical( + \\test "prefix operators" { + \\ try return --%~!&0; + \\} + \\ + ); +} + +test "zig fmt: call expression" { + try testCanonical( + \\test "test calls" { + \\ a(); + \\ a(1); + \\ a(1, 2); + \\ a(1, 2) + a(1, 2); + \\} + \\ + ); +} + +test "zig fmt: anytype type" { + try testCanonical( + \\fn print(args: anytype) @This() {} + \\ + ); +} + +test "zig fmt: functions" { + try testCanonical( + \\extern fn puts(s: *const u8) c_int; + \\extern "c" fn puts(s: *const u8) c_int; + \\export fn puts(s: *const u8) c_int; + \\inline fn puts(s: *const u8) c_int; + \\noinline fn puts(s: *const u8) c_int; + \\pub extern fn puts(s: *const u8) c_int; + \\pub extern "c" fn puts(s: *const u8) c_int; + \\pub export fn puts(s: *const u8) c_int; + \\pub inline fn puts(s: *const u8) c_int; + \\pub noinline fn puts(s: *const u8) c_int; + \\pub extern fn puts(s: *const u8) align(2 + 2) c_int; + \\pub extern "c" fn puts(s: *const u8) align(2 + 2) c_int; + \\pub export fn puts(s: *const u8) align(2 + 2) c_int; + \\pub inline fn puts(s: *const u8) align(2 + 2) c_int; + \\pub noinline fn puts(s: *const u8) align(2 + 2) c_int; + \\pub fn callInlineFn(func: fn () callconv(.@"inline") void) void { + \\ func(); + \\} + \\ + ); +} + +test "zig fmt: multiline string" { + try testCanonical( + \\test "" { + \\ const s1 = + \\ \\one + \\ \\two) + \\ \\three + \\ ; + \\ const s3 = // hi + \\ \\one + \\ \\two) + \\ \\three + \\ ; + \\} + \\ + ); +} + +test "zig fmt: multiline string with CRLF line endings" { + try testTransform("" ++ + "const s =\r\n" ++ + " \\\\one\r\n" ++ + " \\\\two)\r\n" ++ + " \\\\three\r\n" ++ + ";\r\n", + \\const s = + \\ \\one + \\ \\two) + \\ \\three + \\; + \\ + ); +} + +test "zig fmt: values" { + try testCanonical( + \\test "values" { + \\ 1; + \\ 1.0; + \\ "string"; + \\ 'c'; + \\ true; + \\ false; + \\ null; + \\ undefined; + \\ anyerror; + \\ this; + \\ unreachable; + \\} + \\ + ); +} + +test "zig fmt: indexing" { + try testCanonical( + \\test "test index" { + \\ a[0]; + \\ a[0 + 5]; + \\ a[0..]; + \\ a[0..5]; + \\ a[a[0]]; + \\ a[a[0..]]; + \\ a[a[0..5]]; + \\ a[a[0]..]; + \\ a[a[0..5]..]; + \\ a[a[0]..a[0]]; + \\ a[a[0..5]..a[0]]; + \\ a[a[0..5]..a[0..5]]; + \\} + \\ + ); +} + +test "zig fmt: struct declaration" { + try testCanonical( + \\const S = struct { + \\ const Self = @This(); + \\ f1: u8, + \\ f3: u8, + \\ + \\ f2: u8, + \\ + \\ fn method(self: *Self) Self { + \\ return self.*; + \\ } + \\}; + \\ + \\const Ps = packed struct { + \\ a: u8, + \\ b: u8, + \\ + \\ c: u8, + \\}; + \\ + \\const Ps = packed struct(u32) { + \\ a: u1, + \\ b: u2, + \\ + \\ c: u29, + \\}; + \\ + \\const Es = extern struct { + \\ a: u8, + \\ b: u8, + \\ + \\ c: u8, + \\}; + \\ + ); +} + +test "zig fmt: enum declaration" { + try testCanonical( + \\const E = enum { + \\ Ok, + \\ SomethingElse = 0, + \\}; + \\ + \\const E2 = enum(u8) { + \\ Ok, + \\ SomethingElse = 255, + \\ SomethingThird, + \\}; + \\ + \\const Ee = extern enum { + \\ Ok, + \\ SomethingElse, + \\ SomethingThird, + \\}; + \\ + \\const Ep = packed enum { + \\ Ok, + \\ SomethingElse, + \\ SomethingThird, + \\}; + \\ + ); +} + +test "zig fmt: union declaration" { + try testCanonical( + \\const U = union { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + \\const Ue = union(enum) { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + \\const E = enum { + \\ Int, + \\ Float, + \\ None, + \\ Bool, + \\}; + \\ + \\const Ue2 = union(E) { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + \\const Eu = extern union { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + ); +} + +test "zig fmt: arrays" { try testCanonical( - \\test "prefix operators" { - \\ try return --%~!&0; + \\test "arrays" { + \\ const a: [2]u32 = .{ 1, 2 }; + \\ const b = a ++ a; + \\ const c = a[0..]; + \\ _ = c; \\} \\ ); } -test "zig fmt: call expression" { +test "zig fmt: container initializers" { try testCanonical( - \\test "test calls" { - \\ a(); - \\ a(1); - \\ a(1, 2); - \\ a(1, 2) + a(1, 2); - \\} + \\const a0 = []u8{}; + \\const a1 = []u8{1}; + \\const a2 = []u8{ + \\ 1, + \\ 2, + \\ 3, + \\ 4, + \\}; + \\const s0 = S{}; + \\const s1 = S{ .a = 1 }; + \\const s2 = S{ + \\ .a = 1, + \\ .b = 2, + \\}; \\ ); } -test "zig fmt: anytype type" { +test "zig fmt: catch" { try testCanonical( - \\fn print(args: anytype) @This() {} + \\test "catch" { + \\ const a: anyerror!u8 = 0; + \\ _ = a catch return; + \\ _ = a catch + \\ return; + \\ _ = a catch |err| return; + \\ _ = a catch |err| + \\ return; + \\} \\ ); } -test "zig fmt: functions" { +test "zig fmt: blocks" { try testCanonical( - \\extern fn puts(s: *const u8) c_int; - \\extern "c" fn puts(s: *const u8) c_int; - \\export fn puts(s: *const u8) c_int; - \\inline fn puts(s: *const u8) c_int; - \\noinline fn puts(s: *const u8) c_int; - \\pub extern fn puts(s: *const u8) c_int; - \\pub extern "c" fn puts(s: *const u8) c_int; - \\pub export fn puts(s: *const u8) c_int; - \\pub inline fn puts(s: *const u8) c_int; - \\pub noinline fn puts(s: *const u8) c_int; - \\pub extern fn puts(s: *const u8) align(2 + 2) c_int; - \\pub extern "c" fn puts(s: *const u8) align(2 + 2) c_int; - \\pub export fn puts(s: *const u8) align(2 + 2) c_int; - \\pub inline fn puts(s: *const u8) align(2 + 2) c_int; - \\pub noinline fn puts(s: *const u8) align(2 + 2) c_int; - \\pub fn callInlineFn(func: fn () callconv(.@"inline") void) void { - \\ func(); + \\test { + \\ { + \\ const a = b; + \\ } + \\ const c = d; \\} \\ ); } -test "zig fmt: multiline string" { +test "zig fmt: switch" { try testCanonical( - \\test "" { - \\ const s1 = - \\ \\one - \\ \\two) - \\ \\three - \\ ; - \\ const s3 = // hi - \\ \\one - \\ \\two) - \\ \\three - \\ ; + \\test "switch" { + \\ switch (0) { + \\ 0 => {}, + \\ 1 => unreachable, + \\ 2, 3 => {}, + \\ 4...7 => {}, + \\ 1 + 4 * 3 + 22 => {}, + \\ else => { + \\ const a = 1; + \\ const b = a; + \\ }, + \\ } + \\ + \\ const res = switch (0) { + \\ 0 => 0, + \\ 1 => 2, + \\ 1 => a = 4, + \\ else => 4, + \\ }; + \\ + \\ const Union = union(enum) { + \\ Int: i64, + \\ Float: f64, + \\ }; + \\ + \\ switch (u) { + \\ Union.Int => |int| {}, + \\ Union.Float => |*float| unreachable, + \\ 1 => |a, b| unreachable, + \\ 2 => |*a, b| unreachable, + \\ } \\} \\ ); -} -test "zig fmt: multiline string with CRLF line endings" { - try testTransform("" ++ - "const s =\r\n" ++ - " \\\\one\r\n" ++ - " \\\\two)\r\n" ++ - " \\\\three\r\n" ++ - ";\r\n", - \\const s = - \\ \\one - \\ \\two) - \\ \\three - \\; + try testTransform( + \\test { + \\ switch (x) { + \\ foo => + \\ "bar", + \\ } + \\} + \\ + , + \\test { + \\ switch (x) { + \\ foo => "bar", + \\ } + \\} \\ ); } -test "zig fmt: values" { +test "zig fmt: switch multiline string" { try testCanonical( - \\test "values" { - \\ 1; - \\ 1.0; - \\ "string"; - \\ 'c'; - \\ true; - \\ false; - \\ null; - \\ undefined; - \\ anyerror; - \\ this; - \\ unreachable; + \\test "switch multiline string" { + \\ const x: u32 = 0; + \\ const str = switch (x) { + \\ 1 => "one", + \\ 2 => + \\ \\ Comma after the multiline string + \\ \\ is needed + \\ , + \\ 3 => "three", + \\ else => "else", + \\ }; + \\ + \\ const Union = union(enum) { + \\ Int: i64, + \\ Float: f64, + \\ }; + \\ + \\ const str = switch (u) { + \\ Union.Int => |int| + \\ \\ Comma after the multiline string + \\ \\ is needed + \\ , + \\ Union.Float => |*float| unreachable, + \\ }; \\} \\ - ); -} - -test "zig fmt: indexing" { - try testCanonical( - \\test "test index" { - \\ a[0]; - \\ a[0 + 5]; - \\ a[0..]; - \\ a[0..5]; - \\ a[a[0]]; - \\ a[a[0..]]; - \\ a[a[0..5]]; - \\ a[a[0]..]; - \\ a[a[0..5]..]; - \\ a[a[0]..a[0]]; - \\ a[a[0..5]..a[0]]; - \\ a[a[0..5]..a[0..5]]; + ); +} + +test "zig fmt: while" { + try testCanonical( + \\test "while" { + \\ while (10 < 1) unreachable; + \\ + \\ while (10 < 1) unreachable else unreachable; + \\ + \\ while (10 < 1) { + \\ unreachable; + \\ } + \\ + \\ while (10 < 1) + \\ unreachable; + \\ + \\ var i: usize = 0; + \\ while (i < 10) : (i += 1) { + \\ continue; + \\ } + \\ + \\ i = 0; + \\ while (i < 10) : (i += 1) + \\ continue; + \\ + \\ i = 0; + \\ var j: usize = 0; + \\ while (i < 10) : ({ + \\ i += 1; + \\ j += 1; + \\ }) continue; + \\ + \\ while (i < 10) : ({ + \\ i += 1; + \\ j += 1; + \\ }) { + \\ continue; + \\ } + \\ + \\ var a: ?u8 = 2; + \\ while (a) |v| : (a = null) { + \\ continue; + \\ } + \\ + \\ while (a) |v| : (a = null) + \\ continue; + \\ + \\ while (a) |v| : (a = null) + \\ continue + \\ else + \\ unreachable; + \\ + \\ for (&[_]u8{}) |v| { + \\ continue; + \\ } + \\ + \\ while (a) |v| : (a = null) + \\ unreachable; + \\ + \\ label: while (10 < 0) { + \\ unreachable; + \\ } + \\ + \\ const res = while (0 < 10) { + \\ break 7; + \\ } else { + \\ unreachable; + \\ }; + \\ + \\ const res = while (0 < 10) + \\ break 7 + \\ else + \\ unreachable; + \\ + \\ var a: anyerror!u8 = 0; + \\ while (a) |v| { + \\ a = error.Err; + \\ } else |err| { + \\ i = 1; + \\ } + \\ + \\ comptime var k: usize = 0; + \\ inline while (i < 10) : (i += 1) + \\ j += 2; \\} \\ ); } -test "zig fmt: struct declaration" { +test "zig fmt: for" { try testCanonical( - \\const S = struct { - \\ const Self = @This(); - \\ f1: u8, - \\ f3: u8, + \\test "for" { + \\ for (a) |v| { + \\ continue; + \\ } \\ - \\ f2: u8, + \\ for (a) |v| continue; \\ - \\ fn method(self: *Self) Self { - \\ return self.*; + \\ for (a) |v| continue else return; + \\ + \\ for (a) |v| { + \\ continue; + \\ } else return; + \\ + \\ for (a) |v| continue else { + \\ return; \\ } - \\}; \\ - \\const Ps = packed struct { - \\ a: u8, - \\ b: u8, + \\ for (a) |v| + \\ continue + \\ else + \\ return; \\ - \\ c: u8, - \\}; + \\ for (a) |v| + \\ continue; \\ - \\const Ps = packed struct(u32) { - \\ a: u1, - \\ b: u2, + \\ for (a) |*v| + \\ continue; \\ - \\ c: u29, - \\}; + \\ for (a, 0..) |v, i| { + \\ continue; + \\ } \\ - \\const Es = extern struct { - \\ a: u8, - \\ b: u8, + \\ for (a, 0..) |v, i| + \\ continue; \\ - \\ c: u8, - \\}; + \\ for (a) |b| switch (b) { + \\ c => {}, + \\ d => {}, + \\ }; + \\ + \\ const res = for (a, 0..) |v, i| { + \\ break v; + \\ } else { + \\ unreachable; + \\ }; + \\ + \\ var num: usize = 0; + \\ inline for (a, 0..1) |v, i| { + \\ num += v; + \\ num += i; + \\ } + \\ + \\ for (a, b) | + \\ long_name, + \\ another_long_name, + \\ | { + \\ continue; + \\ } + \\} \\ ); -} -test "zig fmt: enum declaration" { - try testCanonical( - \\const E = enum { - \\ Ok, - \\ SomethingElse = 0, - \\}; + try testTransform( + \\test "fix for" { + \\ for (a) |x| + \\ f(x) else continue; + \\} \\ - \\const E2 = enum(u8) { - \\ Ok, - \\ SomethingElse = 255, - \\ SomethingThird, - \\}; + , + \\test "fix for" { + \\ for (a) |x| + \\ f(x) + \\ else + \\ continue; + \\} \\ - \\const Ee = extern enum { - \\ Ok, - \\ SomethingElse, - \\ SomethingThird, - \\}; + ); + + try testTransform( + \\test "fix for" { + \\ for (a, b, c,) |long, another, third,| {} + \\} \\ - \\const Ep = packed enum { - \\ Ok, - \\ SomethingElse, - \\ SomethingThird, - \\}; + , + \\test "fix for" { + \\ for ( + \\ a, + \\ b, + \\ c, + \\ ) | + \\ long, + \\ another, + \\ third, + \\ | {} + \\} \\ ); } -test "zig fmt: union declaration" { +test "zig fmt: for if" { try testCanonical( - \\const U = union { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; - \\ - \\const Ue = union(enum) { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; + \\test { + \\ for (a) |x| if (x) f(x); \\ - \\const E = enum { - \\ Int, - \\ Float, - \\ None, - \\ Bool, - \\}; + \\ for (a) |x| if (x) + \\ f(x); \\ - \\const Ue2 = union(E) { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; + \\ for (a) |x| if (x) { + \\ f(x); + \\ }; \\ - \\const Eu = extern union { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; + \\ for (a) |x| + \\ if (x) + \\ f(x); \\ - ); -} - -test "zig fmt: arrays" { - try testCanonical( - \\test "arrays" { - \\ const a: [2]u32 = .{ 1, 2 }; - \\ const b = a ++ a; - \\ const c = a[0..]; - \\ _ = c; + \\ for (a) |x| + \\ if (x) { + \\ f(x); + \\ }; \\} \\ ); } -test "zig fmt: container initializers" { +test "zig fmt: if for" { try testCanonical( - \\const a0 = []u8{}; - \\const a1 = []u8{1}; - \\const a2 = []u8{ - \\ 1, - \\ 2, - \\ 3, - \\ 4, - \\}; - \\const s0 = S{}; - \\const s1 = S{ .a = 1 }; - \\const s2 = S{ - \\ .a = 1, - \\ .b = 2, - \\}; + \\test { + \\ if (a) for (x) |x| f(x); \\ - ); -} - -test "zig fmt: catch" { - try testCanonical( - \\test "catch" { - \\ const a: anyerror!u8 = 0; - \\ _ = a catch return; - \\ _ = a catch - \\ return; - \\ _ = a catch |err| return; - \\ _ = a catch |err| - \\ return; + \\ if (a) for (x) |x| + \\ f(x); + \\ + \\ if (a) for (x) |x| { + \\ f(x); + \\ }; + \\ + \\ if (a) + \\ for (x) |x| + \\ f(x); + \\ + \\ if (a) + \\ for (x) |x| { + \\ f(x); + \\ }; \\} \\ ); } -test "zig fmt: blocks" { +test "zig fmt: while if" { try testCanonical( \\test { - \\ { - \\ const a = b; - \\ } - \\ const c = d; + \\ while (a) if (x) f(x); + \\ + \\ while (a) if (x) + \\ f(x); + \\ + \\ while (a) if (x) { + \\ f(x); + \\ }; + \\ + \\ while (a) + \\ if (x) + \\ f(x); + \\ + \\ while (a) + \\ if (x) { + \\ f(x); + \\ }; \\} \\ ); } -test "zig fmt: switch" { +test "zig fmt: if while" { try testCanonical( - \\test "switch" { - \\ switch (0) { - \\ 0 => {}, - \\ 1 => unreachable, - \\ 2, 3 => {}, - \\ 4...7 => {}, - \\ 1 + 4 * 3 + 22 => {}, - \\ else => { - \\ const a = 1; - \\ const b = a; - \\ }, - \\ } + \\test { + \\ if (a) while (x) : (cont) f(x); \\ - \\ const res = switch (0) { - \\ 0 => 0, - \\ 1 => 2, - \\ 1 => a = 4, - \\ else => 4, - \\ }; + \\ if (a) while (x) : (cont) + \\ f(x); \\ - \\ const Union = union(enum) { - \\ Int: i64, - \\ Float: f64, + \\ if (a) while (x) : (cont) { + \\ f(x); \\ }; \\ - \\ switch (u) { - \\ Union.Int => |int| {}, - \\ Union.Float => |*float| unreachable, - \\ 1 => |a, b| unreachable, - \\ 2 => |*a, b| unreachable, - \\ } + \\ if (a) + \\ while (x) : (cont) + \\ f(x); + \\ + \\ if (a) + \\ while (x) : (cont) { + \\ f(x); + \\ }; \\} \\ ); +} - try testTransform( +test "zig fmt: while for" { + try testCanonical( \\test { - \\ switch (x) { - \\ foo => - \\ "bar", - \\ } - \\} + \\ while (a) for (x) |x| f(x); \\ - , - \\test { - \\ switch (x) { - \\ foo => "bar", - \\ } + \\ while (a) for (x) |x| + \\ f(x); + \\ + \\ while (a) for (x) |x| { + \\ f(x); + \\ }; + \\ + \\ while (a) + \\ for (x) |x| + \\ f(x); + \\ + \\ while (a) + \\ for (x) |x| { + \\ f(x); + \\ }; \\} \\ ); } -test "zig fmt: switch multiline string" { +test "zig fmt: for while" { try testCanonical( - \\test "switch multiline string" { - \\ const x: u32 = 0; - \\ const str = switch (x) { - \\ 1 => "one", - \\ 2 => - \\ \\ Comma after the multiline string - \\ \\ is needed - \\ , - \\ 3 => "three", - \\ else => "else", - \\ }; + \\test { + \\ for (a) |a| while (x) |x| f(x); \\ - \\ const Union = union(enum) { - \\ Int: i64, - \\ Float: f64, - \\ }; + \\ for (a) |a| while (x) |x| + \\ f(x); \\ - \\ const str = switch (u) { - \\ Union.Int => |int| - \\ \\ Comma after the multiline string - \\ \\ is needed - \\ , - \\ Union.Float => |*float| unreachable, + \\ for (a) |a| while (x) |x| { + \\ f(x); \\ }; + \\ + \\ for (a) |a| + \\ while (x) |x| + \\ f(x); + \\ + \\ for (a) |a| + \\ while (x) |x| { + \\ f(x); + \\ }; \\} \\ ); } -test "zig fmt: while" { +test "zig fmt: if" { try testCanonical( - \\test "while" { - \\ while (10 < 1) unreachable; + \\test "if" { + \\ if (10 < 0) { + \\ unreachable; + \\ } \\ - \\ while (10 < 1) unreachable else unreachable; + \\ if (10 < 0) unreachable; \\ - \\ while (10 < 1) { + \\ if (10 < 0) { \\ unreachable; + \\ } else { + \\ const a = 20; \\ } \\ - \\ while (10 < 1) + \\ if (10 < 0) { \\ unreachable; - \\ - \\ var i: usize = 0; - \\ while (i < 10) : (i += 1) { - \\ continue; + \\ } else if (5 < 0) { + \\ unreachable; + \\ } else { + \\ const a = 20; \\ } \\ - \\ i = 0; - \\ while (i < 10) : (i += 1) - \\ continue; - \\ - \\ i = 0; - \\ var j: usize = 0; - \\ while (i < 10) : ({ - \\ i += 1; - \\ j += 1; - \\ }) continue; + \\ const is_world_broken = if (10 < 0) true else false; + \\ const some_number = 1 + if (10 < 0) 2 else 3; \\ - \\ while (i < 10) : ({ - \\ i += 1; - \\ j += 1; - \\ }) { - \\ continue; + \\ const a: ?u8 = 10; + \\ const b: ?u8 = null; + \\ if (a) |v| { + \\ const some = v; + \\ } else if (b) |*v| { + \\ unreachable; + \\ } else { + \\ const some = 10; \\ } \\ - \\ var a: ?u8 = 2; - \\ while (a) |v| : (a = null) { - \\ continue; + \\ const non_null_a = if (a) |v| v else 0; + \\ + \\ const a_err: anyerror!u8 = 0; + \\ if (a_err) |v| { + \\ const p = v; + \\ } else |err| { + \\ unreachable; \\ } + \\} \\ - \\ while (a) |v| : (a = null) - \\ continue; + ); +} + +test "zig fmt: fix single statement if/for/while line breaks" { + try testTransform( + \\test { + \\ if (cond) a + \\ else b; \\ - \\ while (a) |v| : (a = null) - \\ continue + \\ if (cond) + \\ a + \\ else b; + \\ + \\ for (xs) |x| foo() + \\ else bar(); + \\ + \\ for (xs) |x| + \\ foo() + \\ else bar(); + \\ + \\ while (a) : (b) foo() + \\ else bar(); + \\ + \\ while (a) : (b) + \\ foo() + \\ else bar(); + \\} + \\ + , + \\test { + \\ if (cond) a else b; + \\ + \\ if (cond) + \\ a \\ else - \\ unreachable; + \\ b; + \\ + \\ for (xs) |x| foo() else bar(); + \\ + \\ for (xs) |x| + \\ foo() + \\ else + \\ bar(); + \\ + \\ while (a) : (b) foo() else bar(); + \\ + \\ while (a) : (b) + \\ foo() + \\ else + \\ bar(); + \\} \\ - \\ for (&[_]u8{}) |v| { - \\ continue; + ); +} + +test "zig fmt: defer" { + try testCanonical( + \\test "defer" { + \\ defer foo(); + \\ defer { + \\ bar(); \\ } + \\} \\ - \\ while (a) |v| : (a = null) - \\ unreachable; - \\ - \\ label: while (10 < 0) { - \\ unreachable; + ); +} + +test "zig fmt: comptime" { + try testCanonical( + \\fn foo() void { + \\ comptime { + \\ bar(); \\ } + \\} \\ - \\ const res = while (0 < 10) { - \\ break 7; - \\ } else { - \\ unreachable; - \\ }; + ); +} + +test "zig fmt: fn type" { + try testCanonical( + \\fn a(i: u8) u8 { + \\ return i + 1; + \\} \\ - \\ const res = while (0 < 10) - \\ break 7 - \\ else - \\ unreachable; + \\const a: fn (u8) u8 = undefined; + \\const b: fn (u8) callconv(.naked) u8 = undefined; + \\const ap: fn (u8) u8 = a; \\ - \\ var a: anyerror!u8 = 0; - \\ while (a) |v| { - \\ a = error.Err; - \\ } else |err| { - \\ i = 1; - \\ } + ); +} + +test "zig fmt: inline asm" { + try testTransform( + \\pub fn syscall1(number: usize, arg1: usize) usize { + \\ return asm volatile ("syscall" + \\ : [ret] "={rax}" (-> usize), + \\ : [number] "{rax}" (number), + \\ [arg1] "{rdi}" (arg1), + \\ : "rcx", "r11" + \\ ); + \\} \\ - \\ comptime var k: usize = 0; - \\ inline while (i < 10) : (i += 1) - \\ j += 2; + , + \\pub fn syscall1(number: usize, arg1: usize) usize { + \\ return asm volatile ("syscall" + \\ : [ret] "={rax}" (-> usize), + \\ : [number] "{rax}" (number), + \\ [arg1] "{rdi}" (arg1), + \\ : .{ .rcx = true, .r11 = true } + \\ ); \\} \\ ); } -test "zig fmt: for" { +test "zig fmt: nosuspend" { try testCanonical( - \\test "for" { - \\ for (a) |v| { - \\ continue; + \\const a = nosuspend foo(); + \\ + ); +} + +test "zig fmt: Block after if" { + try testCanonical( + \\test { + \\ if (true) { + \\ const a = 0; \\ } \\ - \\ for (a) |v| continue; + \\ { + \\ const a = 0; + \\ } + \\} \\ - \\ for (a) |v| continue else return; + ); +} + +test "zig fmt: string identifier" { + try testCanonical( + \\const @"a b" = @"c d".@"e f"; + \\fn @"g h"() void {} \\ - \\ for (a) |v| { - \\ continue; - \\ } else return; + ); +} + +test "zig fmt: error return" { + try testCanonical( + \\fn err() anyerror { + \\ call(); + \\ return error.InvalidArgs; + \\} \\ - \\ for (a) |v| continue else { - \\ return; + ); +} + +test "zig fmt: comptime block in container" { + try testCanonical( + \\const Foo = struct { + \\ comptime { + \\ @compileLog("hello comptime"); \\ } + \\}; \\ - \\ for (a) |v| - \\ continue - \\ else - \\ return; + ); +} + +test "zig fmt: inline asm parameter alignment" { + try testCanonical( + \\pub fn main() void { + \\ asm volatile ( + \\ \\ foo + \\ \\ bar + \\ ); + \\ asm volatile ( + \\ \\ foo + \\ \\ bar + \\ : [_] "" (-> usize), + \\ [_] "" (-> usize), + \\ ); + \\ asm volatile ( + \\ \\ foo + \\ \\ bar + \\ : + \\ : [_] "" (0), + \\ [_] "" (0), + \\ ); + \\ asm volatile ( + \\ \\ foo + \\ \\ bar + \\ ::: .{ .a = true, .b = true }); + \\ asm volatile ( + \\ \\ foo + \\ \\ bar + \\ : [_] "" (-> usize), + \\ [_] "" (-> usize), + \\ : [_] "" (0), + \\ [_] "" (0), + \\ : .{}); + \\} \\ - \\ for (a) |v| - \\ continue; + ); +} + +test "zig fmt: multiline string in array" { + try testCanonical( + \\const Foo = [][]const u8{ + \\ \\aaa + \\ , + \\ \\bbb + \\}; \\ - \\ for (a) |*v| - \\ continue; + \\fn bar() void { + \\ const Foo = [][]const u8{ + \\ \\aaa + \\ , + \\ \\bbb + \\ }; + \\ const Bar = [][]const u8{ // comment here + \\ \\aaa + \\ \\ + \\ , // and another comment can go here + \\ \\bbb + \\ }; + \\} \\ - \\ for (a, 0..) |v, i| { - \\ continue; - \\ } + ); +} + +test "zig fmt: file ends with struct field" { + try testCanonical( + \\a: bool \\ - \\ for (a, 0..) |v, i| - \\ continue; + ); +} + +test "zig fmt: comment after empty comment" { + try testCanonical( + \\// + \\/// A doc comment + \\const a = b; \\ - \\ for (a) |b| switch (b) { - \\ c => {}, - \\ d => {}, + ); +} + +test "zig fmt: line comment in array" { + try testTransform( + \\test "a" { + \\ var arr = [_]u32{ + \\ 0 + \\ // 1, + \\ // 2, \\ }; + \\} \\ - \\ const res = for (a, 0..) |v, i| { - \\ break v; - \\ } else { - \\ unreachable; + , + \\test "a" { + \\ var arr = [_]u32{ + \\ 0, + \\ // 1, + \\ // 2, \\ }; + \\} \\ - \\ var num: usize = 0; - \\ inline for (a, 0..1) |v, i| { - \\ num += v; - \\ num += i; - \\ } - \\ - \\ for (a, b) | - \\ long_name, - \\ another_long_name, - \\ | { - \\ continue; - \\ } + ); + try testCanonical( + \\test "a" { + \\ var arr = [_]u32{ + \\ 0, + \\ // 1, + \\ // 2, + \\ }; \\} \\ ); +} - try testTransform( - \\test "fix for" { - \\ for (a) |x| - \\ f(x) else continue; - \\} +test "zig fmt: comment after params" { + try testCanonical( + \\fn foo( + \\ a: i32, // comment + \\ b: i32, // comment + \\) void {} \\ - , - \\test "fix for" { - \\ for (a) |x| - \\ f(x) - \\ else - \\ continue; + ); +} + +test "zig fmt: comment in array initializer/access" { + try testCanonical( + \\test "a" { + \\ var a = x{ //aa + \\ //bb + \\ }; + \\ var a = []x{ //aa + \\ //bb + \\ }; + \\ var b = [ //aa + \\ _ + \\ ]x{ //aa + \\ //bb + \\ 9, + \\ }; + \\ var c = b[ //aa + \\ 0 + \\ ]; + \\ var d = [ + \\ _ + \\ //aa + \\ : + \\ 0 + \\ ]x{ //aa + \\ //bb + \\ 9, + \\ }; + \\ var e = d[ + \\ 0 + \\ //aa + \\ ]; \\} \\ ); +} +test "zig fmt: comments at several places in struct init" { try testTransform( - \\test "fix for" { - \\ for (a, b, c,) |long, another, third,| {} - \\} + \\var bar = Bar{ + \\ .x = 10, // test + \\ .y = "test" + \\ // test + \\}; \\ , - \\test "fix for" { - \\ for ( - \\ a, - \\ b, - \\ c, - \\ ) | - \\ long, - \\ another, - \\ third, - \\ | {} - \\} + \\var bar = Bar{ + \\ .x = 10, // test + \\ .y = "test", + \\ // test + \\}; + \\ + ); + + try testCanonical( + \\var bar = Bar{ // test + \\ .x = 10, // test + \\ .y = "test", + \\ // test + \\}; \\ ); } -test "zig fmt: for if" { +test "zig fmt: container doc comments" { try testCanonical( - \\test { - \\ for (a) |x| if (x) f(x); + \\//! tld 1 + \\//! tld 2 + \\//! tld 3 + \\const a = b; \\ - \\ for (a) |x| if (x) - \\ f(x); + ); +} + +test "zig fmt: remove newlines surrounding doc comment" { + try testTransform( \\ - \\ for (a) |x| if (x) { - \\ f(x); - \\ }; \\ - \\ for (a) |x| - \\ if (x) - \\ f(x); \\ - \\ for (a) |x| - \\ if (x) { - \\ f(x); - \\ }; - \\} + \\/// doc comment + \\ + \\fn foo() void {} + \\ + , + \\/// doc comment + \\fn foo() void {} \\ ); } -test "zig fmt: if for" { - try testCanonical( - \\test { - \\ if (a) for (x) |x| f(x); +test "zig fmt: remove newlines surrounding doc comment between members" { + try testTransform( + \\f1: i32, \\ - \\ if (a) for (x) |x| - \\ f(x); \\ - \\ if (a) for (x) |x| { - \\ f(x); - \\ }; + \\/// doc comment \\ - \\ if (a) - \\ for (x) |x| - \\ f(x); + \\f2: i32, \\ - \\ if (a) - \\ for (x) |x| { - \\ f(x); - \\ }; - \\} + , + \\f1: i32, + \\ + \\/// doc comment + \\f2: i32, \\ ); } -test "zig fmt: while if" { - try testCanonical( - \\test { - \\ while (a) if (x) f(x); +test "zig fmt: remove newlines surrounding doc comment between members within container decl (1)" { + try testTransform( + \\const Foo = struct { + \\ fn foo() void {} \\ - \\ while (a) if (x) - \\ f(x); \\ - \\ while (a) if (x) { - \\ f(x); - \\ }; + \\ /// doc comment \\ - \\ while (a) - \\ if (x) - \\ f(x); \\ - \\ while (a) - \\ if (x) { - \\ f(x); - \\ }; - \\} + \\ fn bar() void {} + \\}; + \\ + , + \\const Foo = struct { + \\ fn foo() void {} + \\ + \\ /// doc comment + \\ fn bar() void {} + \\}; \\ ); } -test "zig fmt: if while" { - try testCanonical( - \\test { - \\ if (a) while (x) : (cont) f(x); - \\ - \\ if (a) while (x) : (cont) - \\ f(x); +test "zig fmt: remove newlines surrounding doc comment between members within container decl (2)" { + try testTransform( + \\const Foo = struct { + \\ fn foo() void {} + \\ /// doc comment 1 \\ - \\ if (a) while (x) : (cont) { - \\ f(x); - \\ }; + \\ /// doc comment 2 \\ - \\ if (a) - \\ while (x) : (cont) - \\ f(x); + \\ fn bar() void {} + \\}; \\ - \\ if (a) - \\ while (x) : (cont) { - \\ f(x); - \\ }; - \\} + , + \\const Foo = struct { + \\ fn foo() void {} + \\ /// doc comment 1 + \\ /// doc comment 2 + \\ fn bar() void {} + \\}; \\ ); } -test "zig fmt: while for" { - try testCanonical( - \\test { - \\ while (a) for (x) |x| f(x); +test "zig fmt: remove newlines surrounding doc comment within container decl" { + try testTransform( + \\const Foo = struct { \\ - \\ while (a) for (x) |x| - \\ f(x); \\ - \\ while (a) for (x) |x| { - \\ f(x); - \\ }; + \\ /// doc comment \\ - \\ while (a) - \\ for (x) |x| - \\ f(x); + \\ fn foo() void {} + \\}; \\ - \\ while (a) - \\ for (x) |x| { - \\ f(x); - \\ }; - \\} + , + \\const Foo = struct { + \\ /// doc comment + \\ fn foo() void {} + \\}; + \\ + ); +} + +test "zig fmt: comments with CRLF line endings" { + try testTransform("" ++ + "//! Top-level doc comment\r\n" ++ + "//! Continuing to another line\r\n" ++ + "\r\n" ++ + "/// Regular doc comment\r\n" ++ + "const S = struct {\r\n" ++ + " // Regular comment\r\n" ++ + " // More content\r\n" ++ + "};\r\n", + \\//! Top-level doc comment + \\//! Continuing to another line + \\ + \\/// Regular doc comment + \\const S = struct { + \\ // Regular comment + \\ // More content + \\}; \\ ); } -test "zig fmt: for while" { +test "zig fmt: else comptime expr" { try testCanonical( - \\test { - \\ for (a) |a| while (x) |x| f(x); - \\ - \\ for (a) |a| while (x) |x| - \\ f(x); - \\ - \\ for (a) |a| while (x) |x| { - \\ f(x); - \\ }; - \\ - \\ for (a) |a| - \\ while (x) |x| - \\ f(x); - \\ - \\ for (a) |a| - \\ while (x) |x| { - \\ f(x); - \\ }; + \\comptime { + \\ if (true) {} else comptime foo(); + \\} + \\comptime { + \\ while (true) {} else comptime foo(); + \\} + \\comptime { + \\ for ("") |_| {} else comptime foo(); \\} \\ ); } -test "zig fmt: if" { - try testCanonical( - \\test "if" { - \\ if (10 < 0) { - \\ unreachable; - \\ } - \\ - \\ if (10 < 0) unreachable; - \\ - \\ if (10 < 0) { - \\ unreachable; - \\ } else { - \\ const a = 20; - \\ } - \\ - \\ if (10 < 0) { - \\ unreachable; - \\ } else if (5 < 0) { - \\ unreachable; - \\ } else { - \\ const a = 20; - \\ } - \\ - \\ const is_world_broken = if (10 < 0) true else false; - \\ const some_number = 1 + if (10 < 0) 2 else 3; +test "zig fmt: integer literals with underscore separators" { + try testTransform( + \\const + \\ x = + \\ 1_234_567 + \\ + (0b0_1-0o7_0+0xff_FF ) + 1_0; + , + \\const x = + \\ 1_234_567 + (0b0_1 - 0o7_0 + 0xff_FF) + 1_0; \\ - \\ const a: ?u8 = 10; - \\ const b: ?u8 = null; - \\ if (a) |v| { - \\ const some = v; - \\ } else if (b) |*v| { - \\ unreachable; - \\ } else { - \\ const some = 10; + ); +} + +test "zig fmt: hex literals with underscore separators" { + try testTransform( + \\pub fn orMask(a: [ 1_000 ]u64, b: [ 1_000] u64) [1_000]u64 { + \\ var c: [1_000]u64 = [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000; + \\ for (c [ 1_0 .. ], 0..) |_, i| { + \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA; \\ } + \\ return c; + \\} \\ - \\ const non_null_a = if (a) |v| v else 0; \\ - \\ const a_err: anyerror!u8 = 0; - \\ if (a_err) |v| { - \\ const p = v; - \\ } else |err| { - \\ unreachable; + , + \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 { + \\ var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000; + \\ for (c[1_0..], 0..) |_, i| { + \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA; \\ } + \\ return c; \\} \\ ); } -test "zig fmt: fix single statement if/for/while line breaks" { - try testTransform( - \\test { - \\ if (cond) a - \\ else b; - \\ - \\ if (cond) - \\ a - \\ else b; - \\ - \\ for (xs) |x| foo() - \\ else bar(); - \\ - \\ for (xs) |x| - \\ foo() - \\ else bar(); - \\ - \\ while (a) : (b) foo() - \\ else bar(); +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; \\ - \\ while (a) : (b) - \\ foo() - \\ else bar(); + ); +} + +test "zig fmt: hexadecimal float literals with underscore separators" { + try testTransform( + \\pub fn main() void { + \\ const a: f64 = (0x10.0p-0+(0x10.0p+0))+0x10_00.00_00p-8+0x00_00.00_10p+16; + \\ const b: f64 = 0x0010.0--0x00_10.0+0x10.00+0x1p4; + \\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b }); \\} - \\ , - \\test { - \\ if (cond) a else b; - \\ - \\ if (cond) - \\ a - \\ else - \\ b; - \\ - \\ for (xs) |x| foo() else bar(); - \\ - \\ for (xs) |x| - \\ foo() - \\ else - \\ bar(); + \\pub fn main() void { + \\ const a: f64 = (0x10.0p-0 + (0x10.0p+0)) + 0x10_00.00_00p-8 + 0x00_00.00_10p+16; + \\ const b: f64 = 0x0010.0 - -0x00_10.0 + 0x10.00 + 0x1p4; + \\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b }); + \\} \\ - \\ while (a) : (b) foo() else bar(); + ); +} + +test "zig fmt: C var args" { + try testCanonical( + \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; \\ - \\ while (a) : (b) - \\ foo() - \\ else - \\ bar(); + ); +} + +test "zig fmt: Only indent multiline string literals in function calls" { + try testCanonical( + \\test "zig fmt:" { + \\ try testTransform( + \\ \\const X = struct { + \\ \\ foo: i32, bar: i8 }; + \\ , + \\ \\const X = struct { + \\ \\ foo: i32, bar: i8 + \\ \\}; + \\ \\ + \\ ); \\} \\ ); } -test "zig fmt: defer" { +test "zig fmt: Don't add extra newline after if" { try testCanonical( - \\test "defer" { - \\ defer foo(); - \\ defer { - \\ bar(); + \\pub fn atomicSymLink(allocator: Allocator, existing_path: []const u8, new_path: []const u8) !void { + \\ if (cwd().symLink(existing_path, new_path, .{})) { + \\ return; \\ } \\} \\ ); } -test "zig fmt: comptime" { +test "zig fmt: comments in ternary ifs" { try testCanonical( - \\fn foo() void { - \\ comptime { - \\ bar(); - \\ } - \\} + \\const x = if (true) { + \\ 1; + \\} else if (false) + \\ // Comment + \\ 0; + \\const y = if (true) + \\ // Comment + \\ 1 + \\else + \\ // Comment + \\ 0; + \\ + \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; \\ ); } -test "zig fmt: fn type" { +test "zig fmt: while statement in blockless if" { try testCanonical( - \\fn a(i: u8) u8 { - \\ return i + 1; + \\pub fn main() void { + \\ const zoom_node = if (focused_node == layout_first) + \\ while (it.next()) |node| { + \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node; + \\ } else null + \\ else + \\ focused_node; \\} \\ - \\const a: fn (u8) u8 = undefined; - \\const b: fn (u8) callconv(.naked) u8 = undefined; - \\const ap: fn (u8) u8 = a; + ); +} + +test "zig fmt: test comments in field access chain" { + try testCanonical( + \\pub const str = struct { + \\ pub const Thing = more.more // + \\ .more() // + \\ .more().more() // + \\ .more() // + \\ // .more() // + \\ .more() // + \\ .more(); + \\ data: Data, + \\}; + \\ + \\pub const str = struct { + \\ pub const Thing = more.more // + \\ .more() // + \\ // .more() // + \\ // .more() // + \\ // .more() // + \\ .more() // + \\ .more(); + \\ data: Data, + \\}; + \\ + \\pub const str = struct { + \\ pub const Thing = more // + \\ .more // + \\ .more() // + \\ .more(); + \\ data: Data, + \\}; \\ ); } -test "zig fmt: inline asm" { - try testTransform( - \\pub fn syscall1(number: usize, arg1: usize) usize { - \\ return asm volatile ("syscall" - \\ : [ret] "={rax}" (-> usize), - \\ : [number] "{rax}" (number), - \\ [arg1] "{rdi}" (arg1), - \\ : "rcx", "r11" - \\ ); - \\} +test "zig fmt: allow line break before field access" { + try testCanonical( + \\test { + \\ const w = foo.bar().zippy(zag).iguessthisisok(); \\ - , - \\pub fn syscall1(number: usize, arg1: usize) usize { - \\ return asm volatile ("syscall" - \\ : [ret] "={rax}" (-> usize), - \\ : [number] "{rax}" (number), - \\ [arg1] "{rdi}" (arg1), - \\ : .{ .rcx = true, .r11 = true } - \\ ); + \\ const x = foo + \\ .bar() + \\ . // comment + \\ // comment + \\ swooop().zippy(zag) + \\ .iguessthisisok(); + \\ + \\ const y = view.output.root.server.input_manager.default_seat.wlr_seat.name; + \\ + \\ const z = view.output.root.server + \\ .input_manager // + \\ .default_seat + \\ . // comment + \\ // another comment + \\ wlr_seat.name; \\} \\ ); -} - -test "zig fmt: nosuspend" { - try testCanonical( - \\const a = nosuspend foo(); + try testTransform( + \\test { + \\ const x = foo. + \\ bar() + \\ .zippy(zag).iguessthisisok(); \\ - ); -} - -test "zig fmt: Block after if" { - try testCanonical( + \\ const z = view.output.root.server. + \\ input_manager. + \\ default_seat.wlr_seat.name; + \\} + \\ + , \\test { - \\ if (true) { - \\ const a = 0; - \\ } + \\ const x = foo + \\ .bar() + \\ .zippy(zag).iguessthisisok(); \\ - \\ { - \\ const a = 0; - \\ } + \\ const z = view.output.root.server + \\ .input_manager + \\ .default_seat.wlr_seat.name; \\} \\ ); } -test "zig fmt: string identifier" { +test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" { try testCanonical( - \\const @"a b" = @"c d".@"e f"; - \\fn @"g h"() void {} + \\fn foo() void { + \\ z.display_message_dialog( + \\ *const [323:0]u8, + \\ \\Message Text + \\ \\------------ + \\ \\xxxxxxxxxxxx + \\ \\xxxxxxxxxxxx + \\ , + \\ g.GtkMessageType.GTK_MESSAGE_WARNING, + \\ null, + \\ ); + \\ + \\ z.display_message_dialog(*const [323:0]u8, + \\ \\Message Text + \\ \\------------ + \\ \\xxxxxxxxxxxx + \\ \\xxxxxxxxxxxx + \\ , g.GtkMessageType.GTK_MESSAGE_WARNING, null); + \\} \\ ); } -test "zig fmt: error return" { +test "zig fmt: regression test for #5722" { try testCanonical( - \\fn err() anyerror { - \\ call(); - \\ return error.InvalidArgs; + \\pub fn sendViewTags(self: Self) void { + \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32)); + \\ while (it.next()) |node| + \\ view_tags.append(node.view.current_tags) catch { + \\ c.wl_resource_post_no_memory(self.wl_resource); + \\ log.err(.river_status, "out of memory", .{}); + \\ return; + \\ }; \\} \\ ); } -test "zig fmt: comptime block in container" { +test "zig fmt: regression test for #8974" { try testCanonical( - \\const Foo = struct { - \\ comptime { - \\ @compileLog("hello comptime"); - \\ } - \\}; + \\pub const VARIABLE; \\ ); } -test "zig fmt: inline asm parameter alignment" { +test "zig fmt: allow trailing line comments to do manual array formatting" { try testCanonical( - \\pub fn main() void { - \\ asm volatile ( - \\ \\ foo - \\ \\ bar - \\ ); - \\ asm volatile ( - \\ \\ foo - \\ \\ bar - \\ : [_] "" (-> usize), - \\ [_] "" (-> usize), - \\ ); - \\ asm volatile ( - \\ \\ foo - \\ \\ bar - \\ : - \\ : [_] "" (0), - \\ [_] "" (0), - \\ ); - \\ asm volatile ( - \\ \\ foo - \\ \\ bar - \\ ::: .{ .a = true, .b = true }); - \\ asm volatile ( - \\ \\ foo - \\ \\ bar - \\ : [_] "" (-> usize), - \\ [_] "" (-> usize), - \\ : [_] "" (0), - \\ [_] "" (0), - \\ : .{}); + \\fn foo() void { + \\ self.code.appendSliceAssumeCapacity(&[_]u8{ + \\ 0x55, // push rbp + \\ 0x48, 0x89, 0xe5, // mov rbp, rsp + \\ 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc) + \\ }); + \\ + \\ di_buf.appendAssumeCapacity(&[_]u8{ + \\ 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header + \\ DW.AT_stmt_list, DW_FORM_data4, // form value pairs + \\ DW.AT_low_pc, DW_FORM_addr, + \\ DW.AT_high_pc, DW_FORM_addr, + \\ DW.AT_name, DW_FORM_strp, + \\ DW.AT_comp_dir, DW_FORM_strp, + \\ DW.AT_producer, DW_FORM_strp, + \\ DW.AT_language, DW_FORM_data2, + \\ 0, 0, // sentinel + \\ }); + \\ + \\ self.code.appendSliceAssumeCapacity(&[_]u8{ + \\ 0x55, // push rbp + \\ 0x48, 0x89, 0xe5, // mov rbp, rsp + \\ // How do we handle this? + \\ //0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc) + \\ // Here's a blank line, should that be allowed? + \\ + \\ 0x48, 0x89, 0xe5, + \\ 0x33, 0x45, + \\ // Now the comment breaks a single line -- how do we handle this? + \\ 0x88, + \\ }); \\} \\ ); } -test "zig fmt: multiline string in array" { +test "zig fmt: multiline string literals should play nice with array initializers" { try testCanonical( - \\const Foo = [][]const u8{ - \\ \\aaa - \\ , - \\ \\bbb - \\}; - \\ - \\fn bar() void { - \\ const Foo = [][]const u8{ - \\ \\aaa + \\fn main() void { + \\ var a = .{.{.{.{.{.{.{.{ + \\ 0, + \\ }}}}}}}}; + \\ myFunc(.{ + \\ "aaaaaaa", "bbbbbb", "ccccc", + \\ "dddd", ("eee"), ("fff"), + \\ ("gggg"), + \\ // Line comment + \\ \\Multiline String Literals can be quite long \\ , - \\ \\bbb - \\ }; - \\ const Bar = [][]const u8{ // comment here - \\ \\aaa - \\ \\ - \\ , // and another comment can go here - \\ \\bbb - \\ }; + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ , + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ , + \\ ( + \\ \\Multiline String Literals can be quite long + \\ ), + \\ .{ + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ }, + \\ .{( + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ )}, + \\ .{ + \\ "xxxxxxx", "xxx", + \\ ( + \\ \\ xxx + \\ ), + \\ "xxx", + \\ "xxx", + \\ }, + \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, + \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, + \\ "aaaaaaa", "bbbbbb", "ccccc", // - + \\ "dddd", ("eee"), ("fff"), + \\ .{ + \\ "xxx", "xxx", + \\ ( + \\ \\ xxx + \\ ), + \\ "xxxxxxxxxxxxxx", + \\ "xxx", + \\ }, + \\ .{ + \\ ( + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ ), + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ }, + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ }); \\} \\ ); } -test "zig fmt: file ends with struct field" { +test "zig fmt: use of comments and multiline string literals may force the parameters over multiple lines" { try testCanonical( - \\a: bool + \\pub fn makeMemUndefined(qzz: []u8) i1 { + \\ cases.add( // fixed bug foo + \\ "compile diagnostic string for top level decl type", + \\ \\export fn entry() void { + \\ \\ var foo: u32 = @This(){}; + \\ \\} + \\ , &[_][]const u8{ + \\ "tmp.zig:2:27: error: type 'u32' does not support array initialization", + \\ }); + \\ @compileError( + \\ \\ unknown-length pointers and C pointers cannot be hashed deeply. + \\ \\ Consider providing your own hash function. + \\ \\ unknown-length pointers and C pointers cannot be hashed deeply. + \\ \\ Consider providing your own hash function. + \\ ); + \\ return @intCast(doMemCheckClientRequestExpr(0, // default return + \\ .MakeMemUndefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0)); + \\} + \\ + \\// This looks like garbage don't do this + \\const rparen = tree.prevToken( + \\ // the first token for the annotation expressions is the left + \\ // parenthesis, hence the need for two prevToken + \\ if (fn_proto.getAlignExpr()) |align_expr| + \\ tree.prevToken(tree.prevToken(align_expr.firstToken())) + \\ else if (fn_proto.getSectionExpr()) |section_expr| + \\ tree.prevToken(tree.prevToken(section_expr.firstToken())) + \\ else if (fn_proto.getCallconvExpr()) |callconv_expr| + \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken())) + \\ else switch (fn_proto.return_type) { + \\ .Explicit => |node| node.firstToken(), + \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()), + \\ .Invalid => unreachable, + \\ }); \\ ); } -test "zig fmt: comment after empty comment" { +test "zig fmt: single argument trailing commas in @builtins()" { try testCanonical( - \\// - \\/// A doc comment - \\const a = b; + \\pub fn foo(qzz: []u8) i1 { + \\ @panic( + \\ foo, + \\ ); + \\ panic( + \\ foo, + \\ ); + \\ @panic( + \\ foo, + \\ bar, + \\ ); + \\} \\ ); } -test "zig fmt: line comment in array" { +test "zig fmt: trailing comma should force multiline 1 column" { try testTransform( - \\test "a" { - \\ var arr = [_]u32{ - \\ 0 - \\ // 1, - \\ // 2, - \\ }; - \\} + \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,}; \\ , - \\test "a" { - \\ var arr = [_]u32{ - \\ 0, - \\ // 1, - \\ // 2, - \\ }; - \\} + \\pub const UUID_NULL: uuid_t = [16]u8{ + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\}; \\ ); +} + +test "zig fmt: function params should align nicely" { try testCanonical( - \\test "a" { - \\ var arr = [_]u32{ - \\ 0, - \\ // 1, - \\ // 2, - \\ }; + \\pub fn foo() void { + \\ cases.addRuntimeSafety("slicing operator with sentinel", + \\ \\const std = @import("std"); + \\ ++ check_panic_msg ++ + \\ \\pub fn main() void { + \\ \\ var buf = [4]u8{'a','b','c',0}; + \\ \\ const slice = buf[0..:0]; + \\ \\} + \\ ); \\} \\ ); } -test "zig fmt: comment after params" { +test "zig fmt: fn proto end with anytype and comma" { try testCanonical( - \\fn foo( - \\ a: i32, // comment - \\ b: i32, // comment - \\) void {} + \\pub fn format( + \\ out_stream: anytype, + \\) !void {} \\ ); } -test "zig fmt: comment in array initializer/access" { +test "zig fmt: space after top level doc comment" { try testCanonical( - \\test "a" { - \\ var a = x{ //aa - \\ //bb - \\ }; - \\ var a = []x{ //aa - \\ //bb - \\ }; - \\ var b = [ //aa - \\ _ - \\ ]x{ //aa - \\ //bb - \\ 9, - \\ }; - \\ var c = b[ //aa - \\ 0 - \\ ]; - \\ var d = [ - \\ _ - \\ //aa - \\ : - \\ 0 - \\ ]x{ //aa - \\ //bb - \\ 9, - \\ }; - \\ var e = d[ - \\ 0 - \\ //aa - \\ ]; - \\} + \\//! top level doc comment + \\ + \\field: i32, \\ ); } -test "zig fmt: comments at several places in struct init" { +test "zig fmt: remove trailing whitespace after container doc comment" { try testTransform( - \\var bar = Bar{ - \\ .x = 10, // test - \\ .y = "test" - \\ // test - \\}; + \\//! top level doc comment \\ , - \\var bar = Bar{ - \\ .x = 10, // test - \\ .y = "test", - \\ // test - \\}; - \\ - ); - - try testCanonical( - \\var bar = Bar{ // test - \\ .x = 10, // test - \\ .y = "test", - \\ // test - \\}; - \\ - ); -} - -test "zig fmt: container doc comments" { - try testCanonical( - \\//! tld 1 - \\//! tld 2 - \\//! tld 3 - \\const a = b; + \\//! top level doc comment \\ ); } -test "zig fmt: remove newlines surrounding doc comment" { +test "zig fmt: remove trailing whitespace after doc comment" { try testTransform( - \\ - \\ - \\ - \\/// doc comment - \\ - \\fn foo() void {} + \\/// doc comment + \\a = 0, \\ , \\/// doc comment - \\fn foo() void {} + \\a = 0, \\ ); } -test "zig fmt: remove newlines surrounding doc comment between members" { - try testTransform( - \\f1: i32, - \\ - \\ - \\/// doc comment - \\ - \\f2: i32, - \\ - , - \\f1: i32, - \\ - \\/// doc comment - \\f2: i32, +test "zig fmt: for loop with ptr payload and index" { + try testCanonical( + \\test { + \\ for (self.entries.items, 0..) |*item, i| {} + \\ for (self.entries.items, 0..) |*item, i| + \\ a = b; + \\ for (self.entries.items, 0..) |*item, i| a = b; + \\} \\ ); } -test "zig fmt: remove newlines surrounding doc comment between members within container decl (1)" { - try testTransform( - \\const Foo = struct { - \\ fn foo() void {} - \\ - \\ - \\ /// doc comment - \\ - \\ - \\ fn bar() void {} - \\}; - \\ - , - \\const Foo = struct { - \\ fn foo() void {} +test "zig fmt: proper indent line comment after multi-line single expr while loop" { + try testCanonical( + \\test { + \\ while (a) : (b) + \\ foo(); \\ - \\ /// doc comment - \\ fn bar() void {} - \\}; + \\ // bar + \\ baz(); + \\} \\ ); } -test "zig fmt: remove newlines surrounding doc comment between members within container decl (2)" { - try testTransform( - \\const Foo = struct { - \\ fn foo() void {} - \\ /// doc comment 1 - \\ - \\ /// doc comment 2 - \\ - \\ fn bar() void {} - \\}; - \\ - , - \\const Foo = struct { - \\ fn foo() void {} - \\ /// doc comment 1 - \\ /// doc comment 2 - \\ fn bar() void {} - \\}; +test "zig fmt: extern function with missing param name" { + try testCanonical( + \\extern fn a( + \\ *b, + \\ c: *d, + \\) e; + \\extern fn f(*g, h: *i) j; \\ ); } -test "zig fmt: remove newlines surrounding doc comment within container decl" { - try testTransform( - \\const Foo = struct { - \\ - \\ - \\ /// doc comment - \\ - \\ fn foo() void {} - \\}; +test "zig fmt: respect extra newline between switch items" { + try testCanonical( + \\const a = switch (b) { + \\ .c => {}, \\ - , - \\const Foo = struct { - \\ /// doc comment - \\ fn foo() void {} + \\ .d, + \\ .e, + \\ => f, \\}; \\ ); } -test "zig fmt: comments with CRLF line endings" { - try testTransform("" ++ - "//! Top-level doc comment\r\n" ++ - "//! Continuing to another line\r\n" ++ - "\r\n" ++ - "/// Regular doc comment\r\n" ++ - "const S = struct {\r\n" ++ - " // Regular comment\r\n" ++ - " // More content\r\n" ++ - "};\r\n", - \\//! Top-level doc comment - \\//! Continuing to another line - \\ - \\/// Regular doc comment - \\const S = struct { - \\ // Regular comment - \\ // More content - \\}; +test "zig fmt: assignment with inline for and inline while" { + try testCanonical( + \\const tmp = inline for (items) |item| {}; \\ ); -} -test "zig fmt: else comptime expr" { try testCanonical( - \\comptime { - \\ if (true) {} else comptime foo(); - \\} - \\comptime { - \\ while (true) {} else comptime foo(); - \\} - \\comptime { - \\ for ("") |_| {} else comptime foo(); - \\} + \\const tmp2 = inline while (true) {}; \\ ); } -test "zig fmt: integer literals with underscore separators" { - try testTransform( - \\const - \\ x = - \\ 1_234_567 - \\ + (0b0_1-0o7_0+0xff_FF ) + 1_0; - , - \\const x = - \\ 1_234_567 + (0b0_1 - 0o7_0 + 0xff_FF) + 1_0; +test "zig fmt: saturating arithmetic" { + try testCanonical( + \\test { + \\ const actual = switch (op) { + \\ .add => a +| b, + \\ .sub => a -| b, + \\ .mul => a *| b, + \\ .shl => a <<| b, + \\ }; + \\ switch (op) { + \\ .add => actual +|= b, + \\ .sub => actual -|= b, + \\ .mul => actual *|= b, + \\ .shl => actual <<|= b, + \\ } + \\} \\ ); } -test "zig fmt: hex literals with underscore separators" { +test "zig fmt: insert trailing comma if there are comments between switch values" { try testTransform( - \\pub fn orMask(a: [ 1_000 ]u64, b: [ 1_000] u64) [1_000]u64 { - \\ var c: [1_000]u64 = [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000; - \\ for (c [ 1_0 .. ], 0..) |_, i| { - \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA; - \\ } - \\ return c; - \\} + \\const a = switch (b) { + \\ .c => {}, + \\ + \\ .d, // foobar + \\ .e + \\ => f, \\ + \\ .g, .h + \\ // comment + \\ => i, + \\}; \\ , - \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 { - \\ var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000; - \\ for (c[1_0..], 0..) |_, i| { - \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA; - \\ } - \\ return c; - \\} + \\const a = switch (b) { + \\ .c => {}, + \\ + \\ .d, // foobar + \\ .e, + \\ => f, + \\ + \\ .g, + \\ .h, + \\ // comment + \\ => i, + \\}; \\ ); } -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: insert trailing comma if comments in array init" { + try testTransform( + \\var a = .{ + \\ "foo", // + \\ "bar" + \\}; + \\var a = .{ + \\ "foo", + \\ "bar" // + \\}; + \\var a = .{ + \\ "foo", + \\ "//" + \\}; + \\var a = .{ + \\ "foo", + \\ "//" // + \\}; + \\ + , + \\var a = .{ + \\ "foo", // + \\ "bar", + \\}; + \\var a = .{ + \\ "foo", + \\ "bar", // + \\}; + \\var a = .{ "foo", "//" }; + \\var a = .{ + \\ "foo", + \\ "//", // + \\}; \\ ); } -test "zig fmt: hexadecimal float literals with underscore separators" { +test "zig fmt: make single-line if no trailing comma" { try testTransform( - \\pub fn main() void { - \\ const a: f64 = (0x10.0p-0+(0x10.0p+0))+0x10_00.00_00p-8+0x00_00.00_10p+16; - \\ const b: f64 = 0x0010.0--0x00_10.0+0x10.00+0x1p4; - \\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b }); + \\test "function call no trailing comma" { + \\ foo( + \\ 1, + \\ 2 + \\ ); \\} + \\ , - \\pub fn main() void { - \\ const a: f64 = (0x10.0p-0 + (0x10.0p+0)) + 0x10_00.00_00p-8 + 0x00_00.00_10p+16; - \\ const b: f64 = 0x0010.0 - -0x00_10.0 + 0x10.00 + 0x1p4; - \\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b }); + \\test "function call no trailing comma" { + \\ foo(1, 2); \\} \\ ); -} -test "zig fmt: C var args" { - try testCanonical( - \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; + try testTransform( + \\test "struct no trailing comma" { + \\ const a = .{ + \\ .foo = 1, + \\ .bar = 2 + \\ }; + \\} \\ - ); -} - -test "zig fmt: Only indent multiline string literals in function calls" { - try testCanonical( - \\test "zig fmt:" { - \\ try testTransform( - \\ \\const X = struct { - \\ \\ foo: i32, bar: i8 }; - \\ , - \\ \\const X = struct { - \\ \\ foo: i32, bar: i8 - \\ \\}; - \\ \\ - \\ ); + , + \\test "struct no trailing comma" { + \\ const a = .{ .foo = 1, .bar = 2 }; \\} \\ ); -} -test "zig fmt: Don't add extra newline after if" { - try testCanonical( - \\pub fn atomicSymLink(allocator: Allocator, existing_path: []const u8, new_path: []const u8) !void { - \\ if (cwd().symLink(existing_path, new_path, .{})) { - \\ return; - \\ } + try testTransform( + \\test "array no trailing comma" { + \\ var stream = multiOutStream(.{ + \\ fbs1.outStream(), + \\ fbs2.outStream() + \\ }); + \\} + \\ + , + \\test "array no trailing comma" { + \\ var stream = multiOutStream(.{ fbs1.outStream(), fbs2.outStream() }); \\} \\ ); } -test "zig fmt: comments in ternary ifs" { - try testCanonical( - \\const x = if (true) { - \\ 1; - \\} else if (false) - \\ // Comment - \\ 0; - \\const y = if (true) - \\ // Comment - \\ 1 - \\else - \\ // Comment - \\ 0; +test "zig fmt: preserve container doc comment in container without trailing comma" { + try testTransform( + \\const A = enum(u32) { + \\//! comment + \\_ }; \\ - \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; + , + \\const A = enum(u32) { + \\ //! comment + \\ _, + \\}; \\ ); } -test "zig fmt: while statement in blockless if" { - try testCanonical( - \\pub fn main() void { - \\ const zoom_node = if (focused_node == layout_first) - \\ while (it.next()) |node| { - \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node; - \\ } else null - \\ else - \\ focused_node; +test "zig fmt: no space before newline before multiline string" { + try testCanonical( + \\const S = struct { + \\ text: []const u8, + \\ comment: []const u8, + \\}; + \\ + \\test { + \\ const s1 = .{ + \\ .text = + \\ \\hello + \\ \\world + \\ , + \\ .comment = "test", + \\ }; + \\ _ = s1; + \\ const s2 = .{ + \\ .comment = "test", + \\ .text = + \\ \\hello + \\ \\world + \\ , + \\ }; + \\ _ = s2; \\} \\ ); } -test "zig fmt: test comments in field access chain" { - try testCanonical( - \\pub const str = struct { - \\ pub const Thing = more.more // - \\ .more() // - \\ .more().more() // - \\ .more() // - \\ // .more() // - \\ .more() // - \\ .more(); - \\ data: Data, +test "zig fmt: don't canonicalize _ in enums" { + try testTransform( + \\const A = enum { + \\ first, + \\ second, + \\ third, + \\ _, \\}; - \\ - \\pub const str = struct { - \\ pub const Thing = more.more // - \\ .more() // - \\ // .more() // - \\ // .more() // - \\ // .more() // - \\ .more() // - \\ .more(); - \\ data: Data, + \\const B = enum { + \\ @"_", + \\ @"__", + \\ @"___", + \\ @"____", + \\}; + \\const C = struct { + \\ @"_": u8, + \\ @"__": u8, + \\ @"___": u8, + \\ @"____": u8, + \\}; + \\const D = union { + \\ @"_": u8, + \\ @"__": u8, + \\ @"___": u8, + \\ @"____": u8, \\}; \\ - \\pub const str = struct { - \\ pub const Thing = more // - \\ .more // - \\ .more() // - \\ .more(); - \\ data: Data, + , + \\const A = enum { + \\ first, + \\ second, + \\ third, + \\ _, + \\}; + \\const B = enum { + \\ @"_", + \\ __, + \\ ___, + \\ ____, + \\}; + \\const C = struct { + \\ _: u8, + \\ __: u8, + \\ ___: u8, + \\ ____: u8, + \\}; + \\const D = union { + \\ _: u8, + \\ __: u8, + \\ ___: u8, + \\ ____: u8, \\}; \\ ); } -test "zig fmt: allow line break before field access" { +test "zig fmt: pointer type syntax to index" { try testCanonical( \\test { - \\ const w = foo.bar().zippy(zag).iguessthisisok(); - \\ - \\ const x = foo - \\ .bar() - \\ . // comment - \\ // comment - \\ swooop().zippy(zag) - \\ .iguessthisisok(); - \\ - \\ const y = view.output.root.server.input_manager.default_seat.wlr_seat.name; - \\ - \\ const z = view.output.root.server - \\ .input_manager // - \\ .default_seat - \\ . // comment - \\ // another comment - \\ wlr_seat.name; + \\ _ = .{}[*0]; \\} \\ ); - try testTransform( - \\test { - \\ const x = foo. - \\ bar() - \\ .zippy(zag).iguessthisisok(); - \\ - \\ const z = view.output.root.server. - \\ input_manager. - \\ default_seat.wlr_seat.name; - \\} - \\ - , +} + +test "zig fmt: binop indentation in if statement" { + try testCanonical( \\test { - \\ const x = foo - \\ .bar() - \\ .zippy(zag).iguessthisisok(); - \\ - \\ const z = view.output.root.server - \\ .input_manager - \\ .default_seat.wlr_seat.name; + \\ if (first_param_type.isGenericPoison() or + \\ (first_param_type.zigTypeTag(zcu) == .pointer and + \\ (first_param_type.ptrSize(zcu) == .One or + \\ first_param_type.ptrSize(zcu) == .C) and + \\ first_param_type.childType(zcu).eql(concrete_ty, zcu))) + \\ { + \\ f(x); + \\ } \\} \\ ); } -test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" { +test "zig fmt: test indentation of if expressions" { try testCanonical( - \\fn foo() void { - \\ z.display_message_dialog( - \\ *const [323:0]u8, - \\ \\Message Text - \\ \\------------ - \\ \\xxxxxxxxxxxx - \\ \\xxxxxxxxxxxx - \\ , - \\ g.GtkMessageType.GTK_MESSAGE_WARNING, - \\ null, - \\ ); + \\test { + \\ const foo = 1 + + \\ if (1 == 2) + \\ 2 + \\ else + \\ 0; \\ - \\ z.display_message_dialog(*const [323:0]u8, - \\ \\Message Text - \\ \\------------ - \\ \\xxxxxxxxxxxx - \\ \\xxxxxxxxxxxx - \\ , g.GtkMessageType.GTK_MESSAGE_WARNING, null); + \\ const foo = 1 + if (1 == 2) + \\ 2 + \\ else + \\ 0; + \\ + \\ errval catch |e| + \\ if (e == error.Meow) + \\ return 0x1F408 + \\ else + \\ unreachable; + \\ + \\ errval catch |e| if (e == error.Meow) + \\ return 0x1F408 + \\ else + \\ unreachable; + \\ + \\ return if (1 == 2) + \\ 1 + \\ else if (3 > 4) + \\ 2 + \\ else + \\ 0; \\} \\ ); } +test "Ast header smoke test" { + try std.testing.expectEqual(zigNode(c.AST_NODE_IF), Ast.Node.Tag.@"if"); +} + +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); +} + +const Error = std.zig.Ast.Error.Tag; -test "zig fmt: regression test for #5722" { - try testCanonical( - \\pub fn sendViewTags(self: Self) void { - \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32)); - \\ while (it.next()) |node| - \\ view_tags.append(node.view.current_tags) catch { - \\ c.wl_resource_post_no_memory(self.wl_resource); - \\ log.err(.river_status, "out of memory", .{}); - \\ return; - \\ }; - \\} - \\ - ); -} +fn testError(source: [:0]const u8, expected_errors: []const Error) !void { + var tree = try std.zig.Ast.parse(std.testing.allocator, source, .zig); + defer tree.deinit(std.testing.allocator); -test "zig fmt: regression test for #8974" { - try testCanonical( - \\pub const VARIABLE; - \\ - ); + std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| { + std.debug.print("errors found: {any}\n", .{tree.errors}); + return err; + }; + for (expected_errors, 0..) |expected, i| { + try std.testing.expectEqual(expected, tree.errors[i].tag); + } } -test "zig fmt: allow trailing line comments to do manual array formatting" { - try testCanonical( - \\fn foo() void { - \\ self.code.appendSliceAssumeCapacity(&[_]u8{ - \\ 0x55, // push rbp - \\ 0x48, 0x89, 0xe5, // mov rbp, rsp - \\ 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc) - \\ }); - \\ - \\ di_buf.appendAssumeCapacity(&[_]u8{ - \\ 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header - \\ DW.AT_stmt_list, DW_FORM_data4, // form value pairs - \\ DW.AT_low_pc, DW_FORM_addr, - \\ DW.AT_high_pc, DW_FORM_addr, - \\ DW.AT_name, DW_FORM_strp, - \\ DW.AT_comp_dir, DW_FORM_strp, - \\ DW.AT_producer, DW_FORM_strp, - \\ DW.AT_language, DW_FORM_data2, - \\ 0, 0, // sentinel - \\ }); - \\ - \\ self.code.appendSliceAssumeCapacity(&[_]u8{ - \\ 0x55, // push rbp - \\ 0x48, 0x89, 0xe5, // mov rbp, rsp - \\ // How do we handle this? - \\ //0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc) - \\ // Here's a blank line, should that be allowed? - \\ - \\ 0x48, 0x89, 0xe5, - \\ 0x33, 0x45, - \\ // Now the comment breaks a single line -- how do we handle this? - \\ 0x88, - \\ }); - \\} - \\ - ); -} +const testing = std.testing; -test "zig fmt: multiline string literals should play nice with array initializers" { - try testCanonical( - \\fn main() void { - \\ var a = .{.{.{.{.{.{.{.{ - \\ 0, - \\ }}}}}}}}; - \\ myFunc(.{ - \\ "aaaaaaa", "bbbbbb", "ccccc", - \\ "dddd", ("eee"), ("fff"), - \\ ("gggg"), - \\ // Line comment - \\ \\Multiline String Literals can be quite long - \\ , - \\ \\Multiline String Literals can be quite long - \\ \\Multiline String Literals can be quite long - \\ , - \\ \\Multiline String Literals can be quite long - \\ \\Multiline String Literals can be quite long - \\ \\Multiline String Literals can be quite long - \\ \\Multiline String Literals can be quite long - \\ , - \\ ( - \\ \\Multiline String Literals can be quite long - \\ ), - \\ .{ - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ }, - \\ .{( - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ )}, - \\ .{ - \\ "xxxxxxx", "xxx", - \\ ( - \\ \\ xxx - \\ ), - \\ "xxx", - \\ "xxx", - \\ }, - \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, - \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, - \\ "aaaaaaa", "bbbbbb", "ccccc", // - - \\ "dddd", ("eee"), ("fff"), - \\ .{ - \\ "xxx", "xxx", - \\ ( - \\ \\ xxx - \\ ), - \\ "xxxxxxxxxxxxxx", - \\ "xxx", - \\ }, - \\ .{ - \\ ( - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ ), - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ }, - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - \\ }); - \\} - \\ - ); +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_LEGACY => .asm_legacy, + 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, + }; } -test "zig fmt: use of comments and multiline string literals may force the parameters over multiple lines" { - try testCanonical( - \\pub fn makeMemUndefined(qzz: []u8) i1 { - \\ cases.add( // fixed bug foo - \\ "compile diagnostic string for top level decl type", - \\ \\export fn entry() void { - \\ \\ var foo: u32 = @This(){}; - \\ \\} - \\ , &[_][]const u8{ - \\ "tmp.zig:2:27: error: type 'u32' does not support array initialization", - \\ }); - \\ @compileError( - \\ \\ unknown-length pointers and C pointers cannot be hashed deeply. - \\ \\ Consider providing your own hash function. - \\ \\ unknown-length pointers and C pointers cannot be hashed deeply. - \\ \\ Consider providing your own hash function. - \\ ); - \\ return @intCast(doMemCheckClientRequestExpr(0, // default return - \\ .MakeMemUndefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0)); - \\} - \\ - \\// This looks like garbage don't do this - \\const rparen = tree.prevToken( - \\ // the first token for the annotation expressions is the left - \\ // parenthesis, hence the need for two prevToken - \\ if (fn_proto.getAlignExpr()) |align_expr| - \\ tree.prevToken(tree.prevToken(align_expr.firstToken())) - \\ else if (fn_proto.getSectionExpr()) |section_expr| - \\ tree.prevToken(tree.prevToken(section_expr.firstToken())) - \\ else if (fn_proto.getCallconvExpr()) |callconv_expr| - \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken())) - \\ else switch (fn_proto.return_type) { - \\ .Explicit => |node| node.firstToken(), - \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()), - \\ .Invalid => unreachable, - \\ }); - \\ - ); +fn toIndex(v: u32) Ast.Node.Index { + return @enumFromInt(v); } -test "zig fmt: single argument trailing commas in @builtins()" { - try testCanonical( - \\pub fn foo(qzz: []u8) i1 { - \\ @panic( - \\ foo, - \\ ); - \\ panic( - \\ foo, - \\ ); - \\ @panic( - \\ foo, - \\ bar, - \\ ); - \\} - \\ - ); +fn toOptIndex(v: u32) Ast.Node.OptionalIndex { + return if (v == 0) .none else @enumFromInt(v); } -test "zig fmt: trailing comma should force multiline 1 column" { - try testTransform( - \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,}; - \\ - , - \\pub const UUID_NULL: uuid_t = [16]u8{ - \\ 0, - \\ 0, - \\ 0, - \\ 0, - \\}; - \\ - ); +fn toExtraIndex(v: u32) Ast.ExtraIndex { + return @enumFromInt(v); } -test "zig fmt: function params should align nicely" { - try testCanonical( - \\pub fn foo() void { - \\ cases.addRuntimeSafety("slicing operator with sentinel", - \\ \\const std = @import("std"); - \\ ++ check_panic_msg ++ - \\ \\pub fn main() void { - \\ \\ var buf = [4]u8{'a','b','c',0}; - \\ \\ const slice = buf[0..:0]; - \\ \\} - \\ ); - \\} - \\ - ); +fn toOptTokenIndex(v: u32) Ast.OptionalTokenIndex { + return @enumFromInt(v); } -test "zig fmt: fn proto end with anytype and comma" { - try testCanonical( - \\pub fn format( - \\ out_stream: anytype, - \\) !void {} - \\ - ); -} +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) } }, -test "zig fmt: space after top level doc comment" { - try testCanonical( - \\//! top level doc comment - \\ - \\field: i32, - \\ - ); -} + // .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) }, -test "zig fmt: remove trailing whitespace after container doc comment" { - try testTransform( - \\//! top level doc comment - \\ - , - \\//! top level doc comment - \\ - ); -} + // .opt_node (single optional node) + .@"return", + => .{ .opt_node = toOptIndex(lhs) }, -test "zig fmt: remove trailing whitespace after doc comment" { - try testTransform( - \\/// doc comment - \\a = 0, - \\ - , - \\/// doc comment - \\a = 0, - \\ - ); -} + // .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) } }, -test "zig fmt: for loop with ptr payload and index" { - try testCanonical( - \\test { - \\ for (self.entries.items, 0..) |*item, i| {} - \\ for (self.entries.items, 0..) |*item, i| - \\ a = b; - \\ for (self.entries.items, 0..) |*item, i| a = b; - \\} - \\ - ); -} + // .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) } }, -test "zig fmt: proper indent line comment after multi-line single expr while loop" { - try testCanonical( - \\test { - \\ while (a) : (b) - \\ foo(); - \\ - \\ // bar - \\ baz(); - \\} - \\ - ); -} + // .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) } }, -test "zig fmt: extern function with missing param name" { - try testCanonical( - \\extern fn a( - \\ *b, - \\ c: *d, - \\) e; - \\extern fn f(*g, h: *i) j; - \\ - ); -} + // .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) } }, -test "zig fmt: respect extra newline between switch items" { - try testCanonical( - \\const a = switch (b) { - \\ .c => {}, - \\ - \\ .d, - \\ .e, - \\ => f, - \\}; - \\ - ); -} + // .extra_and_node + .assign_destructure, + .switch_case, + .switch_case_inline, + .ptr_type, + .ptr_type_bit_range, + => .{ .extra_and_node = .{ toExtraIndex(lhs), toIndex(rhs) } }, -test "zig fmt: assignment with inline for and inline while" { - try testCanonical( - \\const tmp = inline for (items) |item| {}; - \\ - ); + // .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) } }, - try testCanonical( - \\const tmp2 = inline while (true) {}; - \\ - ); -} + // .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) } }, -test "zig fmt: saturating arithmetic" { - try testCanonical( - \\test { - \\ const actual = switch (op) { - \\ .add => a +| b, - \\ .sub => a -| b, - \\ .mul => a *| b, - \\ .shl => a <<| b, - \\ }; - \\ switch (op) { - \\ .add => actual +|= b, - \\ .sub => actual -|= b, - \\ .mul => actual *|= b, - \\ .shl => actual <<|= b, - \\ } - \\} - \\ - ); -} + // .node_and_token + .grouped_expression, + .asm_input, + .asm_simple, + .field_access, + .unwrap_optional, + => .{ .node_and_token = .{ toIndex(lhs), rhs } }, -test "zig fmt: insert trailing comma if there are comments between switch values" { - try testTransform( - \\const a = switch (b) { - \\ .c => {}, - \\ - \\ .d, // foobar - \\ .e - \\ => f, - \\ - \\ .g, .h - \\ // comment - \\ => i, - \\}; - \\ - , - \\const a = switch (b) { - \\ .c => {}, - \\ - \\ .d, // foobar - \\ .e, - \\ => f, - \\ - \\ .g, - \\ .h, - \\ // comment - \\ => i, - \\}; - \\ - ); -} + // .opt_node_and_token + .asm_output, + => .{ .opt_node_and_token = .{ toOptIndex(lhs), rhs } }, -test "zig fmt: insert trailing comma if comments in array init" { - try testTransform( - \\var a = .{ - \\ "foo", // - \\ "bar" - \\}; - \\var a = .{ - \\ "foo", - \\ "bar" // - \\}; - \\var a = .{ - \\ "foo", - \\ "//" - \\}; - \\var a = .{ - \\ "foo", - \\ "//" // - \\}; - \\ - , - \\var a = .{ - \\ "foo", // - \\ "bar", - \\}; - \\var a = .{ - \\ "foo", - \\ "bar", // - \\}; - \\var a = .{ "foo", "//" }; - \\var a = .{ - \\ "foo", - \\ "//", // - \\}; - \\ - ); -} + // .opt_token_and_node + .test_decl, + .@"errdefer", + => .{ .opt_token_and_node = .{ toOptTokenIndex(lhs), toIndex(rhs) } }, -test "zig fmt: make single-line if no trailing comma" { - try testTransform( - \\test "function call no trailing comma" { - \\ foo( - \\ 1, - \\ 2 - \\ ); - \\} - \\ - , - \\test "function call no trailing comma" { - \\ foo(1, 2); - \\} - \\ - ); + // .opt_token_and_opt_node + .@"break", + .@"continue", + => .{ .opt_token_and_opt_node = .{ toOptTokenIndex(lhs), toOptIndex(rhs) } }, - try testTransform( - \\test "struct no trailing comma" { - \\ const a = .{ - \\ .foo = 1, - \\ .bar = 2 - \\ }; - \\} - \\ - , - \\test "struct no trailing comma" { - \\ const a = .{ .foo = 1, .bar = 2 }; - \\} - \\ - ); + // .token_and_token + .error_set_decl, + .multiline_string_literal, + => .{ .token_and_token = .{ lhs, rhs } }, - try testTransform( - \\test "array no trailing comma" { - \\ var stream = multiOutStream(.{ - \\ fbs1.outStream(), - \\ fbs2.outStream() - \\ }); - \\} - \\ - , - \\test "array no trailing comma" { - \\ var stream = multiOutStream(.{ fbs1.outStream(), fbs2.outStream() }); - \\} - \\ - ); -} + // .token_and_node + .anyframe_type, + => .{ .token_and_node = .{ lhs, toIndex(rhs) } }, -test "zig fmt: preserve container doc comment in container without trailing comma" { - try testTransform( - \\const A = enum(u32) { - \\//! comment - \\_ }; - \\ - , - \\const A = enum(u32) { - \\ //! comment - \\ _, - \\}; - \\ - ); -} + // .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) } }, -test "zig fmt: no space before newline before multiline string" { - try testCanonical( - \\const S = struct { - \\ text: []const u8, - \\ comment: []const u8, - \\}; - \\ - \\test { - \\ const s1 = .{ - \\ .text = - \\ \\hello - \\ \\world - \\ , - \\ .comment = "test", - \\ }; - \\ _ = s1; - \\ const s2 = .{ - \\ .comment = "test", - \\ .text = - \\ \\hello - \\ \\world - \\ , - \\ }; - \\ _ = s2; - \\} - \\ - ); -} + .while_cont, + .@"while", + .@"if", + => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, -test "zig fmt: don't canonicalize _ in enums" { - try testTransform( - \\const A = enum { - \\ first, - \\ second, - \\ third, - \\ _, - \\}; - \\const B = enum { - \\ @"_", - \\ @"__", - \\ @"___", - \\ @"____", - \\}; - \\const C = struct { - \\ @"_": u8, - \\ @"__": u8, - \\ @"___": u8, - \\ @"____": u8, - \\}; - \\const D = union { - \\ @"_": u8, - \\ @"__": u8, - \\ @"___": u8, - \\ @"____": u8, - \\}; - \\ - , - \\const A = enum { - \\ first, - \\ second, - \\ third, - \\ _, - \\}; - \\const B = enum { - \\ @"_", - \\ __, - \\ ___, - \\ ____, - \\}; - \\const C = struct { - \\ _: u8, - \\ __: u8, - \\ ___: u8, - \\ ____: u8, - \\}; - \\const D = union { - \\ _: u8, - \\ __: u8, - \\ ___: u8, - \\ ____: u8, - \\}; - \\ - ); -} + .for_range, + => .{ .node_and_opt_node = .{ toIndex(lhs), toOptIndex(rhs) } }, -test "zig fmt: pointer type syntax to index" { - try testCanonical( - \\test { - \\ _ = .{}[*0]; - \\} - \\ - ); -} + .@"for", + => .{ .@"for" = .{ toExtraIndex(lhs), @bitCast(rhs) } }, -test "zig fmt: binop indentation in if statement" { - try testCanonical( - \\test { - \\ if (first_param_type.isGenericPoison() or - \\ (first_param_type.zigTypeTag(zcu) == .pointer and - \\ (first_param_type.ptrSize(zcu) == .One or - \\ first_param_type.ptrSize(zcu) == .C) and - \\ first_param_type.childType(zcu).eql(concrete_ty, zcu))) - \\ { - \\ f(x); - \\ } - \\} - \\ - ); + .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); -test "zig fmt: test indentation of if expressions" { - try testCanonical( - \\test { - \\ const foo = 1 + - \\ if (1 == 2) - \\ 2 - \\ else - \\ 0; - \\ - \\ const foo = 1 + if (1 == 2) - \\ 2 - \\ else - \\ 0; - \\ - \\ errval catch |e| - \\ if (e == error.Meow) - \\ return 0x1F408 - \\ else - \\ unreachable; - \\ - \\ errval catch |e| if (e == error.Meow) - \\ return 0x1F408 - \\ else - \\ unreachable; - \\ - \\ return if (1 == 2) - \\ 1 - \\ else if (3 > 4) - \\ 2 - \\ else - \\ 0; - \\} - \\ - ); -} + 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); -test "Ast header smoke test" { - try std.testing.expectEqual(zigNode(c.AST_NODE_IF), Ast.Node.Tag.@"if"); -} + 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), + }); + } -test "my function" { - try testCanonical( - \\pub fn main() void { - \\ @panic("hello"); - \\} - \\ - ); + 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, + }; } +// copy-past from parser_test.zig