diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index f875c5a93d..5d65fc5ae6 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -234,7 +234,9 @@ pub const Tree = struct { .StringLiteral, .GroupedExpression, .BuiltinCallTwo, + .BuiltinCallTwoComma, .BuiltinCall, + .BuiltinCallComma, .ErrorSetDecl, .AnyType, .Comptime, @@ -474,6 +476,7 @@ pub const Tree = struct { .ErrorUnion, .IfSimple, .WhileSimple, + .FnDecl, => n = datas[n].rhs, .FieldAccess, @@ -497,9 +500,7 @@ pub const Tree = struct { .EnumLiteral, => return main_tokens[n] + end_offset, - .Call, - .BuiltinCall, - => { + .Call => { end_offset += 1; // for the rparen const params = tree.extraData(datas[n].rhs, Node.SubRange); if (params.end - params.start == 0) { @@ -526,6 +527,7 @@ pub const Tree = struct { .Block, .ContainerDecl, .TaggedUnion, + .BuiltinCall, => { end_offset += 1; // for the rbrace if (datas[n].rhs - datas[n].lhs == 0) { @@ -533,9 +535,12 @@ pub const Tree = struct { } n = tree.extra_data[datas[n].rhs - 1]; // last statement }, - .ContainerDeclComma, .TaggedUnionComma => { + .ContainerDeclComma, + .TaggedUnionComma, + .BuiltinCallComma, + => { assert(datas[n].rhs - datas[n].lhs > 0); - end_offset += 2; // for the comma + rbrace + end_offset += 2; // for the comma + rbrace/rparen n = tree.extra_data[datas[n].rhs - 1]; // last member }, .CallOne, @@ -565,11 +570,12 @@ pub const Tree = struct { } }, .ArrayInitDotTwoComma, + .BuiltinCallTwoComma, .StructInitDotTwoComma, .ContainerDeclTwoComma, .TaggedUnionTwoComma, => { - end_offset += 2; // for the comma + rbrace + end_offset += 2; // for the comma + rbrace/rparen if (datas[n].rhs != 0) { n = datas[n].rhs; } else if (datas[n].lhs != 0) { @@ -690,7 +696,6 @@ pub const Tree = struct { .Slice => unreachable, // TODO .SwitchCaseOne => unreachable, // TODO .SwitchRange => unreachable, // TODO - .FnDecl => unreachable, // TODO .ArrayType => unreachable, // TODO .ArrayTypeSentinel => unreachable, // TODO .PtrTypeAligned => unreachable, // TODO @@ -1836,8 +1841,12 @@ pub const Node = struct { GroupedExpression, /// `@a(lhs, rhs)`. lhs and rhs may be omitted. BuiltinCallTwo, + /// Same as BuiltinCallTwo but there is known to be a trailing comma before the rparen. + BuiltinCallTwoComma, /// `@a(b, c)`. `sub_list[lhs..rhs]`. BuiltinCall, + /// Same as BuiltinCall but there is known to be a trailing comma before the rparen. + BuiltinCallComma, /// `error{a, b}`. /// lhs and rhs both unused. ErrorSetDecl, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 35b5083562..1143c9f9c0 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -3676,7 +3676,6 @@ const Parser = struct { /// FnCallArguments <- LPAREN ExprList RPAREN /// ExprList <- (Expr COMMA)* Expr? - /// TODO detect when we can emit BuiltinCallTwo instead of BuiltinCall. fn parseBuiltinCall(p: *Parser) !Node.Index { const builtin_token = p.assertToken(.Builtin); _ = (try p.expectTokenRecoverable(.LParen)) orelse { @@ -3708,7 +3707,7 @@ const Parser = struct { .Comma => { if (p.eatToken(.RParen)) |_| { return p.addNode(.{ - .tag = .BuiltinCallTwo, + .tag = .BuiltinCallTwoComma, .main_token = builtin_token, .data = .{ .lhs = param_one, @@ -3739,7 +3738,7 @@ const Parser = struct { .Comma => { if (p.eatToken(.RParen)) |_| { return p.addNode(.{ - .tag = .BuiltinCallTwo, + .tag = .BuiltinCallTwoComma, .main_token = builtin_token, .data = .{ .lhs = param_one, @@ -3776,10 +3775,30 @@ const Parser = struct { try list.append(param); switch (p.token_tags[p.nextToken()]) { .Comma => { - if (p.eatToken(.RParen)) |_| break; + if (p.eatToken(.RParen)) |_| { + const params = try p.listToSpan(list.items); + return p.addNode(.{ + .tag = .BuiltinCallComma, + .main_token = builtin_token, + .data = .{ + .lhs = params.start, + .rhs = params.end, + }, + }); + } continue; }, - .RParen => break, + .RParen => { + const params = try p.listToSpan(list.items); + return p.addNode(.{ + .tag = .BuiltinCall, + .main_token = builtin_token, + .data = .{ + .lhs = params.start, + .rhs = params.end, + }, + }); + }, else => { // This is likely just a missing comma; // give an error but continue parsing this list. @@ -3790,15 +3809,6 @@ const Parser = struct { }, } } - const params = try p.listToSpan(list.items); - return p.addNode(.{ - .tag = .BuiltinCall, - .main_token = builtin_token, - .data = .{ - .lhs = params.start, - .rhs = params.end, - }, - }); } // string literal or multiline string literal diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e46cbf09d9..65843f9b5f 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -263,38 +263,38 @@ test "zig fmt: trailing comma in fn parameter list" { ); } -//test "zig fmt: comptime struct field" { -// try testCanonical( -// \\const Foo = struct { -// \\ a: i32, -// \\ comptime b: i32 = 1234, -// \\}; -// \\ -// ); -//} -// +test "zig fmt: comptime struct field" { + try testCanonical( + \\const Foo = struct { + \\ a: i32, + \\ comptime b: i32 = 1234, + \\}; + \\ + ); +} + //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(); -// \\ _ = @boolToInt(a); -// \\ _ = @call( -// \\ a, -// \\ b, -// \\ c, -// \\ ); -// \\} -// \\ -// ); -//} -// + +test "zig fmt: builtin call with trailing comma" { + try testCanonical( + \\pub fn main() void { + \\ @breakpoint(); + \\ _ = @boolToInt(a); + \\ _ = @call( + \\ a, + \\ b, + \\ c, + \\ ); + \\} + \\ + ); +} + //test "zig fmt: asm expression with comptime content" { // try testCanonical( // \\comptime { @@ -3988,14 +3988,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b return error.ParseError; } - var buffer = std.ArrayList(u8).init(allocator); - errdefer buffer.deinit(); - - const writer = buffer.writer(); - try std.zig.render(allocator, writer, tree); - const result = buffer.toOwnedSlice(); - anything_changed.* = !mem.eql(u8, result, source); - return result; + const formatted = try std.zig.render(allocator, tree); + anything_changed.* = !mem.eql(u8, formatted, source); + return formatted; } fn testTransform(source: []const u8, expected_source: []const u8) !void { const needed_alloc_count = x: { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 274e181e0e..d873676d53 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -22,13 +22,19 @@ pub const Error = error{ const Writer = std.ArrayList(u8).Writer; const Ais = std.io.AutoIndentingStream(Writer); -/// Returns whether anything changed. -/// `gpa` is used for allocating extra stack memory if needed, because -/// this function utilizes recursion. -pub fn render(gpa: *mem.Allocator, writer: Writer, tree: ast.Tree) Error!void { - assert(tree.errors.len == 0); // cannot render an invalid tree - var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, writer); - return renderRoot(&auto_indenting_stream, tree); +/// `gpa` is used both for allocating the resulting formatted source code, but also +/// for allocating extra stack memory if needed, because this function utilizes recursion. +/// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006. +/// Caller owns the returned slice of bytes, allocated with `gpa`. +pub fn render(gpa: *mem.Allocator, tree: ast.Tree) Error![]u8 { + assert(tree.errors.len == 0); // Cannot render an invalid tree. + + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + + var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, buffer.writer()); + try renderRoot(&auto_indenting_stream, tree); + return buffer.toOwnedSlice(); } /// Assumes there are no tokens in between start and end. @@ -770,7 +776,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac // } //}, - .BuiltinCallTwo => { + .BuiltinCallTwo, .BuiltinCallTwoComma => { if (datas[node].lhs == 0) { const params = [_]ast.Node.Index{}; return renderBuiltinCall(ais, tree, main_tokens[node], ¶ms, space); @@ -782,7 +788,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac return renderBuiltinCall(ais, tree, main_tokens[node], ¶ms, space); } }, - .BuiltinCall => { + .BuiltinCall, .BuiltinCallComma => { const params = tree.extra_data[datas[node].lhs..datas[node].rhs]; return renderBuiltinCall(ais, tree, main_tokens[node], params, space); },