Files
zig/parser_test.zig
Motiejus Jakštys ca3738bc3e parser: port asm, comment, doc comment tests
Port tests:
- "inline asm"
- "inline asm parameter alignment"
- "multiline string in array"
- "file ends with struct field"
- "line comment in array"
- "comment in array initializer/access"
- "comments at several places in struct init"
- "remove newlines surrounding doc comment"
- "remove newlines surrounding doc comment between members"
- "fix single statement if/for/while line breaks"
- "fn type"
- "nosuspend"
- "Block after if"
- "string identifier"
- "error return"

Add if/switch support in parsePrimaryTypeExpr.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-11 14:40:24 +00:00

4585 lines
107 KiB
Zig

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);
}
test "zig fmt: remove extra whitespace at start and end of file with comment between" {
try testTransform(
\\
\\
\\// hello
\\
\\
,
\\// hello
\\
);
}
test "zig fmt: tuple struct" {
try testCanonical(
\\const T = struct {
\\ /// doc comment on tuple field
\\ comptime comptime u32,
\\ /// another doc comment on tuple field
\\ *u32 = 1,
\\ // needs to be wrapped in parentheses to not be parsed as a function decl
\\ (fn () void) align(1),
\\};
\\
);
}
test "zig fmt: preserves clobbers in inline asm with stray comma" {
try testTransform(
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : "clobber"
\\ );
\\ asm volatile (""
\\ :
\\ : [_] "" (type),
\\ : "clobber"
\\ );
\\}
\\
,
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : .{ .clobber = true }
\\ );
\\ asm volatile (""
\\ :
\\ : [_] "" (type),
\\ : .{ .clobber = true }
\\ );
\\}
\\
);
}
test "zig fmt: remove trailing comma at the end of assembly clobber" {
try testTransform(
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : "clobber1", "clobber2",
\\ );
\\}
\\
,
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : .{ .clobber1 = true, .clobber2 = true }
\\ );
\\}
\\
);
}
test "zig fmt: respect line breaks in struct field value declaration" {
try testCanonical(
\\const Foo = struct {
\\ bar: u32 =
\\ 42,
\\ bar: u32 =
\\ // a comment
\\ 42,
\\ bar: u32 =
\\ 42,
\\ // a comment
\\ bar: []const u8 =
\\ \\ foo
\\ \\ bar
\\ \\ baz
\\ ,
\\ bar: u32 =
\\ blk: {
\\ break :blk 42;
\\ },
\\};
\\
);
}
test "zig fmt: respect line breaks before functions" {
try testCanonical(
\\const std = @import("std");
\\
\\inline fn foo() void {}
\\
\\noinline fn foo() void {}
\\
\\export fn foo() void {}
\\
\\extern fn foo() void;
\\
\\extern "foo" fn foo() void;
\\
);
}
test "zig fmt: rewrite callconv(.@\"inline\") to the inline keyword" {
try testTransform(
\\fn foo() callconv(.@"inline") void {}
\\const bar: @import("std").builtin.CallingConvention = .@"inline";
\\fn foo() callconv(bar) void {}
\\
,
\\inline fn foo() void {}
\\const bar: @import("std").builtin.CallingConvention = .@"inline";
\\fn foo() callconv(bar) void {}
\\
);
}
test "zig fmt: simple top level comptime block" {
try testCanonical(
\\// line comment
\\comptime {}
\\
);
}
test "zig fmt: two spaced line comments before decl" {
try testCanonical(
\\// line comment
\\
\\// another
\\comptime {}
\\
);
}
test "zig fmt: respect line breaks after var declarations" {
try testCanonical(
\\const crc =
\\ lookup_tables[0][p[7]] ^
\\ lookup_tables[1][p[6]] ^
\\ lookup_tables[2][p[5]] ^
\\ lookup_tables[3][p[4]] ^
\\ lookup_tables[4][@as(u8, self.crc >> 24)] ^
\\ lookup_tables[5][@as(u8, self.crc >> 16)] ^
\\ lookup_tables[6][@as(u8, self.crc >> 8)] ^
\\ lookup_tables[7][@as(u8, self.crc >> 0)];
\\
);
}
test "zig fmt: multiline string mixed with comments" {
try testCanonical(
\\const s1 =
\\ //\\one
\\ \\two)
\\ \\three
\\;
\\const s2 =
\\ \\one
\\ \\two)
\\ //\\three
\\;
\\const s3 =
\\ \\one
\\ //\\two)
\\ \\three
\\;
\\const s4 =
\\ \\one
\\ //\\two
\\ \\three
\\ //\\four
\\ \\five
\\;
\\const a =
\\ 1;
\\
);
}
test "zig fmt: empty file" {
try testCanonical(
\\
);
}
test "zig fmt: file ends in comment" {
try testTransform(
\\ //foobar
,
\\//foobar
\\
);
}
test "zig fmt: file ends in multi line comment" {
try testTransform(
\\ \\foobar
,
\\\\foobar
\\
);
}
test "zig fmt: file ends in comment after var decl" {
try testTransform(
\\const x = 42;
\\ //foobar
,
\\const x = 42;
\\//foobar
\\
);
}
test "zig fmt: if statement" {
try testCanonical(
\\test "" {
\\ if (optional()) |some|
\\ bar = some.foo();
\\}
\\
);
}
test "zig fmt: top-level fields" {
try testCanonical(
\\a: did_you_know,
\\b: all_files_are,
\\structs: ?x,
\\
);
}
test "zig fmt: top-level tuple function call type" {
try testCanonical(
\\foo()
\\
);
}
test "zig fmt: top-level for/while loop" {
try testCanonical(
\\for (foo) |_| foo
\\
);
try testCanonical(
\\while (foo) |_| foo
\\
);
}
test "zig fmt: top-level bare asterisk+identifier" {
try testCanonical(
\\*x
\\
);
}
test "zig fmt: top-level bare asterisk+asterisk+identifier" {
try testCanonical(
\\**x
\\
);
}
test "zig fmt: errdefer with payload" {
try testCanonical(
\\pub fn main() anyerror!void {
\\ errdefer |a| x += 1;
\\ errdefer |a| {}
\\ errdefer |a| {
\\ x += 1;
\\ }
\\}
\\
);
}
test "zig fmt: nosuspend block" {
try testCanonical(
\\pub fn main() anyerror!void {
\\ nosuspend {
\\ var foo: Foo = .{ .bar = 42 };
\\ }
\\}
\\
);
}
test "zig fmt: container declaration, single line" {
try testCanonical(
\\const X = struct { foo: i32 };
\\const X = struct { foo: i32, bar: i32 };
\\const X = struct { foo: i32 = 1, bar: i32 = 2 };
\\const X = struct { foo: i32 align(4), bar: i32 align(4) };
\\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };
\\
);
}
test "zig fmt: container declaration, one item, multi line trailing comma" {
try testCanonical(
\\test "" {
\\ comptime {
\\ const X = struct {
\\ x: i32,
\\ };
\\ }
\\}
\\
);
}
test "zig fmt: container declaration, no trailing comma on separate line" {
try testTransform(
\\test "" {
\\ comptime {
\\ const X = struct {
\\ x: i32
\\ };
\\ }
\\}
\\
,
\\test "" {
\\ comptime {
\\ const X = struct { x: i32 };
\\ }
\\}
\\
);
}
test "zig fmt: container declaration, line break, no trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: i32, bar: i8 };
,
\\const X = struct { foo: i32, bar: i8 };
\\
);
}
test "zig fmt: container declaration, transform trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: i32, bar: i8, };
,
\\const X = struct {
\\ foo: i32,
\\ bar: i8,
\\};
\\
);
}
test "zig fmt: container declaration, comment, add trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: i32, // foo
\\ bar: i8
\\};
,
\\const X = struct {
\\ foo: i32, // foo
\\ bar: i8,
\\};
\\
);
try testTransform(
\\const X = struct {
\\ foo: i32 // foo
\\};
,
\\const X = struct {
\\ foo: i32, // foo
\\};
\\
);
}
test "zig fmt: container declaration, multiline string, add trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: []const u8 =
\\ \\ foo
\\ ,
\\ bar: i8
\\};
,
\\const X = struct {
\\ foo: []const u8 =
\\ \\ foo
\\ ,
\\ bar: i8,
\\};
\\
);
}
test "zig fmt: container declaration, doc comment on member, add trailing comma" {
try testTransform(
\\pub const Pos = struct {
\\ /// X-axis.
\\ x: u32,
\\ /// Y-axis.
\\ y: u32
\\};
,
\\pub const Pos = struct {
\\ /// X-axis.
\\ x: u32,
\\ /// Y-axis.
\\ y: u32,
\\};
\\
);
}
test "zig fmt: remove empty lines at start/end of container decl" {
try testTransform(
\\const X = struct {
\\
\\ foo: i32,
\\
\\ bar: i8,
\\
\\};
\\
,
\\const X = struct {
\\ foo: i32,
\\
\\ bar: i8,
\\};
\\
);
}
test "zig fmt: remove empty lines at start/end of block" {
try testTransform(
\\test {
\\
\\ if (foo) {
\\ foo();
\\ }
\\
\\}
\\
,
\\test {
\\ if (foo) {
\\ foo();
\\ }
\\}
\\
);
}
test "zig fmt: allow empty line before comment at start of block" {
try testCanonical(
\\test {
\\
\\ // foo
\\ const x = 42;
\\}
\\
);
}
test "zig fmt: trailing comma in fn parameter list" {
try testCanonical(
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) align(8) i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) addrspace(.generic) i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) linksection(".text") i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) callconv(.c) i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) align(8) linksection(".text") i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) align(8) callconv(.c) i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) align(8) linksection(".text") callconv(.c) i32 {}
\\pub fn f(
\\ a: i32,
\\ b: i32,
\\) linksection(".text") callconv(.c) i32 {}
\\
);
}
test "zig fmt: comptime struct field" {
try testCanonical(
\\const Foo = struct {
\\ a: i32,
\\ comptime b: i32 = 1234,
\\};
\\
);
}
test "zig fmt: break from block" {
try testCanonical(
\\const a = blk: {
\\ break :blk 42;
\\};
\\const b = blk: {
\\ break :blk;
\\};
\\const c = {
\\ break 42;
\\};
\\const d = {
\\ break;
\\};
\\
);
}
test "zig fmt: grouped expressions (parentheses)" {
try testCanonical(
\\const r = (x + y) * (a + b);
\\
);
}
test "zig fmt: c pointer type" {
try testCanonical(
\\pub extern fn repro() [*c]const u8;
\\
);
}
test "zig fmt: builtin call with trailing comma" {
try testCanonical(
\\pub fn main() void {
\\ @breakpoint();
\\ _ = @intFromBool(a);
\\ _ = @call(
\\ a,
\\ b,
\\ c,
\\ );
\\}
\\
);
}
test "zig fmt: asm expression with comptime content" {
try testTransform(
\\comptime {
\\ asm ("foo" ++ "bar");
\\}
\\pub fn main() void {
\\ asm volatile ("foo" ++ "bar");
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ );
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ : [_] "" (y),
\\ );
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ : [_] "" (y),
\\ : "h", "e", "l", "l", "o"
\\ );
\\}
\\
,
\\comptime {
\\ asm ("foo" ++ "bar");
\\}
\\pub fn main() void {
\\ asm volatile ("foo" ++ "bar");
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ );
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ : [_] "" (y),
\\ );
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ : [_] "" (y),
\\ : .{ .h = true, .e = true, .l = true, .l = true, .o = true }
\\ );
\\}
\\
);
}
test "zig fmt: array types last token" {
try testCanonical(
\\test {
\\ const x = [40]u32;
\\}
\\
\\test {
\\ const x = [40:0]u32;
\\}
\\
);
}
test "zig fmt: sentinel-terminated array type" {
try testCanonical(
\\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 {
\\ return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
\\}
\\
);
}
test "zig fmt: sentinel-terminated slice type" {
try testCanonical(
\\pub fn toSlice(self: Buffer) [:0]u8 {
\\ return self.list.toSlice()[0..self.len()];
\\}
\\
);
}
test "zig fmt: pointer-to-one with modifiers" {
try testCanonical(
\\const x: *u32 = undefined;
\\const y: *allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\const z: *allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
test "zig fmt: pointer-to-many with modifiers" {
try testCanonical(
\\const x: [*]u32 = undefined;
\\const y: [*]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\const z: [*]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
test "zig fmt: sentinel pointer with modifiers" {
try testCanonical(
\\const x: [*:42]u32 = undefined;
\\const y: [*:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\const y: [*:42]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
test "zig fmt: c pointer with modifiers" {
try testCanonical(
\\const x: [*c]u32 = undefined;
\\const y: [*c]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\const z: [*c]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
test "zig fmt: slice with modifiers" {
try testCanonical(
\\const x: []u32 = undefined;
\\const y: []allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
test "zig fmt: sentinel slice with modifiers" {
try testCanonical(
\\const x: [:42]u32 = undefined;
\\const y: [:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
\\
);
}
test "zig fmt: anon literal in array" {
try testCanonical(
\\var arr: [2]Foo = .{
\\ .{ .a = 2 },
\\ .{ .b = 3 },
\\};
\\
);
}
test "zig fmt: alignment in anonymous literal" {
try testTransform(
\\const a = .{
\\ "U", "L", "F",
\\ "U'",
\\ "L'",
\\ "F'",
\\};
\\
,
\\const a = .{
\\ "U", "L", "F",
\\ "U'", "L'", "F'",
\\};
\\
);
}
test "zig fmt: anon struct literal 0 element" {
try testCanonical(
\\test {
\\ const x = .{};
\\}
\\
);
}
test "zig fmt: anon struct literal 1 element" {
try testCanonical(
\\test {
\\ const x = .{ .a = b };
\\}
\\
);
}
test "zig fmt: anon struct literal 1 element comma" {
try testCanonical(
\\test {
\\ const x = .{
\\ .a = b,
\\ };
\\}
\\
);
}
test "zig fmt: anon struct literal 2 element" {
try testCanonical(
\\test {
\\ const x = .{ .a = b, .c = d };
\\}
\\
);
}
test "zig fmt: anon struct literal 2 element comma" {
try testCanonical(
\\test {
\\ const x = .{
\\ .a = b,
\\ .c = d,
\\ };
\\}
\\
);
}
test "zig fmt: anon struct literal 3 element" {
try testCanonical(
\\test {
\\ const x = .{ .a = b, .c = d, .e = f };
\\}
\\
);
}
test "zig fmt: anon struct literal 3 element comma" {
try testCanonical(
\\test {
\\ const x = .{
\\ .a = b,
\\ .c = d,
\\ .e = f,
\\ };
\\}
\\
);
}
test "zig fmt: struct literal 0 element" {
try testCanonical(
\\test {
\\ const x = X{};
\\}
\\
);
}
test "zig fmt: struct literal 1 element" {
try testCanonical(
\\test {
\\ const x = X{ .a = b };
\\}
\\
);
}
test "zig fmt: Unicode code point literal larger than u8" {
try testCanonical(
\\test {
\\ const x = X{
\\ .a = b,
\\ };
\\}
\\
);
}
test "zig fmt: struct literal 2 element" {
try testCanonical(
\\test {
\\ const x = X{ .a = b, .c = d };
\\}
\\
);
}
test "zig fmt: struct literal 2 element comma" {
try testCanonical(
\\test {
\\ const x = X{
\\ .a = b,
\\ .c = d,
\\ };
\\}
\\
);
}
test "zig fmt: struct literal 3 element" {
try testCanonical(
\\test {
\\ const x = X{ .a = b, .c = d, .e = f };
\\}
\\
);
}
test "zig fmt: struct literal 3 element comma" {
try testCanonical(
\\test {
\\ const x = X{
\\ .a = b,
\\ .c = d,
\\ .e = f,
\\ };
\\}
\\
);
}
test "zig fmt: anon list literal 1 element" {
try testCanonical(
\\test {
\\ const x = .{a};
\\}
\\
);
}
test "zig fmt: anon list literal 1 element comma" {
try testCanonical(
\\test {
\\ const x = .{
\\ a,
\\ };
\\}
\\
);
}
test "zig fmt: anon list literal 2 element" {
try testCanonical(
\\test {
\\ const x = .{ a, b };
\\}
\\
);
}
test "zig fmt: anon list literal 2 element comma" {
try testCanonical(
\\test {
\\ const x = .{
\\ a,
\\ b,
\\ };
\\}
\\
);
}
test "zig fmt: anon list literal 3 element" {
try testCanonical(
\\test {
\\ const x = .{ a, b, c };
\\}
\\
);
}
test "zig fmt: anon list literal 3 element comma" {
try testCanonical(
\\test {
\\ const x = .{
\\ a,
\\ // foo
\\ b,
\\
\\ c,
\\ };
\\}
\\
);
}
test "zig fmt: array literal 0 element" {
try testCanonical(
\\test {
\\ const x = [_]u32{};
\\}
\\
);
}
test "zig fmt: array literal 1 element" {
try testCanonical(
\\test {
\\ const x = [_]u32{a};
\\}
\\
);
}
test "zig fmt: array literal 1 element comma" {
try testCanonical(
\\test {
\\ const x = [1]u32{
\\ a,
\\ };
\\}
\\
);
}
test "zig fmt: array literal 2 element" {
try testCanonical(
\\test {
\\ const x = [_]u32{ a, b };
\\}
\\
);
}
test "zig fmt: array literal 2 element comma" {
try testCanonical(
\\test {
\\ const x = [2]u32{
\\ a,
\\ b,
\\ };
\\}
\\
);
}
test "zig fmt: array literal 3 element" {
try testCanonical(
\\test {
\\ const x = [_]u32{ a, b, c };
\\}
\\
);
}
test "zig fmt: array literal 3 element comma" {
try testCanonical(
\\test {
\\ const x = [3]u32{
\\ a,
\\ b,
\\ c,
\\ };
\\}
\\
);
}
test "zig fmt: sentinel array literal 1 element" {
try testCanonical(
\\test {
\\ const x = [_:9000]u32{a};
\\}
\\
);
}
test "zig fmt: slices" {
try testCanonical(
\\const a = b[0..];
\\const c = d[0..1];
\\const d = f[0.. :0];
\\const e = f[0..1 :0];
\\
);
}
test "zig fmt: slices with spaces in bounds" {
try testCanonical(
\\const a = b[0 + 0 ..];
\\const c = d[0 + 0 .. 1];
\\const c = d[0 + 0 .. :0];
\\const e = f[0 .. 1 + 1 :0];
\\
);
}
test "zig fmt: block in slice expression" {
try testCanonical(
\\const a = b[{
\\ _ = x;
\\}..];
\\const c = d[0..{
\\ _ = x;
\\ _ = y;
\\}];
\\const e = f[0..1 :{
\\ _ = x;
\\ _ = y;
\\ _ = z;
\\}];
\\
);
}
test "zig fmt: whitespace fixes" {
try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
\\test "" {
\\ const hi = x;
\\}
\\// zig fmt: off
\\test ""{
\\ const a = b;}
\\
);
}
test "zig fmt: while else err prong with no block" {
try testCanonical(
\\test "" {
\\ const result = while (returnError()) |value| {
\\ break value;
\\ } else |err| @as(i32, 2);
\\ try expect(result == 2);
\\}
\\
);
}
test "zig fmt: tagged union with enum values" {
try testCanonical(
\\const MultipleChoice2 = union(enum(u32)) {
\\ Unspecified1: i32,
\\ A: f32 = 20,
\\ Unspecified2: void,
\\ B: bool = 40,
\\ Unspecified3: i32,
\\ C: i8 = 60,
\\ Unspecified4: void,
\\ D: void = 1000,
\\ Unspecified5: i32,
\\};
\\
);
}
test "zig fmt: tagged union enum tag last token" {
try testCanonical(
\\test {
\\ const U = union(enum(u32)) {};
\\}
\\
\\test {
\\ const U = union(enum(u32)) { foo };
\\}
\\
\\test {
\\ const U = union(enum(u32)) {
\\ foo,
\\ };
\\}
\\
);
}
test "zig fmt: allowzero pointer" {
try testCanonical(
\\const T = [*]allowzero const u8;
\\
);
}
test "zig fmt: empty enum decls" {
try testCanonical(
\\const A = enum {};
\\const B = enum(u32) {};
\\const C = extern enum(c_int) {};
\\const D = packed enum(u8) {};
\\
);
}
test "zig fmt: empty union decls" {
try testCanonical(
\\const A = union {};
\\const B = union(enum) {};
\\const C = union(Foo) {};
\\const D = extern union {};
\\const E = packed union {};
\\
);
}
test "zig fmt: enum literal" {
try testCanonical(
\\const x = .hi;
\\
);
}
test "zig fmt: enum literal inside array literal" {
try testCanonical(
\\test "enums in arrays" {
\\ var colors = []Color{.Green};
\\ colors = []Colors{ .Green, .Cyan };
\\ colors = []Colors{
\\ .Grey,
\\ .Green,
\\ .Cyan,
\\ };
\\}
\\
);
}
test "zig fmt: character literal larger than u8" {
try testCanonical(
\\const x = '\u{01f4a9}';
\\
);
}
test "zig fmt: infix operator and then multiline string literal" {
try testCanonical(
\\const x = "" ++
\\ \\ hi
\\;
\\
);
}
test "zig fmt: infix operator and then multiline string literal over multiple lines" {
try testCanonical(
\\const x = "" ++
\\ \\ hi0
\\ \\ hi1
\\ \\ hi2
\\;
\\
);
}
test "zig fmt: C pointers" {
try testCanonical(
\\const Ptr = [*c]i32;
\\
);
}
test "zig fmt: threadlocal" {
try testCanonical(
\\threadlocal var x: i32 = 1234;
\\
);
}
test "zig fmt: linksection" {
try testCanonical(
\\export var aoeu: u64 linksection(".text.derp") = 1234;
\\export fn _start() linksection(".text.boot") callconv(.naked) noreturn {}
\\
);
}
test "zig fmt: addrspace" {
try testCanonical(
\\export var python_length: u64 align(1) addrspace(.generic);
\\export var python_color: Color addrspace(.generic) = .green;
\\export var python_legs: u0 align(8) addrspace(.generic) linksection(".python") = 0;
\\export fn python_hiss() align(8) addrspace(.generic) linksection(".python") void;
\\
);
}
test "zig fmt: correctly space struct fields with doc comments" {
try testTransform(
\\pub const S = struct {
\\ /// A
\\ a: u8,
\\ /// B
\\ /// B (cont)
\\ b: u8,
\\
\\
\\ /// C
\\ c: u8,
\\};
\\
,
\\pub const S = struct {
\\ /// A
\\ a: u8,
\\ /// B
\\ /// B (cont)
\\ b: u8,
\\
\\ /// C
\\ c: u8,
\\};
\\
);
}
test "zig fmt: doc comments on param decl" {
try testCanonical(
\\pub const Allocator = struct {
\\ shrinkFn: fn (
\\ self: Allocator,
\\ /// Guaranteed to be the same as what was returned from most recent call to
\\ /// `allocFn`, `reallocFn`, or `shrinkFn`.
\\ old_mem: []u8,
\\ /// Guaranteed to be the same as what was returned from most recent call to
\\ /// `allocFn`, `reallocFn`, or `shrinkFn`.
\\ old_alignment: u29,
\\ /// Guaranteed to be less than or equal to `old_mem.len`.
\\ new_byte_count: usize,
\\ /// Guaranteed to be less than or equal to `old_alignment`.
\\ new_alignment: u29,
\\ ) []u8,
\\};
\\
);
}
test "zig fmt: aligned struct field" {
try testCanonical(
\\pub const S = struct {
\\ f: i32 align(32),
\\};
\\
);
try testCanonical(
\\pub const S = struct {
\\ f: i32 align(32) = 1,
\\};
\\
);
}
test "zig fmt: comment to disable/enable zig fmt first" {
try testCanonical(
\\// Test trailing comma syntax
\\// zig fmt: off
\\
\\const struct_trailing_comma = struct { x: i32, y: i32, };
);
}
test "zig fmt: 'zig fmt: (off|on)' can be surrounded by arbitrary whitespace" {
try testTransform(
\\// Test trailing comma syntax
\\// zig fmt: off
\\
\\const struct_trailing_comma = struct { x: i32, y: i32, };
\\
\\// zig fmt: on
,
\\// Test trailing comma syntax
\\// zig fmt: off
\\
\\const struct_trailing_comma = struct { x: i32, y: i32, };
\\
\\// zig fmt: on
\\
);
}
test "zig fmt: comment to disable/enable zig fmt" {
try testTransform(
\\const a = b;
\\// zig fmt: off
\\const c = d;
\\// zig fmt: on
\\const e = f;
,
\\const a = b;
\\// zig fmt: off
\\const c = d;
\\// zig fmt: on
\\const e = f;
\\
);
}
test "zig fmt: line comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\// Test
\\const e = f;
);
}
test "zig fmt: doc comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\/// test
\\const e = f;
);
}
test "zig fmt: line and doc comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\// test 1
\\/// test 2
\\const e = f;
);
}
test "zig fmt: doc and line comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\/// test 1
\\// test 2
\\const e = f;
);
}
test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\// zig fmt: on
\\// zig fmt: off
\\const e = f;
\\// zig fmt: off
\\// zig fmt: on
\\// zig fmt: off
\\const a = b;
\\// zig fmt: on
\\const c = d;
\\// zig fmt: on
\\
);
}
test "zig fmt: line comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\// test
\\const e = f;
\\
);
}
test "zig fmt: doc comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\/// test
\\const e = f;
\\
);
}
test "zig fmt: line and doc comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\// test1
\\/// test2
\\const e = f;
\\
);
}
test "zig fmt: doc and line comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\/// test1
\\// test2
\\const e = f;
\\
);
}
test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" {
try testTransform(
\\test "" {
\\ const x = 42;
\\
\\ if (foobar) |y| {
\\ // zig fmt: off
\\ }// zig fmt: on
\\
\\ const z = 420;
\\}
\\
,
\\test "" {
\\ const x = 42;
\\
\\ if (foobar) |y| {
\\ // zig fmt: off
\\ }// zig fmt: on
\\
\\ const z = 420;
\\}
\\
);
}
test "zig fmt: 'zig fmt: on' indentation is unchanged" {
try testCanonical(
\\fn initOptionsAndLayouts(output: *Output, context: *Context) !void {
\\ // zig fmt: off
\\ try output.main_amount.init(output, "main_amount"); errdefer optput.main_amount.deinit();
\\ try output.main_factor.init(output, "main_factor"); errdefer optput.main_factor.deinit();
\\ try output.view_padding.init(output, "view_padding"); errdefer optput.view_padding.deinit();
\\ try output.outer_padding.init(output, "outer_padding"); errdefer optput.outer_padding.deinit();
\\ // zig fmt: on
\\
\\ // zig fmt: off
\\ try output.top.init(output, .top); errdefer optput.top.deinit();
\\ try output.right.init(output, .right); errdefer optput.right.deinit();
\\ try output.bottom.init(output, .bottom); errdefer optput.bottom.deinit();
\\ try output.left.init(output, .left); errdefer optput.left.deinit();
\\ // zig fmt: on
\\}
\\
);
}
test "zig fmt: pointer of unknown length" {
try testCanonical(
\\fn foo(ptr: [*]u8) void {}
\\
);
}
test "zig fmt: spaces around slice operator" {
try testCanonical(
\\var a = b[c..d];
\\var a = b[c..d :0];
\\var a = b[c + 1 .. d];
\\var a = b[c + 1 ..];
\\var a = b[c .. d + 1];
\\var a = b[c .. d + 1 :0];
\\var a = b[c.a..d.e];
\\var a = b[c.a..d.e :0];
\\
);
}
test "zig fmt: 2nd arg multiline string" {
try testCanonical(
\\comptime {
\\ cases.addAsm("hello world linux x86_64",
\\ \\.text
\\ , "Hello, world!\n");
\\}
\\
);
try testTransform(
\\comptime {
\\ cases.addAsm("hello world linux x86_64",
\\ \\.text
\\ , "Hello, world!\n",);
\\}
,
\\comptime {
\\ cases.addAsm(
\\ "hello world linux x86_64",
\\ \\.text
\\ ,
\\ "Hello, world!\n",
\\ );
\\}
\\
);
}
test "zig fmt: 2nd arg multiline string many args" {
try testCanonical(
\\comptime {
\\ cases.addAsm("hello world linux x86_64",
\\ \\.text
\\ , "Hello, world!\n", "Hello, world!\n");
\\}
\\
);
}
test "zig fmt: final arg multiline string" {
try testCanonical(
\\comptime {
\\ cases.addAsm("hello world linux x86_64", "Hello, world!\n",
\\ \\.text
\\ );
\\}
\\
);
}
test "zig fmt: if condition wraps" {
try testTransform(
\\comptime {
\\ if (cond and
\\ cond) {
\\ return x;
\\ }
\\ while (cond and
\\ cond) {
\\ return x;
\\ }
\\ if (a == b and
\\ c) {
\\ a = b;
\\ }
\\ while (a == b and
\\ c) {
\\ a = b;
\\ }
\\ if ((cond and
\\ cond)) {
\\ return x;
\\ }
\\ while ((cond and
\\ cond)) {
\\ return x;
\\ }
\\ var a = if (a) |*f| x: {
\\ break :x &a.b;
\\ } else |err| err;
\\ var a = if (cond and
\\ cond) |*f|
\\ x: {
\\ break :x &a.b;
\\ } else |err| err;
\\}
,
\\comptime {
\\ if (cond and
\\ cond)
\\ {
\\ return x;
\\ }
\\ while (cond and
\\ cond)
\\ {
\\ return x;
\\ }
\\ if (a == b and
\\ c)
\\ {
\\ a = b;
\\ }
\\ while (a == b and
\\ c)
\\ {
\\ a = b;
\\ }
\\ if ((cond and
\\ cond))
\\ {
\\ return x;
\\ }
\\ while ((cond and
\\ cond))
\\ {
\\ return x;
\\ }
\\ var a = if (a) |*f| x: {
\\ break :x &a.b;
\\ } else |err| err;
\\ var a = if (cond and
\\ cond) |*f|
\\ x: {
\\ break :x &a.b;
\\ } else |err| err;
\\}
\\
);
}
test "zig fmt: if condition has line break but must not wrap" {
try testCanonical(
\\comptime {
\\ if (self.user_input_options.put(
\\ name,
\\ UserInputOption{
\\ .name = name,
\\ .used = false,
\\ },
\\ ) catch unreachable) |*prev_value| {
\\ foo();
\\ bar();
\\ }
\\ if (put(
\\ a,
\\ b,
\\ )) {
\\ foo();
\\ }
\\}
\\
);
}
test "zig fmt: if condition has line break but must not wrap (no fn call comma)" {
try testCanonical(
\\comptime {
\\ if (self.user_input_options.put(name, UserInputOption{
\\ .name = name,
\\ .used = false,
\\ }) catch unreachable) |*prev_value| {
\\ foo();
\\ bar();
\\ }
\\ if (put(
\\ a,
\\ b,
\\ )) {
\\ foo();
\\ }
\\}
\\
);
}
test "zig fmt: function call with multiline argument" {
try testCanonical(
\\comptime {
\\ self.user_input_options.put(name, UserInputOption{
\\ .name = name,
\\ .used = false,
\\ });
\\}
\\
);
}
test "zig fmt: if-else with comment before else" {
try testCanonical(
\\comptime {
\\ // cexp(finite|nan +- i inf|nan) = nan + i nan
\\ if ((hx & 0x7fffffff) != 0x7f800000) {
\\ return Complex(f32).init(y - y, y - y);
\\ } // cexp(-inf +- i inf|nan) = 0 + i0
\\ else if (hx & 0x80000000 != 0) {
\\ return Complex(f32).init(0, 0);
\\ } // cexp(+inf +- i inf|nan) = inf + i nan
\\ else {
\\ return Complex(f32).init(x, y - y);
\\ }
\\}
\\
);
}
test "zig fmt: if nested" {
try testCanonical(
\\pub fn foo() void {
\\ return if ((aInt & bInt) >= 0)
\\ if (aInt < bInt)
\\ GE_LESS
\\ else if (aInt == bInt)
\\ GE_EQUAL
\\ else
\\ GE_GREATER
\\ // comment
\\ else if (aInt > bInt)
\\ GE_LESS
\\ else if (aInt == bInt)
\\ GE_EQUAL
\\ else
\\ GE_GREATER;
\\ // comment
\\}
\\
);
}
test "zig fmt: respect line breaks in if-else" {
try testCanonical(
\\comptime {
\\ return if (cond) a else b;
\\ return if (cond)
\\ a
\\ else
\\ b;
\\ return if (cond)
\\ a
\\ else if (cond)
\\ b
\\ else
\\ c;
\\}
\\
);
}
test "zig fmt: respect line breaks after infix operators" {
try testCanonical(
\\comptime {
\\ self.crc =
\\ lookup_tables[0][p[7]] ^
\\ lookup_tables[1][p[6]] ^
\\ lookup_tables[2][p[5]] ^
\\ lookup_tables[3][p[4]] ^
\\ lookup_tables[4][@as(u8, self.crc >> 24)] ^
\\ lookup_tables[5][@as(u8, self.crc >> 16)] ^
\\ lookup_tables[6][@as(u8, self.crc >> 8)] ^
\\ lookup_tables[7][@as(u8, self.crc >> 0)];
\\}
\\
);
}
test "zig fmt: fn decl with trailing comma" {
try testTransform(
\\fn foo(a: i32, b: i32,) void {}
,
\\fn foo(
\\ a: i32,
\\ b: i32,
\\) void {}
\\
);
}
test "zig fmt: enum decl with no trailing comma" {
try testTransform(
\\const StrLitKind = enum {Normal, C};
,
\\const StrLitKind = enum { Normal, C };
\\
);
}
test "zig fmt: switch comment before prong" {
try testCanonical(
\\comptime {
\\ switch (a) {
\\ // hi
\\ 0 => {},
\\ }
\\}
\\
);
}
test "zig fmt: switch comment after prong" {
try testCanonical(
\\comptime {
\\ switch (a) {
\\ 0,
\\ // hi
\\ => {},
\\ }
\\}
\\
);
}
test "zig fmt: struct literal no trailing comma" {
try testTransform(
\\const a = foo{ .x = 1, .y = 2 };
\\const a = foo{ .x = 1,
\\ .y = 2 };
\\const a = foo{ .x = 1,
\\ .y = 2, };
,
\\const a = foo{ .x = 1, .y = 2 };
\\const a = foo{ .x = 1, .y = 2 };
\\const a = foo{
\\ .x = 1,
\\ .y = 2,
\\};
\\
);
}
test "zig fmt: struct literal containing a multiline expression" {
try testTransform(
\\const a = A{ .x = if (f1()) 10 else 20 };
\\const a = A{ .x = if (f1()) 10 else 20, };
\\const a = A{ .x = if (f1())
\\ 10 else 20 };
\\const a = A{ .x = if (f1())
\\ 10 else 20,};
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100, };
\\const a = A{ .x = if (f1())
\\ 10 else 20};
\\const a = A{ .x = if (f1())
\\ 10 else 20,};
\\const a = A{ .x = switch(g) {0 => "ok", else => "no"} };
\\const a = A{ .x = switch(g) {0 => "ok", else => "no"}, };
\\
,
\\const a = A{ .x = if (f1()) 10 else 20 };
\\const a = A{
\\ .x = if (f1()) 10 else 20,
\\};
\\const a = A{ .x = if (f1())
\\ 10
\\else
\\ 20 };
\\const a = A{
\\ .x = if (f1())
\\ 10
\\ else
\\ 20,
\\};
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
\\const a = A{
\\ .x = if (f1()) 10 else 20,
\\ .y = f2() + 100,
\\};
\\const a = A{ .x = if (f1())
\\ 10
\\else
\\ 20 };
\\const a = A{
\\ .x = if (f1())
\\ 10
\\ else
\\ 20,
\\};
\\const a = A{ .x = switch (g) {
\\ 0 => "ok",
\\ else => "no",
\\} };
\\const a = A{
\\ .x = switch (g) {
\\ 0 => "ok",
\\ else => "no",
\\ },
\\};
\\
);
}
test "zig fmt: array literal with hint" {
try testTransform(
\\const a = []u8{
\\ 1, 2, //
\\ 3,
\\ 4,
\\ 5,
\\ 6,
\\ 7 };
\\const a = []u8{
\\ 1, 2, //
\\ 3,
\\ 4,
\\ 5,
\\ 6,
\\ 7, 8 };
\\const a = []u8{
\\ 1, 2, //
\\ 3,
\\ 4,
\\ 5,
\\ 6, // blah
\\ 7, 8 };
\\const a = []u8{
\\ 1, 2, //
\\ 3, //
\\ 4,
\\ 5,
\\ 6,
\\ 7 };
\\const a = []u8{
\\ 1,
\\ 2,
\\ 3, 4, //
\\ 5, 6, //
\\ 7, 8, //
\\};
,
\\const a = []u8{
\\ 1, 2, //
\\ 3, 4,
\\ 5, 6,
\\ 7,
\\};
\\const a = []u8{
\\ 1, 2, //
\\ 3, 4,
\\ 5, 6,
\\ 7, 8,
\\};
\\const a = []u8{
\\ 1, 2, //
\\ 3, 4,
\\ 5,
\\ 6, // blah
\\ 7,
\\ 8,
\\};
\\const a = []u8{
\\ 1, 2, //
\\ 3, //
\\ 4,
\\ 5,
\\ 6,
\\ 7,
\\};
\\const a = []u8{
\\ 1,
\\ 2,
\\ 3, 4, //
\\ 5, 6, //
\\ 7, 8, //
\\};
\\
);
}
test "zig fmt: array literal vertical column alignment" {
try testTransform(
\\const a = []u8{
\\ 1000, 200,
\\ 30, 4,
\\ 50000, 60,
\\};
\\const a = []u8{0, 1, 2, 3, 40,
\\ 4,5,600,7,
\\ 80,
\\ 9, 10, 11, 0, 13, 14, 15,};
\\const a = [12]u8{
\\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
\\const a = [12]u8{
\\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, };
\\
,
\\const a = []u8{
\\ 1000, 200,
\\ 30, 4,
\\ 50000, 60,
\\};
\\const a = []u8{
\\ 0, 1, 2, 3, 40,
\\ 4, 5, 600, 7, 80,
\\ 9, 10, 11, 0, 13,
\\ 14, 15,
\\};
\\const a = [12]u8{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
\\const a = [12]u8{
\\ 31,
\\ 28,
\\ 31,
\\ 30,
\\ 31,
\\ 30,
\\ 31,
\\ 31,
\\ 30,
\\ 31,
\\ 30,
\\ 31,
\\};
\\
);
}
test "zig fmt: multiline string with backslash at end of line" {
try testCanonical(
\\comptime {
\\ err(
\\ \\\
\\ );
\\}
\\
);
}
test "zig fmt: multiline string parameter in fn call with trailing comma" {
try testCanonical(
\\fn foo() void {
\\ try stdout.print(
\\ \\ZIG_CMAKE_BINARY_DIR {s}
\\ \\ZIG_C_HEADER_FILES {s}
\\ \\ZIG_DIA_GUIDS_LIB {s}
\\ \\
\\ ,
\\ std.mem.sliceTo(c.ZIG_CMAKE_BINARY_DIR, 0),
\\ std.mem.sliceTo(c.ZIG_CXX_COMPILER, 0),
\\ std.mem.sliceTo(c.ZIG_DIA_GUIDS_LIB, 0),
\\ );
\\}
\\
);
}
test "zig fmt: trailing comma on fn call" {
try testCanonical(
\\comptime {
\\ var module = try Module.create(
\\ allocator,
\\ zig_lib_dir,
\\ full_cache_dir,
\\ );
\\}
\\
);
}
test "zig fmt: multi line arguments without last comma" {
try testTransform(
\\pub fn foo(
\\ a: usize,
\\ b: usize,
\\ c: usize,
\\ d: usize
\\) usize {
\\ return a + b + c + d;
\\}
\\
,
\\pub fn foo(a: usize, b: usize, c: usize, d: usize) usize {
\\ return a + b + c + d;
\\}
\\
);
}
test "zig fmt: empty block with only comment" {
try testCanonical(
\\comptime {
\\ {
\\ // comment
\\ }
\\}
\\
);
}
test "zig fmt: trailing commas on struct decl" {
try testTransform(
\\const RoundParam = struct {
\\ k: usize, s: u32, t: u32
\\};
\\const RoundParam = struct {
\\ k: usize, s: u32, t: u32,
\\};
,
\\const RoundParam = struct { k: usize, s: u32, t: u32 };
\\const RoundParam = struct {
\\ k: usize,
\\ s: u32,
\\ t: u32,
\\};
\\
);
}
test "zig fmt: extra newlines at the end" {
try testTransform(
\\const a = b;
\\
\\
\\
,
\\const a = b;
\\
);
}
test "zig fmt: simple asm" {
try testTransform(
\\comptime {
\\ asm volatile (
\\ \\.globl aoeu;
\\ \\.type aoeu, @function;
\\ \\.set aoeu, derp;
\\ );
\\
\\ asm ("not real assembly"
\\ :[a] "x" (x),);
\\ asm ("not real assembly"
\\ :[a] "x" (->i32),:[a] "x" (1),);
\\ asm ("still not real assembly"
\\ :::"a","b",);
\\}
,
\\comptime {
\\ asm volatile (
\\ \\.globl aoeu;
\\ \\.type aoeu, @function;
\\ \\.set aoeu, derp;
\\ );
\\
\\ asm ("not real assembly"
\\ : [a] "x" (x),
\\ );
\\ asm ("not real assembly"
\\ : [a] "x" (-> i32),
\\ : [a] "x" (1),
\\ );
\\ asm ("still not real assembly" ::: .{ .a = true, .b = true });
\\}
\\
);
}
test "zig fmt: nested struct literal with one item" {
try testCanonical(
\\const a = foo{
\\ .item = bar{ .a = b },
\\};
\\
);
}
test "zig fmt: switch cases trailing comma" {
try testTransform(
\\test "switch cases trailing comma"{
\\ switch (x) {
\\ 1,2,3 => {},
\\ 4,5, => {},
\\ 6... 8, => {},
\\ 9 ...
\\ 10 => {},
\\ 11 => {},
\\ 12, => {},
\\ else => {},
\\ }
\\}
,
\\test "switch cases trailing comma" {
\\ switch (x) {
\\ 1, 2, 3 => {},
\\ 4,
\\ 5,
\\ => {},
\\ 6...8,
\\ => {},
\\ 9...10 => {},
\\ 11 => {},
\\ 12,
\\ => {},
\\ else => {},
\\ }
\\}
\\
);
}
test "zig fmt: slice align" {
try testCanonical(
\\const A = struct {
\\ items: []align(A) T,
\\};
\\
);
}
test "zig fmt: add trailing comma to array literal" {
try testTransform(
\\comptime {
\\ return []u16{'m', 's', 'y', 's', '-' // hi
\\ };
\\ return []u16{'m', 's', 'y', 's',
\\ '-'};
\\ return []u16{'m', 's', 'y', 's', '-'};
\\}
,
\\comptime {
\\ return []u16{
\\ 'm', 's', 'y', 's', '-', // hi
\\ };
\\ return []u16{ 'm', 's', 'y', 's', '-' };
\\ return []u16{ 'm', 's', 'y', 's', '-' };
\\}
\\
);
}
test "zig fmt: first thing in file is line comment" {
try testCanonical(
\\// Introspection and determination of system libraries needed by zig.
\\
\\// Introspection and determination of system libraries needed by zig.
\\
\\const std = @import("std");
\\
);
}
test "zig fmt: line comment after doc comment" {
try testCanonical(
\\/// doc comment
\\// line comment
\\fn foo() void {}
\\
);
}
test "zig fmt: bit field alignment" {
try testCanonical(
\\test {
\\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3);
\\}
\\
);
}
test "zig fmt: nested switch" {
try testCanonical(
\\test {
\\ switch (state) {
\\ TermState.Start => switch (c) {
\\ '\x1b' => state = TermState.Escape,
\\ else => try out.writeByte(c),
\\ },
\\ }
\\}
\\
);
}
test "zig fmt: float literal with exponent" {
try testCanonical(
\\pub const f64_true_min = 4.94065645841246544177e-324;
\\const threshold = 0x1.a827999fcef32p+1022;
\\
);
}
test "zig fmt: if-else end of comptime" {
try testCanonical(
\\comptime {
\\ if (a) {
\\ b();
\\ } else {
\\ b();
\\ }
\\}
\\
);
}
test "zig fmt: nested blocks" {
try testCanonical(
\\comptime {
\\ {
\\ {
\\ {
\\ a();
\\ }
\\ }
\\ }
\\}
\\
);
}
test "zig fmt: block with same line comment after end brace" {
try testCanonical(
\\test {
\\ {
\\ const a = b;
\\ } // end of block
\\}
\\
);
}
test "zig fmt: statements with comment between" {
try testCanonical(
\\comptime {
\\ a = b;
\\ // comment
\\ a = b;
\\}
\\
);
}
test "zig fmt: statements with empty line between" {
try testCanonical(
\\comptime {
\\ a = b;
\\
\\ a = b;
\\}
\\
);
}
test "zig fmt: ptr deref operator and unwrap optional operator" {
try testCanonical(
\\const a = b.*;
\\const a = b.?;
\\
);
}
test "zig fmt: comment after if before another if" {
try testCanonical(
\\test "aoeu" {
\\ // comment
\\ if (x) {
\\ bar();
\\ }
\\}
\\
\\test "aoeu" {
\\ if (x) {
\\ foo();
\\ }
\\ // comment
\\ if (x) {
\\ bar();
\\ }
\\}
\\
);
}
test "zig fmt: line comment between if block and else keyword" {
try testCanonical(
\\test "aoeu" {
\\ // cexp(finite|nan +- i inf|nan) = nan + i nan
\\ if ((hx & 0x7fffffff) != 0x7f800000) {
\\ return Complex(f32).init(y - y, y - y);
\\ }
\\ // cexp(-inf +- i inf|nan) = 0 + i0
\\ else if (hx & 0x80000000 != 0) {
\\ return Complex(f32).init(0, 0);
\\ }
\\ // cexp(+inf +- i inf|nan) = inf + i nan
\\ // another comment
\\ else {
\\ return Complex(f32).init(x, y - y);
\\ }
\\}
\\
);
}
test "zig fmt: same line comments in expression" {
try testCanonical(
\\test "aoeu" {
\\ const x = ( // a
\\ 0 // b
\\ ); // c
\\}
\\
);
}
test "zig fmt: add comma on last switch prong" {
try testTransform(
\\test "aoeu" {
\\switch (self.init_arg_expr) {
\\ InitArg.Type => |t| { },
\\ InitArg.None,
\\ InitArg.Enum => { }
\\}
\\ switch (self.init_arg_expr) {
\\ InitArg.Type => |t| { },
\\ InitArg.None,
\\ InitArg.Enum => { }//line comment
\\ }
\\}
,
\\test "aoeu" {
\\ switch (self.init_arg_expr) {
\\ InitArg.Type => |t| {},
\\ InitArg.None, InitArg.Enum => {},
\\ }
\\ switch (self.init_arg_expr) {
\\ InitArg.Type => |t| {},
\\ InitArg.None, InitArg.Enum => {}, //line comment
\\ }
\\}
\\
);
}
test "zig fmt: same-line comment after a statement" {
try testCanonical(
\\test "" {
\\ a = b;
\\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
\\ a = b;
\\}
\\
);
}
test "zig fmt: same-line comment after var decl in struct" {
try testCanonical(
\\pub const vfs_cap_data = extern struct {
\\ const Data = struct {}; // when on disk.
\\};
\\
);
}
test "zig fmt: same-line comment after field decl" {
try testCanonical(
\\pub const dirent = extern struct {
\\ d_name: u8,
\\ d_name: u8, // comment 1
\\ d_name: u8,
\\ d_name: u8, // comment 2
\\ d_name: u8,
\\};
\\
);
}
test "zig fmt: same-line comment after switch prong" {
try testCanonical(
\\test "" {
\\ switch (err) {
\\ error.PathAlreadyExists => {}, // comment 2
\\ else => return err, // comment 1
\\ }
\\}
\\
);
}
test "zig fmt: same-line comment after non-block if expression" {
try testCanonical(
\\comptime {
\\ if (sr > n_uword_bits - 1) // d > r
\\ return 0;
\\}
\\
);
}
test "zig fmt: same-line comment on comptime expression" {
try testCanonical(
\\test "" {
\\ comptime assert(@typeInfo(T) == .int); // must pass an integer to absInt
\\}
\\
);
}
test "zig fmt: switch with empty body" {
try testCanonical(
\\test "" {
\\ foo() catch |err| switch (err) {};
\\}
\\
);
}
test "zig fmt: line comments in struct initializer" {
try testCanonical(
\\fn foo() void {
\\ return Self{
\\ .a = b,
\\
\\ // Initialize these two fields to buffer_size so that
\\ // in `readFn` we treat the state as being able to read
\\ .start_index = buffer_size,
\\ .end_index = buffer_size,
\\
\\ // middle
\\
\\ .a = b,
\\
\\ // end
\\ };
\\}
\\
);
}
test "zig fmt: first line comment in struct initializer" {
try testCanonical(
\\pub fn acquire(self: *Self) HeldLock {
\\ return HeldLock{
\\ // guaranteed allocation elision
\\ .held = self.lock.acquire(),
\\ .value = &self.private_data,
\\ };
\\}
\\
);
}
test "zig fmt: doc comments before struct field" {
try testCanonical(
\\pub const Allocator = struct {
\\ /// Allocate byte_count bytes and return them in a slice, with the
\\ /// slice's pointer aligned at least to alignment bytes.
\\ allocFn: fn () void,
\\};
\\
);
}
test "zig fmt: error set declaration" {
try testCanonical(
\\const E = error{
\\ A,
\\ B,
\\
\\ C,
\\};
\\
\\const Error = error{
\\ /// no more memory
\\ OutOfMemory,
\\};
\\
\\const Error = error{
\\ /// no more memory
\\ OutOfMemory,
\\
\\ /// another
\\ Another,
\\
\\ // end
\\};
\\
\\const Error = error{OutOfMemory};
\\const Error = error{};
\\
\\const Error = error{ OutOfMemory, OutOfTime };
\\
);
}
test "zig fmt: union(enum(u32)) with assigned enum values" {
try testCanonical(
\\const MultipleChoice = union(enum(u32)) {
\\ A = 20,
\\ B = 40,
\\ C = 60,
\\ D = 1000,
\\};
\\
);
}
test "zig fmt: resume from suspend block" {
try testCanonical(
\\fn foo() void {
\\ suspend {
\\ resume @frame();
\\ }
\\}
\\
);
}
test "zig fmt: comments before error set decl" {
try testCanonical(
\\const UnexpectedError = error{
\\ /// The Operating System returned an undocumented error code.
\\ Unexpected,
\\ // another
\\ Another,
\\
\\ // in between
\\
\\ // at end
\\};
\\
);
}
test "zig fmt: comments before switch prong" {
try testCanonical(
\\test "" {
\\ switch (err) {
\\ error.PathAlreadyExists => continue,
\\
\\ // comment 1
\\
\\ // comment 2
\\ else => return err,
\\ // at end
\\ }
\\}
\\
);
}
test "zig fmt: comments before var decl in struct" {
try testCanonical(
\\const Foo = struct {
\\ /// comment
\\ bar: bool = true,
\\};
\\
);
}
test "zig fmt: array literal with 1 item on 1 line" {
try testCanonical(
\\var s = []const u64{0} ** 25;
\\
);
}
test "zig fmt: comments before global variables" {
try testCanonical(
\\/// comment
\\var foo: i32 = undefined;
\\
);
}
test "zig fmt: comments in statements" {
try testCanonical(
\\comptime {
\\ // a
\\
\\ const x = 42; // b
\\
\\ // c
\\}
\\
);
}
test "zig fmt: comments before test decl" {
try testCanonical(
\\/// top level doc comment
\\test "hi" {}
\\
);
}
test "zig fmt: preserve spacing" {
try testCanonical(
\\const std = @import("std");
\\
\\pub fn main() !void {
\\ var stdout_file = std.lol.abcd;
\\ var stdout_file = std.lol.abcd;
\\
\\ var stdout_file = std.lol.abcd;
\\ var stdout_file = std.lol.abcd;
\\}
\\
);
}
test "zig fmt: return types" {
try testCanonical(
\\pub fn main() !void {}
\\pub fn main() FooBar {}
\\pub fn main() i32 {}
\\
);
}
test "zig fmt: imports" {
try testCanonical(
\\const std = @import("std");
\\const std = @import();
\\
);
}
test "zig fmt: global declarations" {
try testCanonical(
\\const a = b;
\\pub const a = b;
\\var a = b;
\\pub var a = b;
\\const a: i32 = b;
\\pub const a: i32 = b;
\\var a: i32 = b;
\\pub var a: i32 = b;
\\extern const a: i32 = b;
\\pub extern const a: i32 = b;
\\extern var a: i32 = b;
\\pub extern var a: i32 = b;
\\extern "a" const a: i32 = b;
\\pub extern "a" const a: i32 = b;
\\extern "a" var a: i32 = b;
\\pub extern "a" var a: i32 = b;
\\
);
}
test "zig fmt: extern declaration" {
try testCanonical(
\\extern var foo: c_int;
\\
);
}
test "zig fmt: alignment" {
try testCanonical(
\\var foo: c_int align(1);
\\
);
}
test "zig fmt: C main" {
try testCanonical(
\\fn main(argc: c_int, argv: **u8) c_int {
\\ const a = b;
\\}
\\
);
}
test "zig fmt: return" {
try testCanonical(
\\fn foo(argc: c_int, argv: **u8) c_int {
\\ return 0;
\\}
\\
\\fn bar() void {
\\ return;
\\}
\\
);
}
test "zig fmt: function attributes" {
try testCanonical(
\\export fn foo() void {}
\\pub export fn foo() void {}
\\extern fn foo() void;
\\pub extern fn foo() void;
\\extern "c" fn foo() void;
\\pub extern "c" fn foo() void;
\\noinline fn foo() void {}
\\pub noinline fn foo() void {}
\\
);
}
test "zig fmt: nested pointers with ** tokens" {
try testCanonical(
\\const x: *u32 = undefined;
\\const x: **u32 = undefined;
\\const x: ***u32 = undefined;
\\const x: ****u32 = undefined;
\\const x: *****u32 = undefined;
\\const x: ******u32 = undefined;
\\const x: *******u32 = undefined;
\\
);
}
test "zig fmt: test declaration" {
try testCanonical(
\\test "test name" {
\\ const a = 1;
\\ var b = 1;
\\}
\\
);
}
test "zig fmt: precedence" {
try testCanonical(
\\test "precedence" {
\\ a!b();
\\ (a!b)();
\\ !a!b;
\\ !(a!b);
\\ !a{};
\\ !(a{});
\\ a + b{};
\\ (a + b){};
\\ a << b + c;
\\ (a << b) + c;
\\ a & b << c;
\\ (a & b) << c;
\\ a ^ b & c;
\\ (a ^ b) & c;
\\ a | b ^ c;
\\ (a | b) ^ c;
\\ a == b | c;
\\ (a == b) | c;
\\ a and b == c;
\\ (a and b) == c;
\\ a or b and c;
\\ (a or b) and c;
\\ (a or b) and c;
\\ a == b and c == d;
\\}
\\
);
}
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 "arrays" {
\\ const a: [2]u32 = .{ 1, 2 };
\\ const b = a ++ a;
\\ const c = a[0..];
\\ _ = c;
\\}
\\
);
}
test "zig fmt: container initializers" {
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 "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;
\\}
\\
);
}
test "zig fmt: blocks" {
try testCanonical(
\\test {
\\ {
\\ const a = b;
\\ }
\\ const c = d;
\\}
\\
);
}
test "zig fmt: switch" {
try testCanonical(
\\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,
\\ }
\\}
\\
);
try testTransform(
\\test {
\\ switch (x) {
\\ foo =>
\\ "bar",
\\ }
\\}
\\
,
\\test {
\\ switch (x) {
\\ foo => "bar",
\\ }
\\}
\\
);
}
test "zig fmt: switch multiline string" {
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",
\\ };
\\
\\ 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: 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: for" {
try testCanonical(
\\test "for" {
\\ for (a) |v| {
\\ continue;
\\ }
\\
\\ for (a) |v| continue;
\\
\\ for (a) |v| continue else return;
\\
\\ for (a) |v| {
\\ continue;
\\ } else return;
\\
\\ for (a) |v| continue else {
\\ return;
\\ }
\\
\\ for (a) |v|
\\ continue
\\ else
\\ return;
\\
\\ for (a) |v|
\\ continue;
\\
\\ for (a) |*v|
\\ continue;
\\
\\ for (a, 0..) |v, i| {
\\ continue;
\\ }
\\
\\ for (a, 0..) |v, i|
\\ continue;
\\
\\ 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;
\\ }
\\}
\\
);
try testTransform(
\\test "fix for" {
\\ for (a) |x|
\\ f(x) else continue;
\\}
\\
,
\\test "fix for" {
\\ for (a) |x|
\\ f(x)
\\ else
\\ continue;
\\}
\\
);
try testTransform(
\\test "fix for" {
\\ for (a, b, c,) |long, another, third,| {}
\\}
\\
,
\\test "fix for" {
\\ for (
\\ a,
\\ b,
\\ c,
\\ ) |
\\ long,
\\ another,
\\ third,
\\ | {}
\\}
\\
);
}
test "zig fmt: for if" {
try testCanonical(
\\test {
\\ for (a) |x| if (x) f(x);
\\
\\ for (a) |x| if (x)
\\ f(x);
\\
\\ for (a) |x| if (x) {
\\ f(x);
\\ };
\\
\\ for (a) |x|
\\ if (x)
\\ f(x);
\\
\\ for (a) |x|
\\ if (x) {
\\ f(x);
\\ };
\\}
\\
);
}
test "zig fmt: if for" {
try testCanonical(
\\test {
\\ 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);
\\
\\ if (a)
\\ for (x) |x| {
\\ f(x);
\\ };
\\}
\\
);
}
test "zig fmt: while if" {
try testCanonical(
\\test {
\\ 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: if while" {
try testCanonical(
\\test {
\\ if (a) while (x) : (cont) f(x);
\\
\\ if (a) while (x) : (cont)
\\ f(x);
\\
\\ if (a) while (x) : (cont) {
\\ f(x);
\\ };
\\
\\ if (a)
\\ while (x) : (cont)
\\ f(x);
\\
\\ if (a)
\\ while (x) : (cont) {
\\ f(x);
\\ };
\\}
\\
);
}
test "zig fmt: while for" {
try testCanonical(
\\test {
\\ 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);
\\
\\ while (a)
\\ for (x) |x| {
\\ f(x);
\\ };
\\}
\\
);
}
test "zig fmt: for while" {
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);
\\ };
\\}
\\
);
}
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;
\\
\\ const a: ?u8 = 10;
\\ const b: ?u8 = null;
\\ if (a) |v| {
\\ const some = v;
\\ } else if (b) |*v| {
\\ unreachable;
\\ } else {
\\ const some = 10;
\\ }
\\
\\ 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;
\\ }
\\}
\\
);
}
test "zig fmt: defer" {
try testCanonical(
\\test "defer" {
\\ defer foo();
\\ defer {
\\ bar();
\\ }
\\}
\\
);
}
test "zig fmt: comptime" {
try testCanonical(
\\fn foo() void {
\\ comptime {
\\ bar();
\\ }
\\}
\\
);
}
test "zig fmt: comptime block in container" {
try testCanonical(
\\const Foo = struct {
\\ comptime {
\\ @compileLog("hello comptime");
\\ }
\\};
\\
);
}
test "zig fmt: comment after empty comment" {
try testCanonical(
\\//
\\/// A doc comment
\\const a = b;
\\
);
}
test "zig fmt: comment after params" {
try testCanonical(
\\fn foo(
\\ a: i32, // comment
\\ b: i32, // comment
\\) void {}
\\
);
}
test "zig fmt: container doc comments" {
try testCanonical(
\\//! tld 1
\\//! tld 2
\\//! tld 3
\\const a = b;
\\
);
}
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: 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();
\\
\\ while (a) : (b)
\\ foo()
\\ else bar();
\\}
\\
,
\\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();
\\
\\ while (a) : (b)
\\ foo()
\\ else
\\ bar();
\\}
\\
);
}
test "zig fmt: fn type" {
try testCanonical(
\\fn a(i: u8) u8 {
\\ return i + 1;
\\}
\\
\\const a: fn (u8) u8 = undefined;
\\const b: fn (u8) callconv(.naked) u8 = undefined;
\\const ap: fn (u8) u8 = a;
\\
);
}
test "zig fmt: nosuspend" {
try testCanonical(
\\const a = nosuspend foo();
\\
);
}
test "zig fmt: Block after if" {
try testCanonical(
\\test {
\\ if (true) {
\\ const a = 0;
\\ }
\\
\\ {
\\ const a = 0;
\\ }
\\}
\\
);
}
test "zig fmt: string identifier" {
try testCanonical(
\\const @"a b" = @"c d".@"e f";
\\fn @"g h"() void {}
\\
);
}
test "zig fmt: error return" {
try testCanonical(
\\fn err() anyerror {
\\ call();
\\ return error.InvalidArgs;
\\}
\\
);
}
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"
\\ );
\\}
\\
,
\\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: 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),
\\ : .{});
\\}
\\
);
}
test "zig fmt: multiline string in array" {
try testCanonical(
\\const Foo = [][]const u8{
\\ \\aaa
\\ ,
\\ \\bbb
\\};
\\
\\fn bar() void {
\\ const Foo = [][]const u8{
\\ \\aaa
\\ ,
\\ \\bbb
\\ };
\\ const Bar = [][]const u8{ // comment here
\\ \\aaa
\\ \\
\\ , // and another comment can go here
\\ \\bbb
\\ };
\\}
\\
);
}
test "zig fmt: file ends with struct field" {
try testCanonical(
\\a: bool
\\
);
}
test "zig fmt: line comment in array" {
try testTransform(
\\test "a" {
\\ var arr = [_]u32{
\\ 0
\\ // 1,
\\ // 2,
\\ };
\\}
\\
,
\\test "a" {
\\ var arr = [_]u32{
\\ 0,
\\ // 1,
\\ // 2,
\\ };
\\}
\\
);
try testCanonical(
\\test "a" {
\\ var arr = [_]u32{
\\ 0,
\\ // 1,
\\ // 2,
\\ };
\\}
\\
);
}
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(
\\var bar = Bar{
\\ .x = 10, // test
\\ .y = "test"
\\ // test
\\};
\\
,
\\var bar = Bar{
\\ .x = 10, // test
\\ .y = "test",
\\ // test
\\};
\\
);
try testCanonical(
\\var bar = Bar{ // test
\\ .x = 10, // test
\\ .y = "test",
\\ // test
\\};
\\
);
}
test "zig fmt: remove newlines surrounding doc comment" {
try testTransform(
\\
\\
\\
\\/// doc comment
\\
\\fn foo() void {}
\\
,
\\/// doc comment
\\fn foo() void {}
\\
);
}
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 "Ast header smoke test" {
try std.testing.expectEqual(zigNode(c.AST_NODE_IF), Ast.Node.Tag.@"if");
}
test "my function" {
try testCanonical(
\\pub fn main() void {
\\ @panic("hello");
\\}
\\
);
}