authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-04 16:38:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-04 16:38:29-07:00
log8e46d06650cf26f6769817f75ced486e9ee5c0dd
treed467a0736570238b8c22cc204c291c34e1497401
parent4428acf0f7b4b8da2709bffe1c304df435d89eed

zig fmt: implement fn protos and defers


4 files changed, 523 insertions(+), 326 deletions(-)

lib/std/zig/ast.zig+114-12
...@@ -244,9 +244,12 @@ pub const Tree = struct {...@@ -244,9 +244,12 @@ pub const Tree = struct {
244 .AnyType,244 .AnyType,
245 .Comptime,245 .Comptime,
246 .Nosuspend,246 .Nosuspend,
247 .Block,
248 .AsmSimple,247 .AsmSimple,
249 .Asm,248 .Asm,
249 .FnProtoSimple,
250 .FnProtoMulti,
251 .FnProtoOne,
252 .FnProto,
250 => return main_tokens[n],253 => return main_tokens[n],
251254
252 .Catch,255 .Catch,
...@@ -303,6 +306,7 @@ pub const Tree = struct {...@@ -303,6 +306,7 @@ pub const Tree = struct {
303 .SwitchCaseOne,306 .SwitchCaseOne,
304 .SwitchRange,307 .SwitchRange,
305 .FnDecl,308 .FnDecl,
309 .ErrorUnion,
306 => n = datas[n].lhs,310 => n = datas[n].lhs,
307311
308 .ContainerFieldInit,312 .ContainerFieldInit,
...@@ -342,6 +346,18 @@ pub const Tree = struct {...@@ -342,6 +346,18 @@ pub const Tree = struct {
342 return i;346 return i;
343 },347 },
344348
349 .Block,
350 .BlockTwo,
351 => {
352 // Look for a label.
353 const lbrace = main_tokens[n];
354 if (token_tags[lbrace - 1] == .Colon) {
355 return lbrace - 2;
356 } else {
357 return lbrace;
358 }
359 },
360
345 .ArrayType => unreachable, // TODO361 .ArrayType => unreachable, // TODO
346 .ArrayTypeSentinel => unreachable, // TODO362 .ArrayTypeSentinel => unreachable, // TODO
347 .PtrTypeAligned => unreachable, // TODO363 .PtrTypeAligned => unreachable, // TODO
...@@ -355,10 +371,6 @@ pub const Tree = struct {...@@ -355,10 +371,6 @@ pub const Tree = struct {
355 .While => unreachable, // TODO371 .While => unreachable, // TODO
356 .ForSimple => unreachable, // TODO372 .ForSimple => unreachable, // TODO
357 .For => unreachable, // TODO373 .For => unreachable, // TODO
358 .FnProtoSimple => unreachable, // TODO
359 .FnProtoSimpleMulti => unreachable, // TODO
360 .FnProtoOne => unreachable, // TODO
361 .FnProto => unreachable, // TODO
362 .ContainerDecl => unreachable, // TODO374 .ContainerDecl => unreachable, // TODO
363 .ContainerDeclArg => unreachable, // TODO375 .ContainerDeclArg => unreachable, // TODO
364 .TaggedUnion => unreachable, // TODO376 .TaggedUnion => unreachable, // TODO
...@@ -366,7 +378,6 @@ pub const Tree = struct {...@@ -366,7 +378,6 @@ pub const Tree = struct {
366 .AsmOutput => unreachable, // TODO378 .AsmOutput => unreachable, // TODO
367 .AsmInput => unreachable, // TODO379 .AsmInput => unreachable, // TODO
368 .ErrorValue => unreachable, // TODO380 .ErrorValue => unreachable, // TODO
369 .ErrorUnion => unreachable, // TODO
370 };381 };
371 }382 }
372383
...@@ -468,13 +479,20 @@ pub const Tree = struct {...@@ -468,13 +479,20 @@ pub const Tree = struct {
468 .Call,479 .Call,
469 .BuiltinCall,480 .BuiltinCall,
470 => {481 => {
471 end_offset += 1; // for the `)`482 end_offset += 1; // for the rparen
472 const params = tree.extraData(datas[n].rhs, Node.SubRange);483 const params = tree.extraData(datas[n].rhs, Node.SubRange);
473 if (params.end - params.start == 0) {484 if (params.end - params.start == 0) {
474 return main_tokens[n] + end_offset;485 return main_tokens[n] + end_offset;
475 }486 }
476 n = tree.extra_data[params.end - 1]; // last parameter487 n = tree.extra_data[params.end - 1]; // last parameter
477 },488 },
489 .Block => {
490 end_offset += 1; // for the rbrace
491 if (datas[n].rhs - datas[n].lhs == 0) {
492 return main_tokens[n] + end_offset;
493 }
494 n = tree.extra_data[datas[n].rhs - 1]; // last statement
495 },
478 .CallOne,496 .CallOne,
479 .ArrayAccess,497 .ArrayAccess,
480 => {498 => {
...@@ -485,8 +503,8 @@ pub const Tree = struct {...@@ -485,8 +503,8 @@ pub const Tree = struct {
485 n = datas[n].rhs;503 n = datas[n].rhs;
486 },504 },
487505
488 .BuiltinCallTwo => {506 .BuiltinCallTwo, .BlockTwo => {
489 end_offset += 1; // for the rparen507 end_offset += 1; // for the rparen/rbrace
490 if (datas[n].rhs == 0) {508 if (datas[n].rhs == 0) {
491 if (datas[n].lhs == 0) {509 if (datas[n].lhs == 0) {
492 return main_tokens[n] + end_offset;510 return main_tokens[n] + end_offset;
...@@ -511,7 +529,6 @@ pub const Tree = struct {...@@ -511,7 +529,6 @@ pub const Tree = struct {
511 .Continue => unreachable, // TODO529 .Continue => unreachable, // TODO
512 .EnumLiteral => unreachable, // TODO530 .EnumLiteral => unreachable, // TODO
513 .ErrorSetDecl => unreachable, // TODO531 .ErrorSetDecl => unreachable, // TODO
514 .Block => unreachable, // TODO
515 .AsmSimple => unreachable, // TODO532 .AsmSimple => unreachable, // TODO
516 .Asm => unreachable, // TODO533 .Asm => unreachable, // TODO
517 .SliceOpen => unreachable, // TODO534 .SliceOpen => unreachable, // TODO
...@@ -539,7 +556,7 @@ pub const Tree = struct {...@@ -539,7 +556,7 @@ pub const Tree = struct {
539 .ForSimple => unreachable, // TODO556 .ForSimple => unreachable, // TODO
540 .For => unreachable, // TODO557 .For => unreachable, // TODO
541 .FnProtoSimple => unreachable, // TODO558 .FnProtoSimple => unreachable, // TODO
542 .FnProtoSimpleMulti => unreachable, // TODO559 .FnProtoMulti => unreachable, // TODO
543 .FnProtoOne => unreachable, // TODO560 .FnProtoOne => unreachable, // TODO
544 .FnProto => unreachable, // TODO561 .FnProto => unreachable, // TODO
545 .ContainerDecl => unreachable, // TODO562 .ContainerDecl => unreachable, // TODO
...@@ -665,6 +682,67 @@ pub const Tree = struct {...@@ -665,6 +682,67 @@ pub const Tree = struct {
665 });682 });
666 }683 }
667684
685 pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.FnProto {
686 assert(tree.nodes.items(.tag)[node] == .FnProtoSimple);
687 const data = tree.nodes.items(.data)[node];
688 buffer[0] = data.lhs;
689 const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1];
690 return tree.fullFnProto(.{
691 .fn_token = tree.nodes.items(.main_token)[node],
692 .return_type = data.rhs,
693 .params = params,
694 .align_expr = 0,
695 .section_expr = 0,
696 .callconv_expr = 0,
697 });
698 }
699
700 pub fn fnProtoMulti(tree: Tree, node: Node.Index) Full.FnProto {
701 assert(tree.nodes.items(.tag)[node] == .FnProtoMulti);
702 const data = tree.nodes.items(.data)[node];
703 const params_range = tree.extraData(data.lhs, Node.SubRange);
704 const params = tree.extra_data[params_range.start..params_range.end];
705 return tree.fullFnProto(.{
706 .fn_token = tree.nodes.items(.main_token)[node],
707 .return_type = data.rhs,
708 .params = params,
709 .align_expr = 0,
710 .section_expr = 0,
711 .callconv_expr = 0,
712 });
713 }
714
715 pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.FnProto {
716 assert(tree.nodes.items(.tag)[node] == .FnProtoOne);
717 const data = tree.nodes.items(.data)[node];
718 const extra = tree.extraData(data.lhs, Node.FnProtoOne);
719 buffer[0] = extra.param;
720 const params = if (extra.param == 0) buffer[0..0] else buffer[0..1];
721 return tree.fullFnProto(.{
722 .fn_token = tree.nodes.items(.main_token)[node],
723 .return_type = data.rhs,
724 .params = params,
725 .align_expr = extra.align_expr,
726 .section_expr = extra.section_expr,
727 .callconv_expr = extra.callconv_expr,
728 });
729 }
730
731 pub fn fnProto(tree: Tree, node: Node.Index) Full.FnProto {
732 assert(tree.nodes.items(.tag)[node] == .FnProto);
733 const data = tree.nodes.items(.data)[node];
734 const extra = tree.extraData(data.lhs, Node.FnProto);
735 const params = tree.extra_data[extra.params_start..extra.params_end];
736 return tree.fullFnProto(.{
737 .fn_token = tree.nodes.items(.main_token)[node],
738 .return_type = data.rhs,
739 .params = params,
740 .align_expr = extra.align_expr,
741 .section_expr = extra.section_expr,
742 .callconv_expr = extra.callconv_expr,
743 });
744 }
745
668 fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl {746 fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl {
669 const token_tags = tree.tokens.items(.tag);747 const token_tags = tree.tokens.items(.tag);
670 var result: Full.VarDecl = .{748 var result: Full.VarDecl = .{
...@@ -728,6 +806,14 @@ pub const Tree = struct {...@@ -728,6 +806,14 @@ pub const Tree = struct {
728 }806 }
729 return result;807 return result;
730 }808 }
809
810 fn fullFnProto(tree: Tree, info: Full.FnProto.Ast) Full.FnProto {
811 const token_tags = tree.tokens.items(.tag);
812 var result: Full.FnProto = .{
813 .ast = info,
814 };
815 return result;
816 }
731};817};
732818
733/// Fully assembled AST node information.819/// Fully assembled AST node information.
...@@ -778,6 +864,19 @@ pub const Full = struct {...@@ -778,6 +864,19 @@ pub const Full = struct {
778 align_expr: Node.Index,864 align_expr: Node.Index,
779 };865 };
780 };866 };
867
868 pub const FnProto = struct {
869 ast: Ast,
870
871 pub const Ast = struct {
872 fn_token: TokenIndex,
873 return_type: Node.Index,
874 params: []const Node.Index,
875 align_expr: Node.Index,
876 section_expr: Node.Index,
877 callconv_expr: Node.Index,
878 };
879 };
781};880};
782881
783pub const Error = union(enum) {882pub const Error = union(enum) {
...@@ -1247,7 +1346,7 @@ pub const Node = struct {...@@ -1247,7 +1346,7 @@ pub const Node = struct {
1247 FnProtoSimple,1346 FnProtoSimple,
1248 /// `fn(a: b, c: d) rhs`. `sub_range_list[lhs]`.1347 /// `fn(a: b, c: d) rhs`. `sub_range_list[lhs]`.
1249 /// anytype and ... parameters are omitted from the AST tree.1348 /// anytype and ... parameters are omitted from the AST tree.
1250 FnProtoSimpleMulti,1349 FnProtoMulti,
1251 /// `fn(a: b) rhs linksection(e) callconv(f)`. lhs is index into extra_data.1350 /// `fn(a: b) rhs linksection(e) callconv(f)`. lhs is index into extra_data.
1252 /// zero or one parameters.1351 /// zero or one parameters.
1253 /// anytype and ... parameters are omitted from the AST tree.1352 /// anytype and ... parameters are omitted from the AST tree.
...@@ -1321,6 +1420,9 @@ pub const Node = struct {...@@ -1321,6 +1420,9 @@ pub const Node = struct {
1321 Comptime,1420 Comptime,
1322 /// `nosuspend lhs`. rhs unused.1421 /// `nosuspend lhs`. rhs unused.
1323 Nosuspend,1422 Nosuspend,
1423 /// `{lhs; rhs;}`. rhs or lhs can be omitted.
1424 /// main_token points at the `{`.
1425 BlockTwo,
1324 /// `{}`. `sub_list[lhs..rhs]`.1426 /// `{}`. `sub_list[lhs..rhs]`.
1325 /// main_token points at the `{`.1427 /// main_token points at the `{`.
1326 Block,1428 Block,
lib/std/zig/parse.zig+56-12
...@@ -541,7 +541,7 @@ const Parser = struct {...@@ -541,7 +541,7 @@ const Parser = struct {
541 .multi => |list| {541 .multi => |list| {
542 const span = try p.listToSpan(list);542 const span = try p.listToSpan(list);
543 return p.addNode(.{543 return p.addNode(.{
544 .tag = .FnProtoSimpleMulti,544 .tag = .FnProtoMulti,
545 .main_token = fn_token,545 .main_token = fn_token,
546 .data = .{546 .data = .{
547 .lhs = try p.addExtra(Node.SubRange{547 .lhs = try p.addExtra(Node.SubRange{
...@@ -810,6 +810,22 @@ const Parser = struct {...@@ -810,6 +810,22 @@ const Parser = struct {
810 return statement;810 return statement;
811 }811 }
812812
813 /// If a parse error occurs, reports an error, but then finds the next statement
814 /// and returns that one instead. If a parse error occurs but there is no following
815 /// statement, returns 0.
816 fn expectStatementRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
817 while (true) {
818 return p.expectStatement() catch |err| switch (err) {
819 error.OutOfMemory => return error.OutOfMemory,
820 error.ParseError => {
821 p.findNextStmt(); // Try to skip to the next statement.
822 if (p.token_tags[p.tok_i] == .RBrace) return null_node;
823 continue;
824 },
825 };
826 }
827 }
828
813 /// IfStatement829 /// IfStatement
814 /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?830 /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
815 /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )831 /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
...@@ -1859,25 +1875,53 @@ const Parser = struct {...@@ -1859,25 +1875,53 @@ const Parser = struct {
1859 fn parseBlock(p: *Parser) !Node.Index {1875 fn parseBlock(p: *Parser) !Node.Index {
1860 const lbrace = p.eatToken(.LBrace) orelse return null_node;1876 const lbrace = p.eatToken(.LBrace) orelse return null_node;
18611877
1878 if (p.eatToken(.RBrace)) |_| {
1879 return p.addNode(.{
1880 .tag = .BlockTwo,
1881 .main_token = lbrace,
1882 .data = .{
1883 .lhs = 0,
1884 .rhs = 0,
1885 },
1886 });
1887 }
1888
1889 const stmt_one = try p.expectStatementRecoverable();
1890 if (p.eatToken(.RBrace)) |_| {
1891 return p.addNode(.{
1892 .tag = .BlockTwo,
1893 .main_token = lbrace,
1894 .data = .{
1895 .lhs = stmt_one,
1896 .rhs = 0,
1897 },
1898 });
1899 }
1900 const stmt_two = try p.expectStatementRecoverable();
1901 if (p.eatToken(.RBrace)) |_| {
1902 return p.addNode(.{
1903 .tag = .BlockTwo,
1904 .main_token = lbrace,
1905 .data = .{
1906 .lhs = stmt_one,
1907 .rhs = stmt_two,
1908 },
1909 });
1910 }
1911
1862 var statements = std.ArrayList(Node.Index).init(p.gpa);1912 var statements = std.ArrayList(Node.Index).init(p.gpa);
1863 defer statements.deinit();1913 defer statements.deinit();
18641914
1915 try statements.appendSlice(&[_]Node.Index{ stmt_one, stmt_two });
1916
1865 while (true) {1917 while (true) {
1866 const statement = (p.parseStatement() catch |err| switch (err) {1918 const statement = try p.expectStatementRecoverable();
1867 error.OutOfMemory => return error.OutOfMemory,
1868 error.ParseError => {
1869 // try to skip to the next statement
1870 p.findNextStmt();
1871 continue;
1872 },
1873 });
1874 if (statement == 0) break;1919 if (statement == 0) break;
1875 try statements.append(statement);1920 try statements.append(statement);
1921 if (p.token_tags[p.tok_i] == .RBrace) break;
1876 }1922 }
18771923 _ = try p.expectToken(.RBrace);
1878 const rbrace = try p.expectToken(.RBrace);
1879 const statements_span = try p.listToSpan(statements.items);1924 const statements_span = try p.listToSpan(statements.items);
1880
1881 return p.addNode(.{1925 return p.addNode(.{
1882 .tag = .Block,1926 .tag = .Block,
1883 .main_token = lbrace,1927 .main_token = lbrace,
lib/std/zig/parser_test.zig+38-38
...@@ -100,44 +100,44 @@ test "zig fmt: top-level fields" {...@@ -100,44 +100,44 @@ test "zig fmt: top-level fields" {
100 );100 );
101}101}
102102
103//test "zig fmt: decl between fields" {103test "zig fmt: decl between fields" {
104// try testError(104 try testError(
105// \\const S = struct {105 \\const S = struct {
106// \\ const foo = 2;106 \\ const foo = 2;
107// \\ const bar = 2;107 \\ const bar = 2;
108// \\ const baz = 2;108 \\ const baz = 2;
109// \\ a: usize,109 \\ a: usize,
110// \\ const foo1 = 2;110 \\ const foo1 = 2;
111// \\ const bar1 = 2;111 \\ const bar1 = 2;
112// \\ const baz1 = 2;112 \\ const baz1 = 2;
113// \\ b: usize,113 \\ b: usize,
114// \\};114 \\};
115// , &[_]Error{115 , &[_]Error{
116// .DeclBetweenFields,116 .DeclBetweenFields,
117// });117 });
118//}118}
119//119
120//test "zig fmt: eof after missing comma" {120test "zig fmt: eof after missing comma" {
121// try testError(121 try testError(
122// \\foo()122 \\foo()
123// , &[_]Error{123 , &[_]Error{
124// .ExpectedToken,124 .ExpectedToken,
125// });125 });
126//}126}
127//127
128//test "zig fmt: errdefer with payload" {128test "zig fmt: errdefer with payload" {
129// try testCanonical(129 try testCanonical(
130// \\pub fn main() anyerror!void {130 \\pub fn main() anyerror!void {
131// \\ errdefer |a| x += 1;131 \\ errdefer |a| x += 1;
132// \\ errdefer |a| {}132 \\ errdefer |a| {}
133// \\ errdefer |a| {133 \\ errdefer |a| {
134// \\ x += 1;134 \\ x += 1;
135// \\ }135 \\ }
136// \\}136 \\}
137// \\137 \\
138// );138 );
139//}139}
140//140
141//test "zig fmt: nosuspend block" {141//test "zig fmt: nosuspend block" {
142// try testCanonical(142// try testCanonical(
143// \\pub fn main() anyerror!void {143// \\pub fn main() anyerror!void {
lib/std/zig/render.zig+315-264
...@@ -77,22 +77,11 @@ fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!voi...@@ -77,22 +77,11 @@ fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!voi
77}77}
7878
79fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, first_token: ast.TokenIndex) Error!void {79fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, first_token: ast.TokenIndex) Error!void {
80 @panic("TODO implement renderExtraNewlineToken");80 if (first_token == 0) return;
81 //var prev_token = first_token;81 const token_starts = tree.tokens.items(.start);
82 //if (prev_token == 0) return;82 if (tree.tokenLocation(token_starts[first_token - 1], first_token).line >= 2) {
83 //const token_tags = tree.tokens.items(.tag);83 return ais.insertNewline();
84 //var newline_threshold: usize = 2;84 }
85 //while (token_tags[prev_token - 1] == .DocComment) {
86 // if (tree.tokenLocation(tree.token_locs[prev_token - 1].end, prev_token).line == 1) {
87 // newline_threshold += 1;
88 // }
89 // prev_token -= 1;
90 //}
91 //const prev_token_end = tree.token_locs[prev_token - 1].end;
92 //const loc = tree.tokenLocation(prev_token_end, first_token);
93 //if (loc.line >= newline_threshold) {
94 // try ais.insertNewline();
95 //}
96}85}
9786
98fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {87fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
...@@ -101,24 +90,43 @@ fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: S...@@ -101,24 +90,43 @@ fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: S
101 const datas = tree.nodes.items(.data);90 const datas = tree.nodes.items(.data);
102 try renderDocComments(ais, tree, tree.firstToken(decl));91 try renderDocComments(ais, tree, tree.firstToken(decl));
103 switch (tree.nodes.items(.tag)[decl]) {92 switch (tree.nodes.items(.tag)[decl]) {
104 .FnProtoSimple => unreachable, // TODO93 .FnDecl => {
105 .FnProtoSimpleMulti => unreachable, // TODO94 // Some examples:
106 .FnProtoOne => unreachable, // TODO95 // pub extern "foo" fn ...
107 .FnDecl => unreachable, // TODO96 // export fn ...
108 .FnProto => unreachable, // TODO97 const fn_proto = datas[decl].lhs;
109 // .FnProto => {98 const fn_token = main_tokens[fn_proto];
110 // const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);99 // Go back to the first token we should render here.
111100 var i = fn_token;
112 // try renderDocComments(ais, tree, fn_proto, fn_proto.getDocComments());101 while (i > 0) {
113102 i -= 1;
114 // if (fn_proto.getBodyNode()) |body_node| {103 switch (token_tags[i]) {
115 // try renderExpression(ais, tree, decl, .Space);104 .Keyword_extern,
116 // try renderExpression(ais, tree, body_node, space);105 .Keyword_export,
117 // } else {106 .Keyword_pub,
118 // try renderExpression(ais, tree, decl, .None);107 .StringLiteral,
119 // try renderToken(ais, tree, tree.nextToken(decl.lastToken()), space);108 => continue,
120 // }109
121 // },110 else => {
111 i += 1;
112 break;
113 },
114 }
115 }
116 while (i < fn_token) : (i += 1) {
117 try renderToken(ais, tree, i, .Space);
118 }
119 try renderExpression(ais, tree, fn_proto, .Space);
120 return renderExpression(ais, tree, datas[decl].rhs, space);
121 },
122 .FnProtoSimple,
123 .FnProtoMulti,
124 .FnProtoOne,
125 .FnProto,
126 => {
127 try renderExpression(ais, tree, decl, .None);
128 try renderToken(ais, tree, tree.lastToken(decl) + 1, space); // semicolon
129 },
122130
123 .UsingNamespace => unreachable, // TODO131 .UsingNamespace => unreachable, // TODO
124 // .Use => {132 // .Use => {
...@@ -186,90 +194,48 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac...@@ -186,90 +194,48 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
186 // }194 // }
187 // return renderToken(ais, tree, any_type.token, space);195 // return renderToken(ais, tree, any_type.token, space);
188 //},196 //},
197 .BlockTwo => {
198 var statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
199 if (datas[node].lhs == 0) {
200 return renderBlock(ais, tree, main_tokens[node], statements[0..0], space);
201 } else if (datas[node].rhs == 0) {
202 return renderBlock(ais, tree, main_tokens[node], statements[0..1], space);
203 } else {
204 return renderBlock(ais, tree, main_tokens[node], statements[0..2], space);
205 }
206 },
189 .Block => {207 .Block => {
190 const lbrace = main_tokens[node];208 const lbrace = main_tokens[node];
191 if (token_tags[lbrace - 1] == .Colon and209 const statements = tree.extra_data[datas[node].lhs..datas[node].rhs];
192 token_tags[lbrace - 2] == .Identifier)210 return renderBlock(ais, tree, main_tokens[node], statements, space);
193 {211 },
194 try renderToken(ais, tree, lbrace - 2, .None);
195 try renderToken(ais, tree, lbrace - 1, .Space);
196 }
197 const nodes_data = tree.nodes.items(.data);
198 const statements = tree.extra_data[nodes_data[node].lhs..nodes_data[node].rhs];
199212
200 if (statements.len == 0) {213 .ErrDefer => {
201 ais.pushIndentNextLine();214 const defer_token = main_tokens[node];
202 try renderToken(ais, tree, lbrace, .None);215 const payload_token = datas[node].lhs;
203 ais.popIndent();216 const expr = datas[node].rhs;
204 const rbrace = lbrace + 1;
205 return renderToken(ais, tree, rbrace, space);
206 } else {
207 ais.pushIndentNextLine();
208
209 try renderToken(ais, tree, lbrace, .Newline);
210
211 for (statements) |stmt, i| {
212 switch (node_tags[stmt]) {
213 .GlobalVarDecl => try renderVarDecl(ais, tree, tree.globalVarDecl(stmt)),
214 .LocalVarDecl => try renderVarDecl(ais, tree, tree.localVarDecl(stmt)),
215 .SimpleVarDecl => try renderVarDecl(ais, tree, tree.simpleVarDecl(stmt)),
216 .AlignedVarDecl => try renderVarDecl(ais, tree, tree.alignedVarDecl(stmt)),
217 else => {
218 const semicolon = tree.lastToken(stmt) + 1;
219 if (token_tags[semicolon] == .Semicolon) {
220 try renderExpression(ais, tree, stmt, .None);
221 try renderToken(ais, tree, semicolon, .Newline);
222 } else {
223 try renderExpression(ais, tree, stmt, .Newline);
224 }
225 },
226 }
227217
228 if (i + 1 < statements.len) {218 try renderToken(ais, tree, defer_token, .Space);
229 try renderExtraNewline(ais, tree, statements[i + 1]);219 if (payload_token != 0) {
230 }220 try renderToken(ais, tree, payload_token - 1, .None); // |
231 }221 try renderToken(ais, tree, payload_token, .None); // identifier
232 ais.popIndent();222 try renderToken(ais, tree, payload_token + 1, .Space); // |
233 // The rbrace could be +1 or +2 from the last token of the last
234 // statement in the block because lastToken() does not count semicolons.
235 const maybe_rbrace = tree.lastToken(statements[statements.len - 1]) + 1;
236 if (token_tags[maybe_rbrace] == .RBrace) {
237 return renderToken(ais, tree, maybe_rbrace, space);
238 } else {
239 assert(token_tags[maybe_rbrace + 1] == .RBrace);
240 return renderToken(ais, tree, maybe_rbrace + 1, space);
241 }
242 }223 }
224 return renderExpression(ais, tree, expr, space);
243 },225 },
244226
245 .Defer => unreachable, // TODO227 .Defer => {
246 .ErrDefer => unreachable, // TODO228 const defer_token = main_tokens[node];
247 //.Defer => {229 const expr = datas[node].rhs;
248 // const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);230 try renderToken(ais, tree, defer_token, .Space);
249231 return renderExpression(ais, tree, expr, space);
250 // try renderToken(ais, tree, defer_node.defer_token, Space.Space);232 },
251 // if (defer_node.payload) |payload| {233 .Comptime, .Nosuspend => {
252 // try renderExpression(ais, tree, payload, Space.Space);
253 // }
254 // return renderExpression(ais, tree, defer_node.expr, space);
255 //},
256 .Comptime => {
257 const comptime_token = main_tokens[node];234 const comptime_token = main_tokens[node];
258 const block = datas[node].lhs;235 const block = datas[node].lhs;
259 try renderToken(ais, tree, comptime_token, .Space);236 try renderToken(ais, tree, comptime_token, .Space);
260 return renderExpression(ais, tree, block, space);237 return renderExpression(ais, tree, block, space);
261 },238 },
262 .Nosuspend => unreachable, // TODO
263 //.Nosuspend => {
264 // const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base);
265 // if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) {
266 // // TODO: remove this
267 // try ais.writer().writeAll("nosuspend ");
268 // } else {
269 // try renderToken(ais, tree, nosuspend_node.nosuspend_token, Space.Space);
270 // }
271 // return renderExpression(ais, tree, nosuspend_node.expr, space);
272 //},
273239
274 .Suspend => unreachable, // TODO240 .Suspend => unreachable, // TODO
275 //.Suspend => {241 //.Suspend => {
...@@ -1274,140 +1240,16 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac...@@ -1274,140 +1240,16 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
1274 return renderBuiltinCall(ais, tree, main_tokens[node], params, space);1240 return renderBuiltinCall(ais, tree, main_tokens[node], params, space);
1275 },1241 },
12761242
1277 .FnProtoSimple => unreachable, // TODO1243 .FnProtoSimple => {
1278 .FnProtoSimpleMulti => unreachable, // TODO1244 var params: [1]ast.Node.Index = undefined;
1279 .FnProtoOne => unreachable, // TODO1245 return renderFnProto(ais, tree, tree.fnProtoSimple(&params, node), space);
1280 .FnProto => unreachable, // TODO1246 },
1281 //.FnProto => {1247 .FnProtoMulti => return renderFnProto(ais, tree, tree.fnProtoMulti(node), space),
1282 // const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);1248 .FnProtoOne => {
12831249 var params: [1]ast.Node.Index = undefined;
1284 // if (fn_proto.getVisibToken()) |visib_token_index| {1250 return renderFnProto(ais, tree, tree.fnProtoOne(&params, node), space);
1285 // const visib_token = tree.token_tags[visib_token_index];1251 },
1286 // assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);1252 .FnProto => return renderFnProto(ais, tree, tree.fnProto(node), space),
1287
1288 // try renderToken(ais, tree, visib_token_index, Space.Space); // pub
1289 // }
1290
1291 // if (fn_proto.getExternExportInlineToken()) |extern_export_inline_token| {
1292 // if (fn_proto.getIsExternPrototype() == null)
1293 // try renderToken(ais, tree, extern_export_inline_token, Space.Space); // extern/export/inline
1294 // }
1295
1296 // if (fn_proto.getLibName()) |lib_name| {
1297 // try renderExpression(ais, tree, lib_name, Space.Space);
1298 // }
1299
1300 // const lparen = if (fn_proto.getNameToken()) |name_token| blk: {
1301 // try renderToken(ais, tree, fn_proto.fn_token, Space.Space); // fn
1302 // try renderToken(ais, tree, name_token, Space.None); // name
1303 // break :blk tree.nextToken(name_token);
1304 // } else blk: {
1305 // try renderToken(ais, tree, fn_proto.fn_token, Space.Space); // fn
1306 // break :blk tree.nextToken(fn_proto.fn_token);
1307 // };
1308 // assert(tree.token_tags[lparen] == .LParen);
1309
1310 // const rparen = tree.prevToken(
1311 // // the first token for the annotation expressions is the left
1312 // // parenthesis, hence the need for two prevToken
1313 // if (fn_proto.getAlignExpr()) |align_expr|
1314 // tree.prevToken(tree.prevToken(align_expr.firstToken()))
1315 // else if (fn_proto.getSectionExpr()) |section_expr|
1316 // tree.prevToken(tree.prevToken(section_expr.firstToken()))
1317 // else if (fn_proto.getCallconvExpr()) |callconv_expr|
1318 // tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
1319 // else switch (fn_proto.return_type) {
1320 // .Explicit => |node| node.firstToken(),
1321 // .InferErrorSet => |node| tree.prevToken(node.firstToken()),
1322 // .Invalid => unreachable,
1323 // },
1324 // );
1325 // assert(tree.token_tags[rparen] == .RParen);
1326
1327 // const src_params_trailing_comma = blk: {
1328 // const maybe_comma = tree.token_tags[rparen - 1];
1329 // break :blk maybe_comma == .Comma or maybe_comma == .LineComment;
1330 // };
1331
1332 // if (!src_params_trailing_comma) {
1333 // try renderToken(ais, tree, lparen, Space.None); // (
1334
1335 // // render all on one line, no trailing comma
1336 // for (fn_proto.params()) |param_decl, i| {
1337 // try renderParamDecl(allocator, ais, tree, param_decl, Space.None);
1338
1339 // if (i + 1 < fn_proto.params_len or fn_proto.getVarArgsToken() != null) {
1340 // const comma = tree.nextToken(param_decl.lastToken());
1341 // try renderToken(ais, tree, comma, Space.Space); // ,
1342 // }
1343 // }
1344 // if (fn_proto.getVarArgsToken()) |var_args_token| {
1345 // try renderToken(ais, tree, var_args_token, Space.None);
1346 // }
1347 // } else {
1348 // // one param per line
1349 // ais.pushIndent();
1350 // defer ais.popIndent();
1351 // try renderToken(ais, tree, lparen, Space.Newline); // (
1352
1353 // for (fn_proto.params()) |param_decl| {
1354 // try renderParamDecl(allocator, ais, tree, param_decl, Space.Comma);
1355 // }
1356 // if (fn_proto.getVarArgsToken()) |var_args_token| {
1357 // try renderToken(ais, tree, var_args_token, Space.Comma);
1358 // }
1359 // }
1360
1361 // try renderToken(ais, tree, rparen, Space.Space); // )
1362
1363 // if (fn_proto.getAlignExpr()) |align_expr| {
1364 // const align_rparen = tree.nextToken(align_expr.lastToken());
1365 // const align_lparen = tree.prevToken(align_expr.firstToken());
1366 // const align_kw = tree.prevToken(align_lparen);
1367
1368 // try renderToken(ais, tree, align_kw, Space.None); // align
1369 // try renderToken(ais, tree, align_lparen, Space.None); // (
1370 // try renderExpression(ais, tree, align_expr, Space.None);
1371 // try renderToken(ais, tree, align_rparen, Space.Space); // )
1372 // }
1373
1374 // if (fn_proto.getSectionExpr()) |section_expr| {
1375 // const section_rparen = tree.nextToken(section_expr.lastToken());
1376 // const section_lparen = tree.prevToken(section_expr.firstToken());
1377 // const section_kw = tree.prevToken(section_lparen);
1378
1379 // try renderToken(ais, tree, section_kw, Space.None); // section
1380 // try renderToken(ais, tree, section_lparen, Space.None); // (
1381 // try renderExpression(ais, tree, section_expr, Space.None);
1382 // try renderToken(ais, tree, section_rparen, Space.Space); // )
1383 // }
1384
1385 // if (fn_proto.getCallconvExpr()) |callconv_expr| {
1386 // const callconv_rparen = tree.nextToken(callconv_expr.lastToken());
1387 // const callconv_lparen = tree.prevToken(callconv_expr.firstToken());
1388 // const callconv_kw = tree.prevToken(callconv_lparen);
1389
1390 // try renderToken(ais, tree, callconv_kw, Space.None); // callconv
1391 // try renderToken(ais, tree, callconv_lparen, Space.None); // (
1392 // try renderExpression(ais, tree, callconv_expr, Space.None);
1393 // try renderToken(ais, tree, callconv_rparen, Space.Space); // )
1394 // } else if (fn_proto.getIsExternPrototype() != null) {
1395 // try ais.writer().writeAll("callconv(.C) ");
1396 // } else if (fn_proto.getIsAsync() != null) {
1397 // try ais.writer().writeAll("callconv(.Async) ");
1398 // }
1399
1400 // switch (fn_proto.return_type) {
1401 // .Explicit => |node| {
1402 // return renderExpression(ais, tree, node, space);
1403 // },
1404 // .InferErrorSet => |node| {
1405 // try renderToken(ais, tree, tree.prevToken(node.firstToken()), Space.None); // !
1406 // return renderExpression(ais, tree, node, space);
1407 // },
1408 // .Invalid => unreachable,
1409 // }
1410 //},
14111253
1412 .AnyFrameType => unreachable, // TODO1254 .AnyFrameType => unreachable, // TODO
1413 //.AnyFrameType => {1255 //.AnyFrameType => {
...@@ -2130,30 +1972,6 @@ fn renderContainerField(...@@ -2130,30 +1972,6 @@ fn renderContainerField(
2130 return renderExpressionComma(ais, tree, field.ast.value_expr, space); // value1972 return renderExpressionComma(ais, tree, field.ast.value_expr, space); // value
2131}1973}
21321974
2133fn renderParamDecl(
2134 allocator: *mem.Allocator,
2135 ais: *Ais,
2136 tree: ast.Tree,
2137 param_decl: ast.Node.FnProto.ParamDecl,
2138 space: Space,
2139) Error!void {
2140 try renderDocComments(ais, tree, param_decl, param_decl.doc_comments);
2141
2142 if (param_decl.comptime_token) |comptime_token| {
2143 try renderToken(ais, tree, comptime_token, Space.Space);
2144 }
2145 if (param_decl.noalias_token) |noalias_token| {
2146 try renderToken(ais, tree, noalias_token, Space.Space);
2147 }
2148 if (param_decl.name_token) |name_token| {
2149 try renderToken(ais, tree, name_token, Space.None);
2150 try renderToken(ais, tree, tree.nextToken(name_token), Space.Space); // :
2151 }
2152 switch (param_decl.param_type) {
2153 .any_type, .type_expr => |node| try renderExpression(ais, tree, node, space),
2154 }
2155}
2156
2157fn renderBuiltinCall(1975fn renderBuiltinCall(
2158 ais: *Ais,1976 ais: *Ais,
2159 tree: ast.Tree,1977 tree: ast.Tree,
...@@ -2200,6 +2018,239 @@ fn renderBuiltinCall(...@@ -2200,6 +2018,239 @@ fn renderBuiltinCall(
2200 }2018 }
2201}2019}
22022020
2021fn renderFnProto(ais: *Ais, tree: ast.Tree, fn_proto: ast.Full.FnProto, space: Space) Error!void {
2022 const token_tags = tree.tokens.items(.tag);
2023
2024 const after_fn_token = fn_proto.ast.fn_token + 1;
2025 const lparen = if (token_tags[after_fn_token] == .Identifier) blk: {
2026 try renderToken(ais, tree, fn_proto.ast.fn_token, .Space); // fn
2027 try renderToken(ais, tree, after_fn_token, .None); // name
2028 break :blk after_fn_token + 1;
2029 } else blk: {
2030 try renderToken(ais, tree, fn_proto.ast.fn_token, .Space); // fn
2031 break :blk fn_proto.ast.fn_token + 1;
2032 };
2033 assert(token_tags[lparen] == .LParen);
2034
2035 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
2036 const rparen = blk: {
2037 // The first token for the annotation expressions is the left
2038 // parenthesis, hence the need for two previous tokens.
2039 if (fn_proto.ast.align_expr != 0) {
2040 break :blk tree.firstToken(fn_proto.ast.align_expr) - 3;
2041 }
2042 if (fn_proto.ast.section_expr != 0) {
2043 break :blk tree.firstToken(fn_proto.ast.section_expr) - 3;
2044 }
2045 if (fn_proto.ast.callconv_expr != 0) {
2046 break :blk tree.firstToken(fn_proto.ast.callconv_expr) - 3;
2047 }
2048 if (token_tags[maybe_bang] == .Bang) {
2049 break :blk maybe_bang - 1;
2050 }
2051 break :blk maybe_bang;
2052 };
2053 assert(token_tags[rparen] == .RParen);
2054
2055 // The params list is a sparse set that does *not* include anytype or ... parameters.
2056
2057 if (token_tags[rparen - 1] != .Comma) {
2058 // Render all on one line, no trailing comma.
2059 try renderToken(ais, tree, lparen, .None); // (
2060
2061 var param_i: usize = 0;
2062 var last_param_token = lparen;
2063 while (true) {
2064 last_param_token += 1;
2065 switch (token_tags[last_param_token]) {
2066 .DocComment => {
2067 try renderToken(ais, tree, last_param_token, .Newline);
2068 continue;
2069 },
2070 .Ellipsis3 => {
2071 try renderToken(ais, tree, last_param_token, .None); // ...
2072 break;
2073 },
2074 .Keyword_noalias, .Keyword_comptime => {
2075 try renderToken(ais, tree, last_param_token, .Space);
2076 last_param_token += 1;
2077 },
2078 .Identifier => {},
2079 .Keyword_anytype => {
2080 try renderToken(ais, tree, last_param_token, .None); // anytype
2081 continue;
2082 },
2083 .RParen => break,
2084 .Comma => {
2085 try renderToken(ais, tree, last_param_token, .Space); // ,
2086 last_param_token += 1;
2087 },
2088 else => unreachable,
2089 }
2090 if (token_tags[last_param_token] == .Identifier) {
2091 try renderToken(ais, tree, last_param_token, .None); // name
2092 last_param_token += 1;
2093 try renderToken(ais, tree, last_param_token, .Space); // :
2094 last_param_token += 1;
2095 }
2096 if (token_tags[last_param_token] == .Keyword_anytype) {
2097 try renderToken(ais, tree, last_param_token, .None); // anytype
2098 continue;
2099 }
2100 const param = fn_proto.ast.params[param_i];
2101 param_i += 1;
2102 try renderExpression(ais, tree, param, .None);
2103 last_param_token = tree.lastToken(param) + 1;
2104 }
2105 } else {
2106 // One param per line.
2107 ais.pushIndent();
2108 try renderToken(ais, tree, lparen, .Newline); // (
2109
2110 var param_i: usize = 0;
2111 var last_param_token = lparen;
2112 while (true) {
2113 last_param_token += 1;
2114 switch (token_tags[last_param_token]) {
2115 .DocComment => {
2116 try renderToken(ais, tree, last_param_token, .Newline);
2117 continue;
2118 },
2119 .Ellipsis3 => {
2120 try renderToken(ais, tree, last_param_token, .Comma); // ...
2121 break;
2122 },
2123 .Keyword_noalias, .Keyword_comptime => {
2124 try renderToken(ais, tree, last_param_token, .Space);
2125 last_param_token += 1;
2126 },
2127 .Identifier => {},
2128 .Keyword_anytype => {
2129 try renderToken(ais, tree, last_param_token, .Comma); // anytype
2130 continue;
2131 },
2132 .RParen => break,
2133 else => unreachable,
2134 }
2135 if (token_tags[last_param_token] == .Identifier) {
2136 try renderToken(ais, tree, last_param_token, .None); // name
2137 last_param_token += 1;
2138 try renderToken(ais, tree, last_param_token, .Space); // :
2139 last_param_token += 1;
2140 }
2141 if (token_tags[last_param_token] == .Keyword_anytype) {
2142 try renderToken(ais, tree, last_param_token, .Comma); // anytype
2143 continue;
2144 }
2145 const param = fn_proto.ast.params[param_i];
2146 param_i += 1;
2147 try renderExpression(ais, tree, param, .Comma);
2148 last_param_token = tree.lastToken(param) + 2;
2149 }
2150 ais.popIndent();
2151 }
2152
2153 try renderToken(ais, tree, rparen, .Space); // )
2154
2155 if (fn_proto.ast.align_expr != 0) {
2156 const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1;
2157 const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1;
2158
2159 try renderToken(ais, tree, align_lparen - 1, .None); // align
2160 try renderToken(ais, tree, align_lparen, .None); // (
2161 try renderExpression(ais, tree, fn_proto.ast.align_expr, .None);
2162 try renderToken(ais, tree, align_rparen, .Space); // )
2163 }
2164
2165 if (fn_proto.ast.section_expr != 0) {
2166 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
2167 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
2168
2169 try renderToken(ais, tree, section_lparen - 1, .None); // section
2170 try renderToken(ais, tree, section_lparen, .None); // (
2171 try renderExpression(ais, tree, fn_proto.ast.section_expr, .None);
2172 try renderToken(ais, tree, section_rparen, .Space); // )
2173 }
2174
2175 if (fn_proto.ast.callconv_expr != 0) {
2176 const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1;
2177 const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1;
2178
2179 try renderToken(ais, tree, callconv_lparen - 1, .None); // callconv
2180 try renderToken(ais, tree, callconv_lparen, .None); // (
2181 try renderExpression(ais, tree, fn_proto.ast.callconv_expr, .None);
2182 try renderToken(ais, tree, callconv_rparen, .Space); // )
2183 }
2184
2185 if (token_tags[maybe_bang] == .Bang) {
2186 try renderToken(ais, tree, maybe_bang, .None); // !
2187 }
2188 return renderExpression(ais, tree, fn_proto.ast.return_type, space);
2189}
2190
2191fn renderBlock(
2192 ais: *Ais,
2193 tree: ast.Tree,
2194 lbrace: ast.TokenIndex,
2195 statements: []const ast.Node.Index,
2196 space: Space,
2197) Error!void {
2198 const token_tags = tree.tokens.items(.tag);
2199 const node_tags = tree.nodes.items(.tag);
2200
2201 if (token_tags[lbrace - 1] == .Colon and
2202 token_tags[lbrace - 2] == .Identifier)
2203 {
2204 try renderToken(ais, tree, lbrace - 2, .None);
2205 try renderToken(ais, tree, lbrace - 1, .Space);
2206 }
2207 const nodes_data = tree.nodes.items(.data);
2208
2209 if (statements.len == 0) {
2210 ais.pushIndentNextLine();
2211 try renderToken(ais, tree, lbrace, .None);
2212 ais.popIndent();
2213 const rbrace = lbrace + 1;
2214 return renderToken(ais, tree, rbrace, space);
2215 } else {
2216 ais.pushIndentNextLine();
2217
2218 try renderToken(ais, tree, lbrace, .Newline);
2219
2220 for (statements) |stmt, i| {
2221 switch (node_tags[stmt]) {
2222 .GlobalVarDecl => try renderVarDecl(ais, tree, tree.globalVarDecl(stmt)),
2223 .LocalVarDecl => try renderVarDecl(ais, tree, tree.localVarDecl(stmt)),
2224 .SimpleVarDecl => try renderVarDecl(ais, tree, tree.simpleVarDecl(stmt)),
2225 .AlignedVarDecl => try renderVarDecl(ais, tree, tree.alignedVarDecl(stmt)),
2226 else => {
2227 const semicolon = tree.lastToken(stmt) + 1;
2228 if (token_tags[semicolon] == .Semicolon) {
2229 try renderExpression(ais, tree, stmt, .None);
2230 try renderToken(ais, tree, semicolon, .Newline);
2231 } else {
2232 try renderExpression(ais, tree, stmt, .Newline);
2233 }
2234 },
2235 }
2236
2237 if (i + 1 < statements.len) {
2238 try renderExtraNewline(ais, tree, statements[i + 1]);
2239 }
2240 }
2241 ais.popIndent();
2242 // The rbrace could be +1 or +2 from the last token of the last
2243 // statement in the block because lastToken() does not count semicolons.
2244 const maybe_rbrace = tree.lastToken(statements[statements.len - 1]) + 1;
2245 if (token_tags[maybe_rbrace] == .RBrace) {
2246 return renderToken(ais, tree, maybe_rbrace, space);
2247 } else {
2248 assert(token_tags[maybe_rbrace + 1] == .RBrace);
2249 return renderToken(ais, tree, maybe_rbrace + 1, space);
2250 }
2251 }
2252}
2253
2203/// Render an expression, and the comma that follows it, if it is present in the source.2254/// Render an expression, and the comma that follows it, if it is present in the source.
2204fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {2255fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
2205 const token_tags = tree.tokens.items(.tag);2256 const token_tags = tree.tokens.items(.tag);