Commit 3235eb03f9

Andrew Kelley <superjoe30@gmail.com>
2018-04-30 01:23:19
zig fmt: preserve same line comment after struct field
1 parent 3fa0bed
Changed files (2)
std/zig/parser.zig
@@ -376,7 +376,7 @@ pub const Parser = struct {
                                 .body_node = &block.base,
                             });
                             try root_node.decls.append(&test_node.base);
-                            stack.append(State { .Block = block }) catch unreachable;
+                            try stack.append(State { .Block = block });
                             try stack.append(State {
                                 .ExpectTokenSave = ExpectTokenSave {
                                     .id = Token.Id.LBrace,
@@ -616,14 +616,18 @@ pub const Parser = struct {
                 State.TopLevelExternOrField => |ctx| {
                     if (self.eatToken(Token.Id.Identifier)) |identifier| {
                         std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
-                        const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField,
-                            ast.Node.StructField {
-                                .base = undefined,
-                                .visib_token = ctx.visib_token,
-                                .name_token = identifier,
-                                .type_expr = undefined,
-                            }
-                        );
+                        const node = try arena.construct(ast.Node.StructField {
+                            .base = ast.Node {
+                                .id = ast.Node.Id.StructField,
+                                .before_comments = null,
+                                .same_line_comment = null,
+                            },
+                            .visib_token = ctx.visib_token,
+                            .name_token = identifier,
+                            .type_expr = undefined,
+                        });
+                        const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
+                        *node_ptr = &node.base;
 
                         stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
                         try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
@@ -706,16 +710,20 @@ pub const Parser = struct {
                         Token.Id.Identifier => {
                             switch (container_decl.kind) {
                                 ast.Node.ContainerDecl.Kind.Struct => {
-                                    const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField,
-                                        ast.Node.StructField {
-                                            .base = undefined,
-                                            .visib_token = null,
-                                            .name_token = token,
-                                            .type_expr = undefined,
-                                        }
-                                    );
+                                    const node = try arena.construct(ast.Node.StructField {
+                                        .base = ast.Node {
+                                            .id = ast.Node.Id.StructField,
+                                            .before_comments = null,
+                                            .same_line_comment = null,
+                                        },
+                                        .visib_token = null,
+                                        .name_token = token,
+                                        .type_expr = undefined,
+                                    });
+                                    const node_ptr = try container_decl.fields_and_decls.addOne();
+                                    *node_ptr = &node.base;
 
-                                    stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+                                    try stack.append(State { .FieldListCommaOrEnd = container_decl });
                                     try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
                                     try stack.append(State { .ExpectToken = Token.Id.Colon });
                                     continue;
@@ -1336,23 +1344,7 @@ pub const Parser = struct {
                 },
 
                 State.LookForSameLineComment => |node_ptr| {
-                    const node = *node_ptr;
-                    const node_last_token = node.lastToken();
-
-                    const line_comment_token = self.getNextToken();
-                    if (line_comment_token.id != Token.Id.LineComment) {
-                        self.putBackToken(line_comment_token);
-                        continue;
-                    }
-
-                    const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token);
-                    const different_line = offset_loc.line != 0;
-                    if (different_line) {
-                        self.putBackToken(line_comment_token);
-                        continue;
-                    }
-
-                    node.same_line_comment = try arena.construct(line_comment_token);
+                    try self.lookForSameLineComment(arena, *node_ptr);
                     continue;
                 },
 
@@ -1463,14 +1455,16 @@ pub const Parser = struct {
                         continue;
                     }
 
-                    const node = try self.createNode(arena, ast.Node.FieldInitializer,
-                        ast.Node.FieldInitializer {
-                            .base = undefined,
-                            .period_token = undefined,
-                            .name_token = undefined,
-                            .expr = undefined,
-                        }
-                    );
+                    const node = try arena.construct(ast.Node.FieldInitializer {
+                        .base = ast.Node {
+                            .id = ast.Node.Id.FieldInitializer,
+                            .before_comments = null,
+                            .same_line_comment = null,
+                        },
+                        .period_token = undefined,
+                        .name_token = undefined,
+                        .expr = undefined,
+                    });
                     try list_state.list.append(node);
 
                     stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
@@ -1504,7 +1498,8 @@ pub const Parser = struct {
                         container_decl.rbrace_token = end;
                         continue;
                     } else {
-                        stack.append(State { .ContainerDecl = container_decl }) catch unreachable;
+                        try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]);
+                        try stack.append(State { .ContainerDecl = container_decl });
                         continue;
                     }
                 },
@@ -2908,6 +2903,25 @@ pub const Parser = struct {
         }
     }
 
+    fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void {
+        const node_last_token = node.lastToken();
+
+        const line_comment_token = self.getNextToken();
+        if (line_comment_token.id != Token.Id.LineComment) {
+            self.putBackToken(line_comment_token);
+            return;
+        }
+
+        const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token);
+        const different_line = offset_loc.line != 0;
+        if (different_line) {
+            self.putBackToken(line_comment_token);
+            return;
+        }
+
+        node.same_line_comment = try arena.construct(line_comment_token);
+    }
+
     fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
         switch (token.id) {
             Token.Id.StringLiteral => {
@@ -3341,6 +3355,7 @@ pub const Parser = struct {
         while (stack.popOrNull()) |state| {
             switch (state) {
                 RenderState.TopLevelDecl => |decl| {
+                    try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } );
                     switch (decl.id) {
                         ast.Node.Id.FnProto => {
                             const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
@@ -3383,12 +3398,14 @@ pub const Parser = struct {
                                 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
                             }
                             try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token));
+                            try stack.append(RenderState { .Text = "," });
                             try stack.append(RenderState { .Expression = field.type_expr});
                         },
                         ast.Node.Id.UnionTag => {
                             const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
                             try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
 
+                            try stack.append(RenderState { .Text = "," });
                             if (tag.type_expr) |type_expr| {
                                 try stream.print(": ");
                                 try stack.append(RenderState { .Expression = type_expr});
@@ -3398,6 +3415,7 @@ pub const Parser = struct {
                             const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
                             try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
 
+                            try stack.append(RenderState { .Text = "," });
                             if (tag.value) |value| {
                                 try stream.print(" = ");
                                 try stack.append(RenderState { .Expression = value});
@@ -3899,14 +3917,6 @@ pub const Parser = struct {
                             while (i != 0) {
                                 i -= 1;
                                 const node = fields_and_decls[i];
-                                switch (node.id) {
-                                    ast.Node.Id.StructField,
-                                    ast.Node.Id.UnionTag,
-                                    ast.Node.Id.EnumTag => {
-                                        try stack.append(RenderState { .Text = "," });
-                                    },
-                                    else => { }
-                                }
                                 try stack.append(RenderState { .TopLevelDecl = node});
                                 try stack.append(RenderState.PrintIndent);
                                 try stack.append(RenderState {
@@ -4466,957 +4476,6 @@ pub const Parser = struct {
 
 };
 
-var fixed_buffer_mem: [100 * 1024]u8 = undefined;
-
-fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
-    var tokenizer = Tokenizer.init(source);
-    var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
-    defer parser.deinit();
-
-    var tree = try parser.parse();
-    defer tree.deinit();
-
-    var buffer = try std.Buffer.initSize(allocator, 0);
-    errdefer buffer.deinit();
-
-    var buffer_out_stream = io.BufferOutStream.init(&buffer);
-    try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
-    return buffer.toOwnedSlice();
-}
-
-fn testCanonical(source: []const u8) !void {
-    const needed_alloc_count = x: {
-        // Try it once with unlimited memory, make sure it works
-        var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
-        var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
-        const result_source = try testParse(source, &failing_allocator.allocator);
-        if (!mem.eql(u8, result_source, source)) {
-            warn("\n====== expected this output: =========\n");
-            warn("{}", source);
-            warn("\n======== instead found this: =========\n");
-            warn("{}", result_source);
-            warn("\n======================================\n");
-            return error.TestFailed;
-        }
-        failing_allocator.allocator.free(result_source);
-        break :x failing_allocator.index;
-    };
-
-    var fail_index: usize = 0;
-    while (fail_index < needed_alloc_count) : (fail_index += 1) {
-        var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
-        var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
-        if (testParse(source, &failing_allocator.allocator)) |_| {
-            return error.NondeterministicMemoryUsage;
-        } else |err| switch (err) {
-            error.OutOfMemory => {
-                if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
-                    warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
-                        fail_index, needed_alloc_count,
-                        failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
-                        failing_allocator.index, failing_allocator.deallocations);
-                    return error.MemoryLeakDetected;
-                }
-            },
-            error.ParseError => @panic("test failed"),
-        }
-    }
-}
-
-test "zig fmt: array literal with 1 item on 1 line" {
-    try testCanonical(
-        \\var s = []const u64 {0} ** 25;
-        \\
-    );
-}
-
-test "zig fmt: preserve 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: preserve comments before global variables" {
-    try testCanonical(
-        \\/// Foo copies keys and values before they go into the map, and
-        \\/// frees them when they get removed.
-        \\pub const Foo = struct {};
-        \\
-    );
-}
-
-test "zig fmt: preserve comments before statements" {
-    try testCanonical(
-        \\test "std" {
-        \\    // statement comment
-        \\    _ = @import("foo/bar.zig");
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: preserve top level comments" {
-    try testCanonical(
-        \\// top level comment
-        \\test "hi" {}
-        \\
-    );
-}
-
-test "zig fmt: get stdout or fail" {
-    try testCanonical(
-        \\const std = @import("std");
-        \\
-        \\pub fn main() !void {
-        \\    // If this program is run without stdout attached, exit with an error.
-        \\    // another comment
-        \\    var stdout_file = try std.io.getStdOut;
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: preserve spacing" {
-    try testCanonical(
-        \\const std = @import("std");
-        \\
-        \\pub fn main() !void {
-        \\    var stdout_file = try std.io.getStdOut;
-        \\    var stdout_file = try std.io.getStdOut;
-        \\
-        \\    var stdout_file = try std.io.getStdOut;
-        \\    var stdout_file = try std.io.getStdOut;
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: return types" {
-    try testCanonical(
-        \\pub fn main() !void {}
-        \\pub fn main() var {}
-        \\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: pointer attributes" {
-    try testCanonical(
-        \\extern fn f1(s: &align(&u8) u8) c_int;
-        \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
-        \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
-        \\extern fn f4(s: &align(1) const volatile u8) c_int;
-        \\
-    );
-}
-
-test "zig fmt: slice attributes" {
-    try testCanonical(
-        \\extern fn f1(s: &align(&u8) u8) c_int;
-        \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
-        \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
-        \\extern fn f4(s: &align(1) const volatile u8) c_int;
-        \\
-    );
-}
-
-test "zig fmt: test declaration" {
-     try testCanonical(
-        \\test "test name" {
-        \\    const a = 1;
-        \\    var b = 1;
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: infix operators" {
-    try testCanonical(
-        \\test "infix operators" {
-        \\    var i = undefined;
-        \\    i = 2;
-        \\    i *= 2;
-        \\    i |= 2;
-        \\    i ^= 2;
-        \\    i <<= 2;
-        \\    i >>= 2;
-        \\    i &= 2;
-        \\    i *= 2;
-        \\    i *%= 2;
-        \\    i -= 2;
-        \\    i -%= 2;
-        \\    i += 2;
-        \\    i +%= 2;
-        \\    i /= 2;
-        \\    i %= 2;
-        \\    _ = i == i;
-        \\    _ = i != i;
-        \\    _ = i != i;
-        \\    _ = i.i;
-        \\    _ = i || i;
-        \\    _ = i!i;
-        \\    _ = i ** i;
-        \\    _ = i ++ i;
-        \\    _ = i ?? i;
-        \\    _ = i % i;
-        \\    _ = i / i;
-        \\    _ = i *% i;
-        \\    _ = i * i;
-        \\    _ = i -% i;
-        \\    _ = i - i;
-        \\    _ = i +% i;
-        \\    _ = i + i;
-        \\    _ = i << i;
-        \\    _ = i >> i;
-        \\    _ = i & i;
-        \\    _ = i ^ i;
-        \\    _ = i | i;
-        \\    _ = i >= i;
-        \\    _ = i <= i;
-        \\    _ = i > i;
-        \\    _ = i < i;
-        \\    _ = i and i;
-        \\    _ = i or i;
-        \\}
-        \\
-    );
-}
-
-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;
-        \\}
-        \\
-    );
-}
-
-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: var args" {
-    try testCanonical(
-        \\fn print(args: ...) void {}
-        \\
-    );
-}
-
-test "zig fmt: var type" {
-    try testCanonical(
-        \\fn print(args: var) var {}
-        \\const Var = var;
-        \\const i: var = 0;
-        \\
-    );
-}
-
-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;
-        \\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 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;
-        \\
-    );
-}
-
-test "zig fmt: multiline string" {
-    try testCanonical(
-        \\const s = 
-        \\    \\ something
-        \\    \\ something else
-        \\    ;
-        \\
-    );
-}
-
-test "zig fmt: values" {
-    try testCanonical(
-        \\test "values" {
-        \\    1;
-        \\    1.0;
-        \\    "string";
-        \\    c"cstring";
-        \\    'c';
-        \\    true;
-        \\    false;
-        \\    null;
-        \\    undefined;
-        \\    error;
-        \\    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,
-        \\    pub f3: u8,
-        \\
-        \\    fn method(self: &Self) Self {
-        \\        return *self;
-        \\    }
-        \\
-        \\    f2: u8,
-        \\};
-        \\
-        \\const Ps = packed struct {
-        \\    a: u8,
-        \\    pub b: u8,
-        \\
-        \\    c: u8,
-        \\};
-        \\
-        \\const Es = extern struct {
-        \\    a: u8,
-        \\    pub 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: error set declaration" {
-      try testCanonical(
-        \\const E = error {
-        \\    A,
-        \\    B,
-        \\
-        \\    C,
-        \\};
-        \\
-    );
-}
-
-test "zig fmt: arrays" {
-    try testCanonical(
-        \\test "test array" {
-        \\    const a: [2]u8 = [2]u8 {
-        \\        1,
-        \\        2,
-        \\    };
-        \\    const a: [2]u8 = []u8 {
-        \\        1,
-        \\        2,
-        \\    };
-        \\    const a: [0]u8 = []u8{};
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: container initializers" {
-    try testCanonical(
-        \\const a1 = []u8{};
-        \\const a2 = []u8 {
-        \\    1,
-        \\    2,
-        \\    3,
-        \\    4,
-        \\};
-        \\const s1 = S{};
-        \\const s2 = S {
-        \\    .a = 1,
-        \\    .b = 2,
-        \\};
-        \\
-    );
-}
-
-test "zig fmt: catch" {
-    try testCanonical(
-        \\test "catch" {
-        \\    const a: error!u8 = 0;
-        \\    _ = a catch return;
-        \\    _ = a catch |err| return;
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: blocks" {
-    try testCanonical(
-        \\test "blocks" {
-        \\    {
-        \\        const a = 0;
-        \\        const b = 0;
-        \\    }
-        \\
-        \\    blk: {
-        \\        const a = 0;
-        \\        const b = 0;
-        \\    }
-        \\
-        \\    const r = blk: {
-        \\        const a = 0;
-        \\        const b = 0;
-        \\    };
-        \\}
-        \\
-    );
-}
-
-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,
-        \\    };
-        \\
-        \\    const u = Union {
-        \\        .Int = 0,
-        \\    };
-        \\    switch (u) {
-        \\        Union.Int => |int| {},
-        \\        Union.Float => |*float| unreachable,
-        \\    }
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: while" {
-    try testCanonical(
-        \\test "while" {
-        \\    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;
-        \\    }
-        \\
-        \\    var a: ?u8 = 2;
-        \\    while (a) |v| : (a = null) {
-        \\        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: error!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" {
-        \\    const a = []u8 {
-        \\        1,
-        \\        2,
-        \\        3,
-        \\    };
-        \\    for (a) |v| {
-        \\        continue;
-        \\    }
-        \\
-        \\    for (a) |v|
-        \\        continue;
-        \\
-        \\    for (a) |*v|
-        \\        continue;
-        \\
-        \\    for (a) |v, i| {
-        \\        continue;
-        \\    }
-        \\
-        \\    for (a) |v, i|
-        \\        continue;
-        \\
-        \\    const res = for (a) |v, i| {
-        \\        break v;
-        \\    } else {
-        \\        unreachable;
-        \\    };
-        \\
-        \\    var num: usize = 0;
-        \\    inline for (a) |v, i| {
-        \\        num += v;
-        \\        num += i;
-        \\    }
-        \\}
-        \\
-    );
-}
-
-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: error!u8 = 0;
-        \\    if (a_err) |v| {
-        \\        const p = v;
-        \\    } else |err| {
-        \\        unreachable;
-        \\    }
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: defer" {
-    try testCanonical(
-        \\test "defer" {
-        \\    var i: usize = 0;
-        \\    defer i = 1;
-        \\    defer {
-        \\        i += 2;
-        \\        i *= i;
-        \\    }
-        \\
-        \\    errdefer i += 3;
-        \\    errdefer {
-        \\        i += 2;
-        \\        i /= i;
-        \\    }
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: comptime" {
-    try testCanonical(
-        \\fn a() u8 {
-        \\    return 5;
-        \\}
-        \\
-        \\fn b(comptime i: u8) u8 {
-        \\    return i;
-        \\}
-        \\
-        \\const av = comptime a();
-        \\const av2 = comptime blk: {
-        \\    var res = a();
-        \\    res *= b(2);
-        \\    break :blk res;
-        \\};
-        \\
-        \\comptime {
-        \\    _ = a();
-        \\}
-        \\
-        \\test "comptime" {
-        \\    const av3 = comptime a();
-        \\    const av4 = comptime blk: {
-        \\        var res = a();
-        \\        res *= a();
-        \\        break :blk res;
-        \\    };
-        \\
-        \\    comptime var i = 0;
-        \\    comptime {
-        \\        i = a();
-        \\        i += b(i);
-        \\    }
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: fn type" {
-    try testCanonical(
-        \\fn a(i: u8) u8 {
-        \\    return i + 1;
-        \\}
-        \\
-        \\const a: fn(u8) u8 = undefined;
-        \\const b: extern fn(u8) u8 = undefined;
-        \\const c: nakedcc fn(u8) u8 = undefined;
-        \\const ap: fn(u8) u8 = a;
-        \\
-    );
-}
-
-test "zig fmt: inline asm" {
-    try testCanonical(
-        \\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: coroutines" {
-    try testCanonical(
-        \\async fn simpleAsyncFn() void {
-        \\    const a = async a.b();
-        \\    x += 1;
-        \\    suspend;
-        \\    x += 1;
-        \\    suspend |p| {}
-        \\    const p = async simpleAsyncFn() catch unreachable;
-        \\    await p;
-        \\}
-        \\
-        \\test "coroutine suspend, resume, cancel" {
-        \\    const p = try async<std.debug.global_allocator> testAsyncSeq();
-        \\    resume p;
-        \\    cancel p;
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: Block after if" {
-    try testCanonical(
-        \\test "Block after if" {
-        \\    if (true) {
-        \\        const a = 0;
-        \\    }
-        \\
-        \\    {
-        \\        const a = 0;
-        \\    }
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: use" {
-    try testCanonical(
-        \\use @import("std");
-        \\pub use @import("std");
-        \\
-    );
-}
-
-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() error {
-        \\    call();
-        \\    return error.InvalidArgs;
-        \\}
-        \\
-    );
-}
-
-test "zig fmt: struct literals with fields on each line" {
-    try testCanonical(
-        \\var self = BufSet {
-        \\    .hash_map = BufSetHashMap.init(a),
-        \\};
-        \\
-    );
+test "std.zig.parser" {
+    _ = @import("parser_test.zig");
 }
std/zig/parser_test.zig
@@ -0,0 +1,975 @@
+test "zig fmt: 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: array literal with 1 item on 1 line" {
+    try testCanonical(
+        \\var s = []const u64 {0} ** 25;
+        \\
+    );
+}
+
+test "zig fmt: preserve 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: preserve comments before global variables" {
+    try testCanonical(
+        \\/// Foo copies keys and values before they go into the map, and
+        \\/// frees them when they get removed.
+        \\pub const Foo = struct {};
+        \\
+    );
+}
+
+test "zig fmt: preserve comments before statements" {
+    try testCanonical(
+        \\test "std" {
+        \\    // statement comment
+        \\    _ = @import("foo/bar.zig");
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: preserve top level comments" {
+    try testCanonical(
+        \\// top level comment
+        \\test "hi" {}
+        \\
+    );
+}
+
+test "zig fmt: get stdout or fail" {
+    try testCanonical(
+        \\const std = @import("std");
+        \\
+        \\pub fn main() !void {
+        \\    // If this program is run without stdout attached, exit with an error.
+        \\    // another comment
+        \\    var stdout_file = try std.io.getStdOut;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: preserve spacing" {
+    try testCanonical(
+        \\const std = @import("std");
+        \\
+        \\pub fn main() !void {
+        \\    var stdout_file = try std.io.getStdOut;
+        \\    var stdout_file = try std.io.getStdOut;
+        \\
+        \\    var stdout_file = try std.io.getStdOut;
+        \\    var stdout_file = try std.io.getStdOut;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: return types" {
+    try testCanonical(
+        \\pub fn main() !void {}
+        \\pub fn main() var {}
+        \\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: pointer attributes" {
+    try testCanonical(
+        \\extern fn f1(s: &align(&u8) u8) c_int;
+        \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+        \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+        \\extern fn f4(s: &align(1) const volatile u8) c_int;
+        \\
+    );
+}
+
+test "zig fmt: slice attributes" {
+    try testCanonical(
+        \\extern fn f1(s: &align(&u8) u8) c_int;
+        \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+        \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+        \\extern fn f4(s: &align(1) const volatile u8) c_int;
+        \\
+    );
+}
+
+test "zig fmt: test declaration" {
+     try testCanonical(
+        \\test "test name" {
+        \\    const a = 1;
+        \\    var b = 1;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: infix operators" {
+    try testCanonical(
+        \\test "infix operators" {
+        \\    var i = undefined;
+        \\    i = 2;
+        \\    i *= 2;
+        \\    i |= 2;
+        \\    i ^= 2;
+        \\    i <<= 2;
+        \\    i >>= 2;
+        \\    i &= 2;
+        \\    i *= 2;
+        \\    i *%= 2;
+        \\    i -= 2;
+        \\    i -%= 2;
+        \\    i += 2;
+        \\    i +%= 2;
+        \\    i /= 2;
+        \\    i %= 2;
+        \\    _ = i == i;
+        \\    _ = i != i;
+        \\    _ = i != i;
+        \\    _ = i.i;
+        \\    _ = i || i;
+        \\    _ = i!i;
+        \\    _ = i ** i;
+        \\    _ = i ++ i;
+        \\    _ = i ?? i;
+        \\    _ = i % i;
+        \\    _ = i / i;
+        \\    _ = i *% i;
+        \\    _ = i * i;
+        \\    _ = i -% i;
+        \\    _ = i - i;
+        \\    _ = i +% i;
+        \\    _ = i + i;
+        \\    _ = i << i;
+        \\    _ = i >> i;
+        \\    _ = i & i;
+        \\    _ = i ^ i;
+        \\    _ = i | i;
+        \\    _ = i >= i;
+        \\    _ = i <= i;
+        \\    _ = i > i;
+        \\    _ = i < i;
+        \\    _ = i and i;
+        \\    _ = i or i;
+        \\}
+        \\
+    );
+}
+
+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;
+        \\}
+        \\
+    );
+}
+
+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: var args" {
+    try testCanonical(
+        \\fn print(args: ...) void {}
+        \\
+    );
+}
+
+test "zig fmt: var type" {
+    try testCanonical(
+        \\fn print(args: var) var {}
+        \\const Var = var;
+        \\const i: var = 0;
+        \\
+    );
+}
+
+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;
+        \\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 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;
+        \\
+    );
+}
+
+test "zig fmt: multiline string" {
+    try testCanonical(
+        \\const s = 
+        \\    \\ something
+        \\    \\ something else
+        \\    ;
+        \\
+    );
+}
+
+test "zig fmt: values" {
+    try testCanonical(
+        \\test "values" {
+        \\    1;
+        \\    1.0;
+        \\    "string";
+        \\    c"cstring";
+        \\    'c';
+        \\    true;
+        \\    false;
+        \\    null;
+        \\    undefined;
+        \\    error;
+        \\    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,
+        \\    pub f3: u8,
+        \\
+        \\    fn method(self: &Self) Self {
+        \\        return *self;
+        \\    }
+        \\
+        \\    f2: u8,
+        \\};
+        \\
+        \\const Ps = packed struct {
+        \\    a: u8,
+        \\    pub b: u8,
+        \\
+        \\    c: u8,
+        \\};
+        \\
+        \\const Es = extern struct {
+        \\    a: u8,
+        \\    pub 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: error set declaration" {
+      try testCanonical(
+        \\const E = error {
+        \\    A,
+        \\    B,
+        \\
+        \\    C,
+        \\};
+        \\
+    );
+}
+
+test "zig fmt: arrays" {
+    try testCanonical(
+        \\test "test array" {
+        \\    const a: [2]u8 = [2]u8 {
+        \\        1,
+        \\        2,
+        \\    };
+        \\    const a: [2]u8 = []u8 {
+        \\        1,
+        \\        2,
+        \\    };
+        \\    const a: [0]u8 = []u8{};
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: container initializers" {
+    try testCanonical(
+        \\const a1 = []u8{};
+        \\const a2 = []u8 {
+        \\    1,
+        \\    2,
+        \\    3,
+        \\    4,
+        \\};
+        \\const s1 = S{};
+        \\const s2 = S {
+        \\    .a = 1,
+        \\    .b = 2,
+        \\};
+        \\
+    );
+}
+
+test "zig fmt: catch" {
+    try testCanonical(
+        \\test "catch" {
+        \\    const a: error!u8 = 0;
+        \\    _ = a catch return;
+        \\    _ = a catch |err| return;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: blocks" {
+    try testCanonical(
+        \\test "blocks" {
+        \\    {
+        \\        const a = 0;
+        \\        const b = 0;
+        \\    }
+        \\
+        \\    blk: {
+        \\        const a = 0;
+        \\        const b = 0;
+        \\    }
+        \\
+        \\    const r = blk: {
+        \\        const a = 0;
+        \\        const b = 0;
+        \\    };
+        \\}
+        \\
+    );
+}
+
+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,
+        \\    };
+        \\
+        \\    const u = Union {
+        \\        .Int = 0,
+        \\    };
+        \\    switch (u) {
+        \\        Union.Int => |int| {},
+        \\        Union.Float => |*float| unreachable,
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: while" {
+    try testCanonical(
+        \\test "while" {
+        \\    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;
+        \\    }
+        \\
+        \\    var a: ?u8 = 2;
+        \\    while (a) |v| : (a = null) {
+        \\        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: error!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" {
+        \\    const a = []u8 {
+        \\        1,
+        \\        2,
+        \\        3,
+        \\    };
+        \\    for (a) |v| {
+        \\        continue;
+        \\    }
+        \\
+        \\    for (a) |v|
+        \\        continue;
+        \\
+        \\    for (a) |*v|
+        \\        continue;
+        \\
+        \\    for (a) |v, i| {
+        \\        continue;
+        \\    }
+        \\
+        \\    for (a) |v, i|
+        \\        continue;
+        \\
+        \\    const res = for (a) |v, i| {
+        \\        break v;
+        \\    } else {
+        \\        unreachable;
+        \\    };
+        \\
+        \\    var num: usize = 0;
+        \\    inline for (a) |v, i| {
+        \\        num += v;
+        \\        num += i;
+        \\    }
+        \\}
+        \\
+    );
+}
+
+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: error!u8 = 0;
+        \\    if (a_err) |v| {
+        \\        const p = v;
+        \\    } else |err| {
+        \\        unreachable;
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: defer" {
+    try testCanonical(
+        \\test "defer" {
+        \\    var i: usize = 0;
+        \\    defer i = 1;
+        \\    defer {
+        \\        i += 2;
+        \\        i *= i;
+        \\    }
+        \\
+        \\    errdefer i += 3;
+        \\    errdefer {
+        \\        i += 2;
+        \\        i /= i;
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: comptime" {
+    try testCanonical(
+        \\fn a() u8 {
+        \\    return 5;
+        \\}
+        \\
+        \\fn b(comptime i: u8) u8 {
+        \\    return i;
+        \\}
+        \\
+        \\const av = comptime a();
+        \\const av2 = comptime blk: {
+        \\    var res = a();
+        \\    res *= b(2);
+        \\    break :blk res;
+        \\};
+        \\
+        \\comptime {
+        \\    _ = a();
+        \\}
+        \\
+        \\test "comptime" {
+        \\    const av3 = comptime a();
+        \\    const av4 = comptime blk: {
+        \\        var res = a();
+        \\        res *= a();
+        \\        break :blk res;
+        \\    };
+        \\
+        \\    comptime var i = 0;
+        \\    comptime {
+        \\        i = a();
+        \\        i += b(i);
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: fn type" {
+    try testCanonical(
+        \\fn a(i: u8) u8 {
+        \\    return i + 1;
+        \\}
+        \\
+        \\const a: fn(u8) u8 = undefined;
+        \\const b: extern fn(u8) u8 = undefined;
+        \\const c: nakedcc fn(u8) u8 = undefined;
+        \\const ap: fn(u8) u8 = a;
+        \\
+    );
+}
+
+test "zig fmt: inline asm" {
+    try testCanonical(
+        \\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: coroutines" {
+    try testCanonical(
+        \\async fn simpleAsyncFn() void {
+        \\    const a = async a.b();
+        \\    x += 1;
+        \\    suspend;
+        \\    x += 1;
+        \\    suspend |p| {}
+        \\    const p = async simpleAsyncFn() catch unreachable;
+        \\    await p;
+        \\}
+        \\
+        \\test "coroutine suspend, resume, cancel" {
+        \\    const p = try async<std.debug.global_allocator> testAsyncSeq();
+        \\    resume p;
+        \\    cancel p;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: Block after if" {
+    try testCanonical(
+        \\test "Block after if" {
+        \\    if (true) {
+        \\        const a = 0;
+        \\    }
+        \\
+        \\    {
+        \\        const a = 0;
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: use" {
+    try testCanonical(
+        \\use @import("std");
+        \\pub use @import("std");
+        \\
+    );
+}
+
+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() error {
+        \\    call();
+        \\    return error.InvalidArgs;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: struct literals with fields on each line" {
+    try testCanonical(
+        \\var self = BufSet {
+        \\    .hash_map = BufSetHashMap.init(a),
+        \\};
+        \\
+    );
+}
+
+const std = @import("std");
+const mem = std.mem;
+const warn = std.debug.warn;
+const Tokenizer = std.zig.Tokenizer;
+const Parser = std.zig.Parser;
+const io = std.io;
+
+var fixed_buffer_mem: [100 * 1024]u8 = undefined;
+
+fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
+    var tokenizer = Tokenizer.init(source);
+    var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
+    defer parser.deinit();
+
+    var tree = try parser.parse();
+    defer tree.deinit();
+
+    var buffer = try std.Buffer.initSize(allocator, 0);
+    errdefer buffer.deinit();
+
+    var buffer_out_stream = io.BufferOutStream.init(&buffer);
+    try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
+    return buffer.toOwnedSlice();
+}
+
+fn testCanonical(source: []const u8) !void {
+    const needed_alloc_count = x: {
+        // Try it once with unlimited memory, make sure it works
+        var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+        var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
+        const result_source = try testParse(source, &failing_allocator.allocator);
+        if (!mem.eql(u8, result_source, source)) {
+            warn("\n====== expected this output: =========\n");
+            warn("{}", source);
+            warn("\n======== instead found this: =========\n");
+            warn("{}", result_source);
+            warn("\n======================================\n");
+            return error.TestFailed;
+        }
+        failing_allocator.allocator.free(result_source);
+        break :x failing_allocator.index;
+    };
+
+    var fail_index: usize = 0;
+    while (fail_index < needed_alloc_count) : (fail_index += 1) {
+        var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+        var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
+        if (testParse(source, &failing_allocator.allocator)) |_| {
+            return error.NondeterministicMemoryUsage;
+        } else |err| switch (err) {
+            error.OutOfMemory => {
+                if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
+                    warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
+                        fail_index, needed_alloc_count,
+                        failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
+                        failing_allocator.index, failing_allocator.deallocations);
+                    return error.MemoryLeakDetected;
+                }
+            },
+            error.ParseError => @panic("test failed"),
+        }
+    }
+}
+