Commit b7095912c7

Andrew Kelley <superjoe30@gmail.com>
2018-04-29 21:48:53
zig fmt: respect comments before statements
1 parent f37e79e
Changed files (3)
std/zig/ast.zig
@@ -6,6 +6,7 @@ const mem = std.mem;
 
 pub const Node = struct {
     id: Id,
+    comments: ?&LineComment,
 
     pub const Id = enum {
         // Top level
@@ -139,7 +140,6 @@ pub const Node = struct {
 
     pub const VarDecl = struct {
         base: Node,
-        comments: ?&LineComment,
         visib_token: ?Token,
         name_token: Token,
         eq_token: Token,
@@ -421,7 +421,6 @@ pub const Node = struct {
 
     pub const FnProto = struct {
         base: Node,
-        comments: ?&LineComment,
         visib_token: ?Token,
         fn_token: Token,
         name_token: ?Token,
@@ -1732,7 +1731,6 @@ pub const Node = struct {
 
     pub const TestDecl = struct {
         base: Node,
-        comments: ?&LineComment,
         test_token: Token,
         name: &Node,
         body_node: &Node,
std/zig/parser.zig
@@ -182,6 +182,11 @@ pub const Parser = struct {
         }
     };
 
+    const AddCommentsCtx = struct {
+        node_ptr: &&ast.Node,
+        comments: ?&ast.Node.LineComment,
+    };
+
     const State = union(enum) {
         TopLevel,
         TopLevelExtern: TopLevelDeclCtx,
@@ -221,6 +226,7 @@ pub const Parser = struct {
         Statement: &ast.Node.Block,
         ComptimeStatement: ComptimeStatementCtx,
         Semicolon: &&ast.Node,
+        AddComments: AddCommentsCtx,
 
         AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
         AsmOutputReturnOrType: &ast.Node.AsmOutput,
@@ -345,24 +351,26 @@ pub const Parser = struct {
                         Token.Id.Keyword_test => {
                             stack.append(State.TopLevel) catch unreachable;
 
-                            const block = try self.createNode(arena, ast.Node.Block,
-                                ast.Node.Block {
-                                    .base = undefined,
-                                    .label = null,
-                                    .lbrace = undefined,
-                                    .statements = ArrayList(&ast.Node).init(arena),
-                                    .rbrace = undefined,
-                                }
-                            );
-                            const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl,
-                                ast.Node.TestDecl {
-                                    .base = undefined,
+                            const block = try arena.construct(ast.Node.Block {
+                                .base = ast.Node {
+                                    .id = ast.Node.Id.Block,
+                                    .comments = null,
+                                },
+                                .label = null,
+                                .lbrace = undefined,
+                                .statements = ArrayList(&ast.Node).init(arena),
+                                .rbrace = undefined,
+                            });
+                            const test_node = try arena.construct(ast.Node.TestDecl {
+                                .base = ast.Node {
+                                    .id = ast.Node.Id.TestDecl,
                                     .comments = comments,
-                                    .test_token = token,
-                                    .name = undefined,
-                                    .body_node = &block.base,
-                                }
-                            );
+                                },
+                                .test_token = token,
+                                .name = undefined,
+                                .body_node = &block.base,
+                            });
+                            try root_node.decls.append(&test_node.base);
                             stack.append(State { .Block = block }) catch unreachable;
                             try stack.append(State {
                                 .ExpectTokenSave = ExpectTokenSave {
@@ -530,24 +538,25 @@ pub const Parser = struct {
                         },
                         Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
                         Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
-                            const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto,
-                                ast.Node.FnProto {
-                                    .base = undefined,
+                            const fn_proto = try arena.construct(ast.Node.FnProto {
+                                .base = ast.Node {
+                                    .id = ast.Node.Id.FnProto,
                                     .comments = comments,
-                                    .visib_token = ctx.visib_token,
-                                    .name_token = null,
-                                    .fn_token = undefined,
-                                    .params = ArrayList(&ast.Node).init(arena),
-                                    .return_type = undefined,
-                                    .var_args_token = null,
-                                    .extern_export_inline_token = ctx.extern_export_inline_token,
-                                    .cc_token = null,
-                                    .async_attr = null,
-                                    .body_node = null,
-                                    .lib_name = ctx.lib_name,
-                                    .align_expr = null,
-                                }
-                            );
+                                },
+                                .visib_token = ctx.visib_token,
+                                .name_token = null,
+                                .fn_token = undefined,
+                                .params = ArrayList(&ast.Node).init(arena),
+                                .return_type = undefined,
+                                .var_args_token = null,
+                                .extern_export_inline_token = ctx.extern_export_inline_token,
+                                .cc_token = null,
+                                .async_attr = null,
+                                .body_node = null,
+                                .lib_name = ctx.lib_name,
+                                .align_expr = null,
+                            });
+                            try ctx.decls.append(&fn_proto.base);
                             stack.append(State { .FnDef = fn_proto }) catch unreachable;
                             try stack.append(State { .FnProto = fn_proto });
 
@@ -789,24 +798,25 @@ pub const Parser = struct {
 
 
                 State.VarDecl => |ctx| {
-                    const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl,
-                        ast.Node.VarDecl {
-                            .base = undefined,
+                    const var_decl = try arena.construct(ast.Node.VarDecl {
+                        .base = ast.Node {
+                            .id = ast.Node.Id.VarDecl,
                             .comments = ctx.comments,
-                            .visib_token = ctx.visib_token,
-                            .mut_token = ctx.mut_token,
-                            .comptime_token = ctx.comptime_token,
-                            .extern_export_token = ctx.extern_export_token,
-                            .type_node = null,
-                            .align_node = null,
-                            .init_node = null,
-                            .lib_name = ctx.lib_name,
-                            // initialized later
-                            .name_token = undefined,
-                            .eq_token = undefined,
-                            .semicolon_token = undefined,
-                        }
-                    );
+                        },
+                        .visib_token = ctx.visib_token,
+                        .mut_token = ctx.mut_token,
+                        .comptime_token = ctx.comptime_token,
+                        .extern_export_token = ctx.extern_export_token,
+                        .type_node = null,
+                        .align_node = null,
+                        .init_node = null,
+                        .lib_name = ctx.lib_name,
+                        // initialized later
+                        .name_token = undefined,
+                        .eq_token = undefined,
+                        .semicolon_token = undefined,
+                    });
+                    try ctx.list.append(&var_decl.base);
 
                     stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
                     try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
@@ -1218,19 +1228,23 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
-                            const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer,
-                                ast.Node.Defer {
-                                    .base = undefined,
-                                    .defer_token = token,
-                                    .kind = switch (token.id) {
-                                        Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
-                                        Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
-                                        else => unreachable,
-                                    },
-                                    .expr = undefined,
-                                }
-                            );
-                            stack.append(State { .Semicolon = &&node.base }) catch unreachable;
+                            const node = try arena.construct(ast.Node.Defer {
+                                .base = ast.Node {
+                                    .id = ast.Node.Id.Defer,
+                                    .comments = comments,
+                                },
+                                .defer_token = token,
+                                .kind = switch (token.id) {
+                                    Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
+                                    Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
+                                    else => unreachable,
+                                },
+                                .expr = undefined,
+                            });
+                            const node_ptr = try block.statements.addOne();
+                            *node_ptr = &node.base;
+
+                            stack.append(State { .Semicolon = node_ptr }) catch unreachable;
                             try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
                             continue;
                         },
@@ -1249,9 +1263,13 @@ pub const Parser = struct {
                         },
                         else => {
                             self.putBackToken(token);
-                            const statememt = try block.statements.addOne();
-                            stack.append(State { .Semicolon = statememt }) catch unreachable;
-                            try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } });
+                            const statement = try block.statements.addOne();
+                            stack.append(State { .Semicolon = statement }) catch unreachable;
+                            try stack.append(State { .AddComments = AddCommentsCtx {
+                                .node_ptr = statement,
+                                .comments = comments,
+                            }});
+                            try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
                             continue;
                         }
                     }
@@ -1293,6 +1311,12 @@ pub const Parser = struct {
                     continue;
                 },
 
+                State.AddComments => |add_comments_ctx| {
+                    const node = *add_comments_ctx.node_ptr;
+                    node.comments = add_comments_ctx.comments;
+                    continue;
+                },
+
 
                 State.AsmOutputItems => |items| {
                     const lbracket = self.getNextToken();
@@ -1576,24 +1600,25 @@ pub const Parser = struct {
 
                 State.ExternType => |ctx| {
                     if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| {
-                        const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto,
-                            ast.Node.FnProto {
-                                .base = undefined,
+                        const fn_proto = try arena.construct(ast.Node.FnProto {
+                            .base = ast.Node {
+                                .id = ast.Node.Id.FnProto,
                                 .comments = ctx.comments,
-                                .visib_token = null,
-                                .name_token = null,
-                                .fn_token = fn_token,
-                                .params = ArrayList(&ast.Node).init(arena),
-                                .return_type = undefined,
-                                .var_args_token = null,
-                                .extern_export_inline_token = ctx.extern_token,
-                                .cc_token = null,
-                                .async_attr = null,
-                                .body_node = null,
-                                .lib_name = null,
-                                .align_expr = null,
-                            }
-                        );
+                            },
+                            .visib_token = null,
+                            .name_token = null,
+                            .fn_token = fn_token,
+                            .params = ArrayList(&ast.Node).init(arena),
+                            .return_type = undefined,
+                            .var_args_token = null,
+                            .extern_export_inline_token = ctx.extern_token,
+                            .cc_token = null,
+                            .async_attr = null,
+                            .body_node = null,
+                            .lib_name = null,
+                            .align_expr = null,
+                        });
+                        ctx.opt_ctx.store(&fn_proto.base);
                         stack.append(State { .FnProto = fn_proto }) catch unreachable;
                         continue;
                     }
@@ -2546,46 +2571,48 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_fn => {
-                            const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
-                                ast.Node.FnProto {
-                                    .base = undefined,
+                            const fn_proto = try arena.construct(ast.Node.FnProto {
+                                .base = ast.Node {
+                                    .id = ast.Node.Id.FnProto,
                                     .comments = null,
-                                    .visib_token = null,
-                                    .name_token = null,
-                                    .fn_token = token,
-                                    .params = ArrayList(&ast.Node).init(arena),
-                                    .return_type = undefined,
-                                    .var_args_token = null,
-                                    .extern_export_inline_token = null,
-                                    .cc_token = null,
-                                    .async_attr = null,
-                                    .body_node = null,
-                                    .lib_name = null,
-                                    .align_expr = null,
-                                }
-                            );
+                                },
+                                .visib_token = null,
+                                .name_token = null,
+                                .fn_token = token,
+                                .params = ArrayList(&ast.Node).init(arena),
+                                .return_type = undefined,
+                                .var_args_token = null,
+                                .extern_export_inline_token = null,
+                                .cc_token = null,
+                                .async_attr = null,
+                                .body_node = null,
+                                .lib_name = null,
+                                .align_expr = null,
+                            });
+                            opt_ctx.store(&fn_proto.base);
                             stack.append(State { .FnProto = fn_proto }) catch unreachable;
                             continue;
                         },
                         Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
-                            const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
-                                ast.Node.FnProto {
-                                    .base = undefined,
+                            const fn_proto = try arena.construct(ast.Node.FnProto {
+                                .base = ast.Node {
+                                    .id = ast.Node.Id.FnProto,
                                     .comments = null,
-                                    .visib_token = null,
-                                    .name_token = null,
-                                    .fn_token = undefined,
-                                    .params = ArrayList(&ast.Node).init(arena),
-                                    .return_type = undefined,
-                                    .var_args_token = null,
-                                    .extern_export_inline_token = null,
-                                    .cc_token = token,
-                                    .async_attr = null,
-                                    .body_node = null,
-                                    .lib_name = null,
-                                    .align_expr = null,
-                                }
-                            );
+                                },
+                                .visib_token = null,
+                                .name_token = null,
+                                .fn_token = undefined,
+                                .params = ArrayList(&ast.Node).init(arena),
+                                .return_type = undefined,
+                                .var_args_token = null,
+                                .extern_export_inline_token = null,
+                                .cc_token = token,
+                                .async_attr = null,
+                                .body_node = null,
+                                .lib_name = null,
+                                .align_expr = null,
+                            });
+                            opt_ctx.store(&fn_proto.base);
                             stack.append(State { .FnProto = fn_proto }) catch unreachable;
                             try stack.append(State {
                                 .ExpectTokenSave = ExpectTokenSave {
@@ -2747,13 +2774,13 @@ pub const Parser = struct {
                     if (result) |comment_node| {
                         break :blk comment_node;
                     } else {
-                        const comment_node = try arena.create(ast.Node.LineComment);
-                        *comment_node = ast.Node.LineComment {
+                        const comment_node = try arena.construct(ast.Node.LineComment {
                             .base = ast.Node {
                                 .id = ast.Node.Id.LineComment,
+                                .comments = null,
                             },
                             .lines = ArrayList(Token).init(arena),
-                        };
+                        });
                         result = comment_node;
                         break :blk comment_node;
                     }
@@ -3096,7 +3123,7 @@ pub const Parser = struct {
         *node = *init_to;
         node.base = blk: {
             const id = ast.Node.typeToId(T);
-            break :blk ast.Node {.id = id};
+            break :blk ast.Node {.id = id, .comments = null};
         };
 
         return node;
@@ -3269,7 +3296,7 @@ pub const Parser = struct {
                     switch (decl.id) {
                         ast.Node.Id.FnProto => {
                             const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
-                            try self.renderComments(stream, fn_proto, indent);
+                            try self.renderComments(stream, &fn_proto.base, indent);
 
                             if (fn_proto.body_node) |body_node| {
                                 stack.append(RenderState { .Expression = body_node}) catch unreachable;
@@ -3295,7 +3322,7 @@ pub const Parser = struct {
                         },
                         ast.Node.Id.TestDecl => {
                             const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
-                            try self.renderComments(stream, test_decl, indent);
+                            try self.renderComments(stream, &test_decl.base, indent);
                             try stream.print("test ");
                             try stack.append(RenderState { .Expression = test_decl.body_node });
                             try stack.append(RenderState { .Text = " " });
@@ -3338,7 +3365,6 @@ pub const Parser = struct {
                 },
 
                 RenderState.FieldInitializer => |field_init| {
-                    //TODO try self.renderComments(stream, field_init, indent);
                     try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
                     try stream.print(" = ");
                     try stack.append(RenderState { .Expression = field_init.expr });
@@ -3385,7 +3411,6 @@ pub const Parser = struct {
 
                 RenderState.ParamDecl => |base| {
                     const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
-                    // TODO try self.renderComments(stream, param_decl, indent);
                     if (param_decl.comptime_token) |comptime_token| {
                         try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
                     }
@@ -4328,10 +4353,10 @@ pub const Parser = struct {
                     ast.Node.Id.ParamDecl => unreachable,
                 },
                 RenderState.Statement => |base| {
+                    try self.renderComments(stream, base, indent);
                     switch (base.id) {
                         ast.Node.Id.VarDecl => {
                             const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
-                            try self.renderComments(stream, var_decl, indent);
                             try stack.append(RenderState { .VarDecl = var_decl});
                         },
                         else => {
@@ -4348,7 +4373,7 @@ pub const Parser = struct {
         }
     }
 
-    fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {
+    fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void {
         const comment = node.comments ?? return;
         for (comment.lines.toSliceConst()) |line_token| {
             try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
@@ -4430,6 +4455,16 @@ fn testCanonical(source: []const u8) !void {
     }
 }
 
+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
std/mem.zig
@@ -37,6 +37,20 @@ pub const Allocator = struct {
         return &slice[0];
     }
 
+    // TODO once #733 is solved, this will replace create
+    fn construct(self: &Allocator, init: var) t: {
+        // TODO this is a workaround for type getting parsed as Error!&const T
+        const T = @typeOf(init).Child;
+        break :t Error!&T;
+    } {
+        const T = @typeOf(init).Child;
+        if (@sizeOf(T) == 0) return &{};
+        const slice = try self.alloc(T, 1);
+        const ptr = &slice[0];
+        *ptr = *init;
+        return ptr;
+    }
+
     fn destroy(self: &Allocator, ptr: var) void {
         self.free(ptr[0..1]);
     }
@@ -54,7 +68,7 @@ pub const Allocator = struct {
         const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
         const byte_slice = try self.allocFn(self, byte_count, alignment);
         assert(byte_slice.len == byte_count);
-        // This loop should get optimized out in ReleaseFast mode
+        // This loop gets optimized out in ReleaseFast mode
         for (byte_slice) |*byte| {
             *byte = undefined;
         }
@@ -81,7 +95,7 @@ pub const Allocator = struct {
         const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
         assert(byte_slice.len == byte_count);
         if (n > old_mem.len) {
-            // This loop should get optimized out in ReleaseFast mode
+            // This loop gets optimized out in ReleaseFast mode
             for (byte_slice[old_byte_slice.len..]) |*byte| {
                 *byte = undefined;
             }