authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-05 15:47:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-05 15:47:18-07:00
log16a2562c3f12a5a4fe9875644b593bd571c2734c
tree13158f34a7389ae37f66398a30e6e3a8f3c04ffc
parentcf42ae178deae475c4fdc2d927f91b4980ec8be5

zig fmt: implement container decls


4 files changed, 762 insertions(+), 364 deletions(-)

lib/std/zig/ast.zig+243-17
...@@ -365,6 +365,26 @@ pub const Tree = struct {...@@ -365,6 +365,26 @@ pub const Tree = struct {
365 }365 }
366 },366 },
367367
368 .ContainerDecl,
369 .ContainerDeclComma,
370 .ContainerDeclTwo,
371 .ContainerDeclTwoComma,
372 .ContainerDeclArg,
373 .ContainerDeclArgComma,
374 .TaggedUnion,
375 .TaggedUnionComma,
376 .TaggedUnionTwo,
377 .TaggedUnionTwoComma,
378 .TaggedUnionEnumTag,
379 .TaggedUnionEnumTagComma,
380 => {
381 const main_token = main_tokens[n];
382 switch (token_tags[main_token - 1]) {
383 .Keyword_packed, .Keyword_extern => return main_token - 1,
384 else => return main_token,
385 }
386 },
387
368 .PtrTypeAligned => unreachable, // TODO388 .PtrTypeAligned => unreachable, // TODO
369 .PtrTypeSentinel => unreachable, // TODO389 .PtrTypeSentinel => unreachable, // TODO
370 .PtrType => unreachable, // TODO390 .PtrType => unreachable, // TODO
...@@ -375,10 +395,6 @@ pub const Tree = struct {...@@ -375,10 +395,6 @@ pub const Tree = struct {
375 .While => unreachable, // TODO395 .While => unreachable, // TODO
376 .ForSimple => unreachable, // TODO396 .ForSimple => unreachable, // TODO
377 .For => unreachable, // TODO397 .For => unreachable, // TODO
378 .ContainerDecl => unreachable, // TODO
379 .ContainerDeclArg => unreachable, // TODO
380 .TaggedUnion => unreachable, // TODO
381 .TaggedUnionEnumTag => unreachable, // TODO
382 .AsmOutput => unreachable, // TODO398 .AsmOutput => unreachable, // TODO
383 .AsmInput => unreachable, // TODO399 .AsmInput => unreachable, // TODO
384 .ErrorValue => unreachable, // TODO400 .ErrorValue => unreachable, // TODO
...@@ -408,6 +424,7 @@ pub const Tree = struct {...@@ -408,6 +424,7 @@ pub const Tree = struct {
408 .Break,424 .Break,
409 .Return,425 .Return,
410 .Nosuspend,426 .Nosuspend,
427 .Comptime,
411 => n = datas[n].lhs,428 => n = datas[n].lhs,
412429
413 .TestDecl,430 .TestDecl,
...@@ -455,7 +472,6 @@ pub const Tree = struct {...@@ -455,7 +472,6 @@ pub const Tree = struct {
455 .BoolOr,472 .BoolOr,
456 .AnyFrameType,473 .AnyFrameType,
457 .ErrorUnion,474 .ErrorUnion,
458 .Comptime,
459 .IfSimple,475 .IfSimple,
460 .WhileSimple,476 .WhileSimple,
461 => n = datas[n].rhs,477 => n = datas[n].rhs,
...@@ -490,13 +506,37 @@ pub const Tree = struct {...@@ -490,13 +506,37 @@ pub const Tree = struct {
490 }506 }
491 n = tree.extra_data[params.end - 1]; // last parameter507 n = tree.extra_data[params.end - 1]; // last parameter
492 },508 },
493 .Block => {509 .ContainerDeclArg => {
510 const members = tree.extraData(datas[n].rhs, Node.SubRange);
511 if (members.end - members.start == 0) {
512 end_offset += 1; // for the rparen
513 n = datas[n].lhs;
514 } else {
515 end_offset += 1; // for the rbrace
516 n = tree.extra_data[members.end - 1]; // last parameter
517 }
518 },
519 .ContainerDeclArgComma => {
520 const members = tree.extraData(datas[n].rhs, Node.SubRange);
521 assert(members.end - members.start > 0);
522 end_offset += 2; // for the comma + rbrace
523 n = tree.extra_data[members.end - 1]; // last parameter
524 },
525 .Block,
526 .ContainerDecl,
527 .TaggedUnion,
528 => {
494 end_offset += 1; // for the rbrace529 end_offset += 1; // for the rbrace
495 if (datas[n].rhs - datas[n].lhs == 0) {530 if (datas[n].rhs - datas[n].lhs == 0) {
496 return main_tokens[n] + end_offset;531 return main_tokens[n] + end_offset;
497 }532 }
498 n = tree.extra_data[datas[n].rhs - 1]; // last statement533 n = tree.extra_data[datas[n].rhs - 1]; // last statement
499 },534 },
535 .ContainerDeclComma, .TaggedUnionComma => {
536 assert(datas[n].rhs - datas[n].lhs > 0);
537 end_offset += 2; // for the comma + rbrace
538 n = tree.extra_data[datas[n].rhs - 1]; // last member
539 },
500 .CallOne,540 .CallOne,
501 .ArrayAccess,541 .ArrayAccess,
502 => {542 => {
...@@ -511,6 +551,8 @@ pub const Tree = struct {...@@ -511,6 +551,8 @@ pub const Tree = struct {
511 .BuiltinCallTwo,551 .BuiltinCallTwo,
512 .BlockTwo,552 .BlockTwo,
513 .StructInitDotTwo,553 .StructInitDotTwo,
554 .ContainerDeclTwo,
555 .TaggedUnionTwo,
514 => {556 => {
515 end_offset += 1; // for the rparen/rbrace557 end_offset += 1; // for the rparen/rbrace
516 if (datas[n].rhs != 0) {558 if (datas[n].rhs != 0) {
...@@ -523,6 +565,8 @@ pub const Tree = struct {...@@ -523,6 +565,8 @@ pub const Tree = struct {
523 },565 },
524 .ArrayInitDotTwoComma,566 .ArrayInitDotTwoComma,
525 .StructInitDotTwoComma,567 .StructInitDotTwoComma,
568 .ContainerDeclTwoComma,
569 .TaggedUnionTwoComma,
526 => {570 => {
527 end_offset += 2; // for the comma + rbrace571 end_offset += 2; // for the comma + rbrace
528 if (datas[n].rhs != 0) {572 if (datas[n].rhs != 0) {
...@@ -589,6 +633,38 @@ pub const Tree = struct {...@@ -589,6 +633,38 @@ pub const Tree = struct {
589 }633 }
590 }634 }
591 },635 },
636 .ContainerFieldInit => {
637 if (datas[n].rhs != 0) {
638 n = datas[n].rhs;
639 } else if (datas[n].lhs != 0) {
640 n = datas[n].lhs;
641 } else {
642 return main_tokens[n] + end_offset;
643 }
644 },
645 .ContainerFieldAlign => {
646 if (datas[n].rhs != 0) {
647 end_offset += 1; // for the rparen
648 n = datas[n].rhs;
649 } else if (datas[n].lhs != 0) {
650 n = datas[n].lhs;
651 } else {
652 return main_tokens[n] + end_offset;
653 }
654 },
655 .ContainerField => {
656 const extra = tree.extraData(datas[n].rhs, Node.ContainerField);
657 if (extra.value_expr != 0) {
658 n = extra.value_expr;
659 } else if (extra.align_expr != 0) {
660 end_offset += 1; // for the rparen
661 n = extra.align_expr;
662 } else if (datas[n].lhs != 0) {
663 n = datas[n].lhs;
664 } else {
665 return main_tokens[n] + end_offset;
666 }
667 },
592668
593 // These are not supported by lastToken() because implementation would669 // These are not supported by lastToken() because implementation would
594 // require recursion due to the optional comma followed by rbrace.670 // require recursion due to the optional comma followed by rbrace.
...@@ -600,10 +676,9 @@ pub const Tree = struct {...@@ -600,10 +676,9 @@ pub const Tree = struct {
600 .StructInit => unreachable,676 .StructInit => unreachable,
601 .StructInitOne => unreachable,677 .StructInitOne => unreachable,
602 .StructInitDot => unreachable,678 .StructInitDot => unreachable,
603 .ContainerFieldInit => unreachable,
604 .ContainerFieldAlign => unreachable,
605 .ContainerField => unreachable,
606679
680 .TaggedUnionEnumTag => unreachable, // TODO
681 .TaggedUnionEnumTagComma => unreachable, // TODO
607 .Switch => unreachable, // TODO682 .Switch => unreachable, // TODO
608 .If => unreachable, // TODO683 .If => unreachable, // TODO
609 .Continue => unreachable, // TODO684 .Continue => unreachable, // TODO
...@@ -631,10 +706,6 @@ pub const Tree = struct {...@@ -631,10 +706,6 @@ pub const Tree = struct {
631 .FnProtoMulti => unreachable, // TODO706 .FnProtoMulti => unreachable, // TODO
632 .FnProtoOne => unreachable, // TODO707 .FnProtoOne => unreachable, // TODO
633 .FnProto => unreachable, // TODO708 .FnProto => unreachable, // TODO
634 .ContainerDecl => unreachable, // TODO
635 .ContainerDeclArg => unreachable, // TODO
636 .TaggedUnion => unreachable, // TODO
637 .TaggedUnionEnumTag => unreachable, // TODO
638 .AsmOutput => unreachable, // TODO709 .AsmOutput => unreachable, // TODO
639 .AsmInput => unreachable, // TODO710 .AsmInput => unreachable, // TODO
640 .ErrorValue => unreachable, // TODO711 .ErrorValue => unreachable, // TODO
...@@ -952,6 +1023,93 @@ pub const Tree = struct {...@@ -952,6 +1023,93 @@ pub const Tree = struct {
952 };1023 };
953 }1024 }
9541025
1026 pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ContainerDecl {
1027 assert(tree.nodes.items(.tag)[node] == .ContainerDeclTwo or
1028 tree.nodes.items(.tag)[node] == .ContainerDeclTwoComma);
1029 const data = tree.nodes.items(.data)[node];
1030 buffer.* = .{ data.lhs, data.rhs };
1031 const members = if (data.rhs != 0)
1032 buffer[0..2]
1033 else if (data.lhs != 0)
1034 buffer[0..1]
1035 else
1036 buffer[0..0];
1037 return tree.fullContainerDecl(.{
1038 .main_token = tree.nodes.items(.main_token)[node],
1039 .enum_token = null,
1040 .members = members,
1041 .arg = 0,
1042 });
1043 }
1044
1045 pub fn containerDecl(tree: Tree, node: Node.Index) Full.ContainerDecl {
1046 assert(tree.nodes.items(.tag)[node] == .ContainerDecl or
1047 tree.nodes.items(.tag)[node] == .ContainerDeclComma);
1048 const data = tree.nodes.items(.data)[node];
1049 return tree.fullContainerDecl(.{
1050 .main_token = tree.nodes.items(.main_token)[node],
1051 .enum_token = null,
1052 .members = tree.extra_data[data.lhs..data.rhs],
1053 .arg = 0,
1054 });
1055 }
1056
1057 pub fn containerDeclArg(tree: Tree, node: Node.Index) Full.ContainerDecl {
1058 assert(tree.nodes.items(.tag)[node] == .ContainerDeclArg);
1059 const data = tree.nodes.items(.data)[node];
1060 const members_range = tree.extraData(data.rhs, Node.SubRange);
1061 return tree.fullContainerDecl(.{
1062 .main_token = tree.nodes.items(.main_token)[node],
1063 .enum_token = null,
1064 .members = tree.extra_data[members_range.start..members_range.end],
1065 .arg = data.lhs,
1066 });
1067 }
1068
1069 pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ContainerDecl {
1070 assert(tree.nodes.items(.tag)[node] == .TaggedUnionTwo);
1071 const data = tree.nodes.items(.data)[node];
1072 buffer.* = .{ data.lhs, data.rhs };
1073 const members = if (data.rhs != 0)
1074 buffer[0..2]
1075 else if (data.lhs != 0)
1076 buffer[0..1]
1077 else
1078 buffer[0..0];
1079 const main_token = tree.nodes.items(.main_token)[node];
1080 return tree.fullContainerDecl(.{
1081 .main_token = main_token,
1082 .enum_token = main_token + 2, // union lparen enum
1083 .members = members,
1084 .arg = 0,
1085 });
1086 }
1087
1088 pub fn taggedUnion(tree: Tree, node: Node.Index) Full.ContainerDecl {
1089 assert(tree.nodes.items(.tag)[node] == .TaggedUnion);
1090 const data = tree.nodes.items(.data)[node];
1091 const main_token = tree.nodes.items(.main_token)[node];
1092 return tree.fullContainerDecl(.{
1093 .main_token = main_token,
1094 .enum_token = main_token + 2, // union lparen enum
1095 .members = tree.extra_data[data.lhs..data.rhs],
1096 .arg = 0,
1097 });
1098 }
1099
1100 pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) Full.ContainerDecl {
1101 assert(tree.nodes.items(.tag)[node] == .TaggedUnionEnumTag);
1102 const data = tree.nodes.items(.data)[node];
1103 const members_range = tree.extraData(data.rhs, Node.SubRange);
1104 const main_token = tree.nodes.items(.main_token)[node];
1105 return tree.fullContainerDecl(.{
1106 .main_token = main_token,
1107 .enum_token = main_token + 2, // union lparen enum
1108 .members = tree.extra_data[data.lhs..data.rhs],
1109 .arg = data.lhs,
1110 });
1111 }
1112
955 fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl {1113 fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl {
956 const token_tags = tree.tokens.items(.tag);1114 const token_tags = tree.tokens.items(.tag);
957 var result: Full.VarDecl = .{1115 var result: Full.VarDecl = .{
...@@ -1031,6 +1189,19 @@ pub const Tree = struct {...@@ -1031,6 +1189,19 @@ pub const Tree = struct {
1031 };1189 };
1032 return result;1190 return result;
1033 }1191 }
1192
1193 fn fullContainerDecl(tree: Tree, info: Full.ContainerDecl.Ast) Full.ContainerDecl {
1194 const token_tags = tree.tokens.items(.tag);
1195 var result: Full.ContainerDecl = .{
1196 .ast = info,
1197 .layout_token = null,
1198 };
1199 switch (token_tags[info.main_token - 1]) {
1200 .Keyword_extern, .Keyword_packed => result.layout_token = info.main_token - 1,
1201 else => {},
1202 }
1203 return result;
1204 }
1034};1205};
10351206
1036/// Fully assembled AST node information.1207/// Fully assembled AST node information.
...@@ -1125,6 +1296,19 @@ pub const Full = struct {...@@ -1125,6 +1296,19 @@ pub const Full = struct {
1125 elem_type: Node.Index,1296 elem_type: Node.Index,
1126 };1297 };
1127 };1298 };
1299
1300 pub const ContainerDecl = struct {
1301 layout_token: ?TokenIndex,
1302 ast: Ast,
1303
1304 pub const Ast = struct {
1305 main_token: TokenIndex,
1306 /// Populated when main_token is Keyword_union.
1307 enum_token: ?TokenIndex,
1308 members: []const Node.Index,
1309 arg: Node.Index,
1310 };
1311 };
1128};1312};
11291313
1130pub const Error = union(enum) {1314pub const Error = union(enum) {
...@@ -1543,9 +1727,11 @@ pub const Node = struct {...@@ -1543,9 +1727,11 @@ pub const Node = struct {
1543 StructInitOne,1727 StructInitOne,
1544 /// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted.1728 /// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted.
1545 /// main_token is the lbrace.1729 /// main_token is the lbrace.
1730 /// No trailing comma before the rbrace.
1546 StructInitDotTwo,1731 StructInitDotTwo,
1547 /// Same as `StructInitDotTwo` except there is known to be a trailing comma1732 /// Same as `StructInitDotTwo` except there is known to be a trailing comma
1548 /// before the final rbrace.1733 /// before the final rbrace. This tag exists to facilitate lastToken() implemented
1734 /// without recursion.
1549 StructInitDotTwoComma,1735 StructInitDotTwoComma,
1550 /// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`.1736 /// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`.
1551 /// main_token is the lbrace.1737 /// main_token is the lbrace.
...@@ -1655,21 +1841,50 @@ pub const Node = struct {...@@ -1655,21 +1841,50 @@ pub const Node = struct {
1655 /// `error{a, b}`.1841 /// `error{a, b}`.
1656 /// lhs and rhs both unused.1842 /// lhs and rhs both unused.
1657 ErrorSetDecl,1843 ErrorSetDecl,
1658 /// `struct {}`, `union {}`, etc. `sub_list[lhs..rhs]`.1844 /// `struct {}`, `union {}`, `opaque {}`, `enum {}`. `extra_data[lhs..rhs]`.
1845 /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
1659 ContainerDecl,1846 ContainerDecl,
1660 /// `union(lhs)` / `enum(lhs)`. `sub_range_list[rhs]`.1847 /// Same as ContainerDecl but there is known to be a trailing comma before the rbrace.
1848 ContainerDeclComma,
1849 /// `struct {lhs, rhs}`, `union {lhs, rhs}`, `opaque {lhs, rhs}`, `enum {lhs, rhs}`.
1850 /// lhs or rhs can be omitted.
1851 /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
1852 ContainerDeclTwo,
1853 /// Same as ContainerDeclTwo except there is known to be a trailing comma
1854 /// before the rbrace.
1855 ContainerDeclTwoComma,
1856 /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
1661 ContainerDeclArg,1857 ContainerDeclArg,
1858 /// Same as ContainerDeclArg but there is known to be a trailing comma before the rbrace.
1859 ContainerDeclArgComma,
1662 /// `union(enum) {}`. `sub_list[lhs..rhs]`.1860 /// `union(enum) {}`. `sub_list[lhs..rhs]`.
1663 /// Note that tagged unions with explicitly provided enums are represented1861 /// Note that tagged unions with explicitly provided enums are represented
1664 /// by `ContainerDeclArg`.1862 /// by `ContainerDeclArg`.
1665 TaggedUnion,1863 TaggedUnion,
1666 /// `union(enum(lhs)) {}`. `sub_list_range[rhs]`.1864 /// Same as TaggedUnion but there is known to be a trailing comma before the rbrace.
1865 TaggedUnionComma,
1866 /// `union(enum) {lhs, rhs}`. lhs or rhs may be omitted.
1867 /// Note that tagged unions with explicitly provided enums are represented
1868 /// by `ContainerDeclArg`.
1869 TaggedUnionTwo,
1870 /// Same as TaggedUnionTwo but there is known to be a trailing comma before the rbrace.
1871 TaggedUnionTwoComma,
1872 /// `union(enum(lhs)) {}`. `SubRange[rhs]`.
1667 TaggedUnionEnumTag,1873 TaggedUnionEnumTag,
1874 /// Same as TaggedUnionEnumTag but there is known to be a trailing comma
1875 /// before the rbrace.
1876 TaggedUnionEnumTagComma,
1668 /// `a: lhs = rhs,`. lhs and rhs can be omitted.1877 /// `a: lhs = rhs,`. lhs and rhs can be omitted.
1878 /// main_token is the field name identifier.
1879 /// lastToken() does not include the possible trailing comma.
1669 ContainerFieldInit,1880 ContainerFieldInit,
1670 /// `a: lhs align(rhs),`. rhs can be omitted.1881 /// `a: lhs align(rhs),`. rhs can be omitted.
1882 /// main_token is the field name identifier.
1883 /// lastToken() does not include the possible trailing comma.
1671 ContainerFieldAlign,1884 ContainerFieldAlign,
1672 /// `a: lhs align(c) = d,`. `container_field_list[rhs]`.1885 /// `a: lhs align(c) = d,`. `container_field_list[rhs]`.
1886 /// main_token is the field name identifier.
1887 /// lastToken() does not include the possible trailing comma.
1673 ContainerField,1888 ContainerField,
1674 /// `anytype`. both lhs and rhs unused.1889 /// `anytype`. both lhs and rhs unused.
1675 /// Used by `ContainerField`.1890 /// Used by `ContainerField`.
...@@ -1699,6 +1914,17 @@ pub const Node = struct {...@@ -1699,6 +1914,17 @@ pub const Node = struct {
1699 ErrorValue,1914 ErrorValue,
1700 /// `lhs!rhs`. main_token is the `!`.1915 /// `lhs!rhs`. main_token is the `!`.
1701 ErrorUnion,1916 ErrorUnion,
1917
1918 pub fn isContainerField(tag: Tag) bool {
1919 return switch (tag) {
1920 .ContainerFieldInit,
1921 .ContainerFieldAlign,
1922 .ContainerField,
1923 => true,
1924
1925 else => false,
1926 };
1927 }
1702 };1928 };
17031929
1704 pub const Data = struct {1930 pub const Data = struct {
lib/std/zig/parse.zig+330-190
...@@ -64,9 +64,10 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {...@@ -64,9 +64,10 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
64 .rhs = undefined,64 .rhs = undefined,
65 },65 },
66 });66 });
67 const root_decls = try parser.parseContainerMembers(true);67 const root_members = try parser.parseContainerMembers();
68 // parseContainerMembers will try to skip as much68 const root_decls = try root_members.toSpan(&parser);
69 // invalid tokens as it can, so we are now at EOF.69 // parseContainerMembers will try to skip as much invalid tokens as
70 // it can, so we are now at EOF.
70 assert(parser.token_tags[parser.tok_i] == .Eof);71 assert(parser.token_tags[parser.tok_i] == .Eof);
71 parser.nodes.items(.data)[0] = .{72 parser.nodes.items(.data)[0] = .{
72 .lhs = root_decls.start,73 .lhs = root_decls.start,
...@@ -108,6 +109,22 @@ const Parser = struct {...@@ -108,6 +109,22 @@ const Parser = struct {
108 }109 }
109 };110 };
110111
112 const Members = struct {
113 len: usize,
114 lhs: Node.Index,
115 rhs: Node.Index,
116 trailing_comma: bool,
117
118 fn toSpan(self: Members, p: *Parser) !Node.SubRange {
119 if (self.len <= 2) {
120 const nodes = [2]Node.Index{ self.lhs, self.rhs };
121 return p.listToSpan(nodes[0..self.len]);
122 } else {
123 return Node.SubRange{ .start = self.lhs, .end = self.rhs };
124 }
125 }
126 };
127
111 fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange {128 fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange {
112 try p.extra_data.appendSlice(p.gpa, list);129 try p.extra_data.appendSlice(p.gpa, list);
113 return Node.SubRange{130 return Node.SubRange{
...@@ -151,169 +168,225 @@ const Parser = struct {...@@ -151,169 +168,225 @@ const Parser = struct {
151 /// / ContainerField COMMA ContainerMembers168 /// / ContainerField COMMA ContainerMembers
152 /// / ContainerField169 /// / ContainerField
153 /// /170 /// /
154 fn parseContainerMembers(p: *Parser, top_level: bool) !Node.SubRange {171 /// TopLevelComptime <- KEYWORD_comptime BlockExpr
172 fn parseContainerMembers(p: *Parser) !Members {
155 var list = std.ArrayList(Node.Index).init(p.gpa);173 var list = std.ArrayList(Node.Index).init(p.gpa);
156 defer list.deinit();174 defer list.deinit();
157175
158 var field_state: union(enum) {176 var field_state: union(enum) {
159 /// no fields have been seen177 /// No fields have been seen.
160 none,178 none,
161 /// currently parsing fields179 /// Currently parsing fields.
162 seen,180 seen,
163 /// saw fields and then a declaration after them.181 /// Saw fields and then a declaration after them.
164 /// payload is first token of previous declaration.182 /// Payload is first token of previous declaration.
165 end: TokenIndex,183 end: Node.Index,
166 /// ther was a declaration between fields, don't report more errors184 /// There was a declaration between fields, don't report more errors.
167 err,185 err,
168 } = .none;186 } = .none;
169187
170 // Skip container doc comments.188 // Skip container doc comments.
171 while (p.eatToken(.ContainerDocComment)) |_| {}189 while (p.eatToken(.ContainerDocComment)) |_| {}
172190
191 var trailing_comma = false;
173 while (true) {192 while (true) {
174 const doc_comment = p.eatDocComments();193 const doc_comment = p.eatDocComments();
175194
176 const test_decl_node = p.parseTestDecl() catch |err| switch (err) {195 switch (p.token_tags[p.tok_i]) {
177 error.OutOfMemory => return error.OutOfMemory,196 .Keyword_test => {
178 error.ParseError => {197 const test_decl_node = try p.expectTestDeclRecoverable();
179 p.findNextContainerMember();198 if (test_decl_node != 0) {
180 continue;199 if (field_state == .seen) {
200 field_state = .{ .end = test_decl_node };
201 }
202 try list.append(test_decl_node);
203 }
204 trailing_comma = false;
181 },205 },
182 };206 .Keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
183 if (test_decl_node != 0) {207 .Identifier => {
184 if (field_state == .seen) {208 p.tok_i += 1;
185 field_state = .{ .end = p.nodes.items(.main_token)[test_decl_node] };209 const container_field = try p.expectContainerFieldRecoverable();
186 }210 if (container_field != 0) {
187 try list.append(test_decl_node);211 switch (field_state) {
188 continue;212 .none => field_state = .seen,
189 }213 .err, .seen => {},
190214 .end => |node| {
191 const comptime_node = p.parseTopLevelComptime() catch |err| switch (err) {215 try p.warn(.{
192 error.OutOfMemory => return error.OutOfMemory,216 .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] },
193 error.ParseError => {217 });
194 p.findNextContainerMember();218 // Continue parsing; error will be reported later.
195 continue;219 field_state = .err;
220 },
221 }
222 try list.append(container_field);
223 switch (p.token_tags[p.tok_i]) {
224 .Comma => {
225 p.tok_i += 1;
226 trailing_comma = true;
227 continue;
228 },
229 .RBrace, .Eof => {
230 trailing_comma = false;
231 break;
232 },
233 else => {},
234 }
235 // There is not allowed to be a decl after a field with no comma.
236 // Report error but recover parser.
237 try p.warn(.{
238 .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
239 });
240 p.findNextContainerMember();
241 }
242 },
243 .LBrace => {
244 const comptime_token = p.nextToken();
245 const block = p.parseBlock() catch |err| switch (err) {
246 error.OutOfMemory => return error.OutOfMemory,
247 error.ParseError => blk: {
248 p.findNextContainerMember();
249 break :blk null_node;
250 },
251 };
252 if (block != 0) {
253 const comptime_node = try p.addNode(.{
254 .tag = .Comptime,
255 .main_token = comptime_token,
256 .data = .{
257 .lhs = block,
258 .rhs = undefined,
259 },
260 });
261 if (field_state == .seen) {
262 field_state = .{ .end = comptime_node };
263 }
264 try list.append(comptime_node);
265 }
266 trailing_comma = false;
267 },
268 else => {
269 p.tok_i += 1;
270 try p.warn(.{ .ExpectedBlockOrField = .{ .token = p.tok_i } });
271 },
196 },272 },
197 };273 .Keyword_pub => {
198 if (comptime_node != 0) {274 p.tok_i += 1;
199 if (field_state == .seen) {275 const top_level_decl = try p.expectTopLevelDeclRecoverable();
200 field_state = .{ .end = p.nodes.items(.main_token)[comptime_node] };276 if (top_level_decl != 0) {
201 }277 if (field_state == .seen) {
202 try list.append(comptime_node);278 field_state = .{ .end = top_level_decl };
203 continue;279 }
204 }280 try list.append(top_level_decl);
205281 }
206 const visib_token = p.eatToken(.Keyword_pub);282 trailing_comma = false;
207
208 const top_level_decl = p.parseTopLevelDecl() catch |err| switch (err) {
209 error.OutOfMemory => return error.OutOfMemory,
210 error.ParseError => {
211 p.findNextContainerMember();
212 continue;
213 },283 },
214 };284 .Keyword_usingnamespace => {
215 if (top_level_decl != 0) {285 const node = try p.expectUsingNamespaceRecoverable();
216 if (field_state == .seen) {286 if (node != 0) {
217 field_state = .{287 if (field_state == .seen) {
218 .end = visib_token orelse p.nodes.items(.main_token)[top_level_decl],288 field_state = .{ .end = node };
219 };289 }
220 }290 try list.append(node);
221 try list.append(top_level_decl);291 }
222 continue;292 trailing_comma = false;
223 }
224
225 if (visib_token != null) {
226 try p.warn(.{ .ExpectedPubItem = .{ .token = p.tok_i } });
227 // ignore this pub
228 continue;
229 }
230
231 const container_field = p.parseContainerField() catch |err| switch (err) {
232 error.OutOfMemory => return error.OutOfMemory,
233 error.ParseError => {
234 // attempt to recover
235 p.findNextContainerMember();
236 continue;
237 },293 },
238 };294 .Keyword_const,
239 if (container_field != 0) {295 .Keyword_var,
240 switch (field_state) {296 .Keyword_threadlocal,
241 .none => field_state = .seen,297 .Keyword_export,
242 .err, .seen => {},298 .Keyword_extern,
243 .end => |tok| {299 .Keyword_inline,
244 try p.warn(.{ .DeclBetweenFields = .{ .token = tok } });300 .Keyword_noinline,
245 // continue parsing, error will be reported later301 .Keyword_fn,
246 field_state = .err;302 => {
247 },303 const top_level_decl = try p.expectTopLevelDeclRecoverable();
248 }304 if (top_level_decl != 0) {
249 try list.append(container_field);305 if (field_state == .seen) {
250 const comma = p.eatToken(.Comma) orelse {306 field_state = .{ .end = top_level_decl };
251 // try to continue parsing307 }
252 const index = p.tok_i;308 try list.append(top_level_decl);
253 p.findNextContainerMember();309 }
254 const next = p.token_tags[p.tok_i];310 trailing_comma = false;
255 switch (next) {311 },
256 .Eof => {312 .Identifier => {
257 // no invalid tokens were found313 const container_field = try p.expectContainerFieldRecoverable();
258 if (index == p.tok_i) break;314 if (container_field != 0) {
259315 switch (field_state) {
260 // Invalid tokens, add error and exit316 .none => field_state = .seen,
261 try p.warn(.{317 .err, .seen => {},
262 .ExpectedToken = .{ .token = index, .expected_id = .Comma },318 .end => |node| {
263 });319 try p.warn(.{
264 break;320 .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] },
265 },321 });
266 else => {322 // Continue parsing; error will be reported later.
267 if (next == .RBrace) {323 field_state = .err;
268 if (!top_level) break;324 },
325 }
326 try list.append(container_field);
327 switch (p.token_tags[p.tok_i]) {
328 .Comma => {
269 p.tok_i += 1;329 p.tok_i += 1;
270 }330 trailing_comma = true;
271331 continue;
272 // add error and continue332 },
273 try p.warn(.{333 .RBrace, .Eof => {
274 .ExpectedToken = .{ .token = index, .expected_id = .Comma },334 trailing_comma = false;
275 });335 break;
276 continue;336 },
277 },337 else => {},
338 }
339 // There is not allowed to be a decl after a field with no comma.
340 // Report error but recover parser.
341 try p.warn(.{
342 .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
343 });
344 p.findNextContainerMember();
278 }345 }
279 };
280 continue;
281 }
282
283 // Dangling doc comment
284 if (doc_comment) |tok| {
285 try p.warn(.{
286 .UnattachedDocComment = .{ .token = tok },
287 });
288 }
289
290 const next = p.token_tags[p.tok_i];
291 switch (next) {
292 .Eof => break,
293 .Keyword_comptime => {
294 p.tok_i += 1;
295 try p.warn(.{
296 .ExpectedBlockOrField = .{ .token = p.tok_i },
297 });
298 },346 },
299 else => {347 .Eof, .RBrace => {
300 const index = p.tok_i;348 if (doc_comment) |tok| {
301 if (next == .RBrace) {349 try p.warn(.{ .UnattachedDocComment = .{ .token = tok } });
302 if (!top_level) break;
303 p.tok_i += 1;
304 }350 }
305351 break;
306 // this was likely not supposed to end yet,352 },
307 // try to find the next declaration353 else => {
354 try p.warn(.{ .ExpectedContainerMembers = .{ .token = p.tok_i } });
355 // This was likely not supposed to end yet; try to find the next declaration.
308 p.findNextContainerMember();356 p.findNextContainerMember();
309 try p.warn(.{
310 .ExpectedContainerMembers = .{ .token = index },
311 });
312 },357 },
313 }358 }
314 }359 }
315360
316 return p.listToSpan(list.items);361 switch (list.items.len) {
362 0 => return Members{
363 .len = 0,
364 .lhs = 0,
365 .rhs = 0,
366 .trailing_comma = trailing_comma,
367 },
368 1 => return Members{
369 .len = 1,
370 .lhs = list.items[0],
371 .rhs = 0,
372 .trailing_comma = trailing_comma,
373 },
374 2 => return Members{
375 .len = 2,
376 .lhs = list.items[0],
377 .rhs = list.items[1],
378 .trailing_comma = trailing_comma,
379 },
380 else => {
381 const span = try p.listToSpan(list.items);
382 return Members{
383 .len = list.items.len,
384 .lhs = span.start,
385 .rhs = span.end,
386 .trailing_comma = trailing_comma,
387 };
388 },
389 }
317 }390 }
318391
319 /// Attempts to find next container member by searching for certain tokens392 /// Attempts to find next container member by searching for certain tokens
...@@ -398,44 +471,36 @@ const Parser = struct {...@@ -398,44 +471,36 @@ const Parser = struct {
398 }471 }
399472
400 /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block473 /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block
401 fn parseTestDecl(p: *Parser) !Node.Index {474 fn expectTestDecl(p: *Parser) !Node.Index {
402 const test_token = p.eatToken(.Keyword_test) orelse return null_node;475 const test_token = try p.expectToken(.Keyword_test);
403 const name_token = try p.expectToken(.StringLiteral);476 const name_token = p.eatToken(.StringLiteral);
404 const block_node = try p.parseBlock();477 const block_node = try p.parseBlock();
405 if (block_node == 0) return p.fail(.{ .ExpectedLBrace = .{ .token = p.tok_i } });478 if (block_node == 0) return p.fail(.{ .ExpectedLBrace = .{ .token = p.tok_i } });
406 return p.addNode(.{479 return p.addNode(.{
407 .tag = .TestDecl,480 .tag = .TestDecl,
408 .main_token = test_token,481 .main_token = test_token,
409 .data = .{482 .data = .{
410 .lhs = name_token,483 .lhs = name_token orelse 0,
411 .rhs = block_node,484 .rhs = block_node,
412 },485 },
413 });486 });
414 }487 }
415488
416 /// TopLevelComptime <- KEYWORD_comptime BlockExpr489 fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
417 fn parseTopLevelComptime(p: *Parser) !Node.Index {490 return p.expectTestDecl() catch |err| switch (err) {
418 if (p.token_tags[p.tok_i] == .Keyword_comptime and491 error.OutOfMemory => return error.OutOfMemory,
419 p.token_tags[p.tok_i + 1] == .LBrace)492 error.ParseError => {
420 {493 p.findNextContainerMember();
421 return p.addNode(.{494 return null_node;
422 .tag = .Comptime,495 },
423 .main_token = p.nextToken(),496 };
424 .data = .{
425 .lhs = try p.parseBlock(),
426 .rhs = undefined,
427 },
428 });
429 } else {
430 return null_node;
431 }
432 }497 }
433498
434 /// TopLevelDecl499 /// TopLevelDecl
435 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)500 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
436 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl501 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
437 /// / KEYWORD_usingnamespace Expr SEMICOLON502 /// / KEYWORD_usingnamespace Expr SEMICOLON
438 fn parseTopLevelDecl(p: *Parser) !Node.Index {503 fn expectTopLevelDecl(p: *Parser) !Node.Index {
439 const extern_export_inline_token = p.nextToken();504 const extern_export_inline_token = p.nextToken();
440 var expect_fn: bool = false;505 var expect_fn: bool = false;
441 var exported: bool = false;506 var exported: bool = false;
...@@ -496,7 +561,21 @@ const Parser = struct {...@@ -496,7 +561,21 @@ const Parser = struct {
496 return p.fail(.{ .ExpectedVarDeclOrFn = .{ .token = p.tok_i } });561 return p.fail(.{ .ExpectedVarDeclOrFn = .{ .token = p.tok_i } });
497 }562 }
498563
499 const usingnamespace_token = p.eatToken(.Keyword_usingnamespace) orelse return null_node;564 return p.expectUsingNamespace();
565 }
566
567 fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
568 return p.expectTopLevelDecl() catch |err| switch (err) {
569 error.OutOfMemory => return error.OutOfMemory,
570 error.ParseError => {
571 p.findNextContainerMember();
572 return null_node;
573 },
574 };
575 }
576
577 fn expectUsingNamespace(p: *Parser) !Node.Index {
578 const usingnamespace_token = try p.expectToken(.Keyword_usingnamespace);
500 const expr = try p.expectExpr();579 const expr = try p.expectExpr();
501 const semicolon_token = try p.expectToken(.Semicolon);580 const semicolon_token = try p.expectToken(.Semicolon);
502 try p.parseAppendedDocComment(semicolon_token);581 try p.parseAppendedDocComment(semicolon_token);
...@@ -510,6 +589,16 @@ const Parser = struct {...@@ -510,6 +589,16 @@ const Parser = struct {
510 });589 });
511 }590 }
512591
592 fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
593 return p.expectUsingNamespace() catch |err| switch (err) {
594 error.OutOfMemory => return error.OutOfMemory,
595 error.ParseError => {
596 p.findNextContainerMember();
597 return null_node;
598 },
599 };
600 }
601
513 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)602 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
514 fn parseFnProto(p: *Parser) !Node.Index {603 fn parseFnProto(p: *Parser) !Node.Index {
515 const fn_token = p.eatToken(.Keyword_fn) orelse return null_node;604 const fn_token = p.eatToken(.Keyword_fn) orelse return null_node;
...@@ -648,12 +737,9 @@ const Parser = struct {...@@ -648,12 +737,9 @@ const Parser = struct {
648 }737 }
649738
650 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?739 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
651 fn parseContainerField(p: *Parser) !Node.Index {740 fn expectContainerField(p: *Parser) !Node.Index {
652 const comptime_token = p.eatToken(.Keyword_comptime);741 const comptime_token = p.eatToken(.Keyword_comptime);
653 const name_token = p.eatToken(.Identifier) orelse {742 const name_token = try p.expectToken(.Identifier);
654 if (comptime_token) |_| p.tok_i -= 1;
655 return null_node;
656 };
657743
658 var align_expr: Node.Index = 0;744 var align_expr: Node.Index = 0;
659 var type_expr: Node.Index = 0;745 var type_expr: Node.Index = 0;
...@@ -708,6 +794,16 @@ const Parser = struct {...@@ -708,6 +794,16 @@ const Parser = struct {
708 }794 }
709 }795 }
710796
797 fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
798 return p.expectContainerField() catch |err| switch (err) {
799 error.OutOfMemory => return error.OutOfMemory,
800 error.ParseError => {
801 p.findNextContainerMember();
802 return null_node;
803 },
804 };
805 }
806
711 /// Statement807 /// Statement
712 /// <- KEYWORD_comptime? VarDecl808 /// <- KEYWORD_comptime? VarDecl
713 /// / KEYWORD_comptime BlockExprStatement809 /// / KEYWORD_comptime BlockExprStatement
...@@ -3333,16 +3429,20 @@ const Parser = struct {...@@ -3333,16 +3429,20 @@ const Parser = struct {
3333 _ = try p.expectToken(.RParen);3429 _ = try p.expectToken(.RParen);
33343430
3335 _ = try p.expectToken(.LBrace);3431 _ = try p.expectToken(.LBrace);
3336 const members = try p.parseContainerMembers(false);3432 const members = try p.parseContainerMembers();
3433 const members_span = try members.toSpan(p);
3337 _ = try p.expectToken(.RBrace);3434 _ = try p.expectToken(.RBrace);
3338 return p.addNode(.{3435 return p.addNode(.{
3339 .tag = .TaggedUnionEnumTag,3436 .tag = switch (members.trailing_comma) {
3437 true => .TaggedUnionEnumTagComma,
3438 false => .TaggedUnionEnumTag,
3439 },
3340 .main_token = main_token,3440 .main_token = main_token,
3341 .data = .{3441 .data = .{
3342 .lhs = enum_tag_expr,3442 .lhs = enum_tag_expr,
3343 .rhs = try p.addExtra(Node.SubRange{3443 .rhs = try p.addExtra(Node.SubRange{
3344 .start = members.start,3444 .start = members_span.start,
3345 .end = members.end,3445 .end = members_span.end,
3346 }),3446 }),
3347 },3447 },
3348 });3448 });
...@@ -3350,16 +3450,34 @@ const Parser = struct {...@@ -3350,16 +3450,34 @@ const Parser = struct {
3350 _ = try p.expectToken(.RParen);3450 _ = try p.expectToken(.RParen);
33513451
3352 _ = try p.expectToken(.LBrace);3452 _ = try p.expectToken(.LBrace);
3353 const members = try p.parseContainerMembers(false);3453 const members = try p.parseContainerMembers();
3354 _ = try p.expectToken(.RBrace);3454 _ = try p.expectToken(.RBrace);
3355 return p.addNode(.{3455 if (members.len <= 2) {
3356 .tag = .TaggedUnion,3456 return p.addNode(.{
3357 .main_token = main_token,3457 .tag = switch (members.trailing_comma) {
3358 .data = .{3458 true => .TaggedUnionTwoComma,
3359 .lhs = members.start,3459 false => .TaggedUnionTwo,
3360 .rhs = members.end,3460 },
3361 },3461 .main_token = main_token,
3362 });3462 .data = .{
3463 .lhs = members.lhs,
3464 .rhs = members.rhs,
3465 },
3466 });
3467 } else {
3468 const span = try members.toSpan(p);
3469 return p.addNode(.{
3470 .tag = switch (members.trailing_comma) {
3471 true => .TaggedUnionComma,
3472 false => .TaggedUnion,
3473 },
3474 .main_token = main_token,
3475 .data = .{
3476 .lhs = span.start,
3477 .rhs = span.end,
3478 },
3479 });
3480 }
3363 }3481 }
3364 } else {3482 } else {
3365 const expr = try p.expectExpr();3483 const expr = try p.expectExpr();
...@@ -3373,26 +3491,48 @@ const Parser = struct {...@@ -3373,26 +3491,48 @@ const Parser = struct {
3373 else => unreachable,3491 else => unreachable,
3374 };3492 };
3375 _ = try p.expectToken(.LBrace);3493 _ = try p.expectToken(.LBrace);
3376 const members = try p.parseContainerMembers(false);3494 const members = try p.parseContainerMembers();
3377 _ = try p.expectToken(.RBrace);3495 _ = try p.expectToken(.RBrace);
3378 if (arg_expr == 0) {3496 if (arg_expr == 0) {
3379 return p.addNode(.{3497 if (members.len <= 2) {
3380 .tag = .ContainerDecl,3498 return p.addNode(.{
3381 .main_token = main_token,3499 .tag = switch (members.trailing_comma) {
3382 .data = .{3500 true => .ContainerDeclTwoComma,
3383 .lhs = members.start,3501 false => .ContainerDeclTwo,
3384 .rhs = members.end,3502 },
3385 },3503 .main_token = main_token,
3386 });3504 .data = .{
3505 .lhs = members.lhs,
3506 .rhs = members.rhs,
3507 },
3508 });
3509 } else {
3510 const span = try members.toSpan(p);
3511 return p.addNode(.{
3512 .tag = switch (members.trailing_comma) {
3513 true => .ContainerDeclComma,
3514 false => .ContainerDecl,
3515 },
3516 .main_token = main_token,
3517 .data = .{
3518 .lhs = span.start,
3519 .rhs = span.end,
3520 },
3521 });
3522 }
3387 } else {3523 } else {
3524 const span = try members.toSpan(p);
3388 return p.addNode(.{3525 return p.addNode(.{
3389 .tag = .ContainerDeclArg,3526 .tag = switch (members.trailing_comma) {
3527 true => .ContainerDeclArgComma,
3528 false => .ContainerDeclArg,
3529 },
3390 .main_token = main_token,3530 .main_token = main_token,
3391 .data = .{3531 .data = .{
3392 .lhs = arg_expr,3532 .lhs = arg_expr,
3393 .rhs = try p.addExtra(Node.SubRange{3533 .rhs = try p.addExtra(Node.SubRange{
3394 .start = members.start,3534 .start = span.start,
3395 .end = members.end,3535 .end = span.end,
3396 }),3536 }),
3397 },3537 },
3398 });3538 });
lib/std/zig/parser_test.zig+76-39
...@@ -149,45 +149,82 @@ test "zig fmt: nosuspend block" {...@@ -149,45 +149,82 @@ test "zig fmt: nosuspend block" {
149 );149 );
150}150}
151151
152//test "zig fmt: nosuspend await" {152test "zig fmt: nosuspend await" {
153// try testCanonical(153 try testCanonical(
154// \\fn foo() void {154 \\fn foo() void {
155// \\ x = nosuspend await y;155 \\ x = nosuspend await y;
156// \\}156 \\}
157// \\157 \\
158// );158 );
159//}159}
160//160
161//test "zig fmt: trailing comma in container declaration" {161test "zig fmt: container declaration, single line" {
162// try testCanonical(162 try testCanonical(
163// \\const X = struct { foo: i32 };163 \\const X = struct { foo: i32 };
164// \\const X = struct { foo: i32, bar: i32 };164 \\const X = struct { foo: i32, bar: i32 };
165// \\const X = struct { foo: i32 = 1, bar: i32 = 2 };165 \\const X = struct { foo: i32 = 1, bar: i32 = 2 };
166// \\const X = struct { foo: i32 align(4), bar: i32 align(4) };166 \\const X = struct { foo: i32 align(4), bar: i32 align(4) };
167// \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };167 \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };
168// \\168 \\
169// );169 );
170// try testCanonical(170}
171// \\test "" {171
172// \\ comptime {172test "zig fmt: container declaration, one item, multi line trailing comma" {
173// \\ const X = struct {173 try testCanonical(
174// \\ x: i32174 \\test "" {
175// \\ };175 \\ comptime {
176// \\ }176 \\ const X = struct {
177// \\}177 \\ x: i32,
178// \\178 \\ };
179// );179 \\ }
180// try testTransform(180 \\}
181// \\const X = struct {181 \\
182// \\ foo: i32, bar: i8 };182 );
183// ,183}
184// \\const X = struct {184
185// \\ foo: i32, bar: i8185test "zig fmt: container declaration, no trailing comma on separate line" {
186// \\};186 try testTransform(
187// \\187 \\test "" {
188// );188 \\ comptime {
189//}189 \\ const X = struct {
190//190 \\ x: i32
191 \\ };
192 \\ }
193 \\}
194 \\
195 ,
196 \\test "" {
197 \\ comptime {
198 \\ const X = struct { x: i32 };
199 \\ }
200 \\}
201 \\
202 );
203}
204
205test "zig fmt: container declaration, line break, no trailing comma" {
206 try testTransform(
207 \\const X = struct {
208 \\ foo: i32, bar: i8 };
209 ,
210 \\const X = struct { foo: i32, bar: i8 };
211 \\
212 );
213}
214
215test "zig fmt: container declaration, transform trailing comma" {
216 try testTransform(
217 \\const X = struct {
218 \\ foo: i32, bar: i8, };
219 ,
220 \\const X = struct {
221 \\ foo: i32,
222 \\ bar: i8,
223 \\};
224 \\
225 );
226}
227
191//test "zig fmt: trailing comma in fn parameter list" {228//test "zig fmt: trailing comma in fn parameter list" {
192// try testCanonical(229// try testCanonical(
193// \\pub fn f(230// \\pub fn f(
lib/std/zig/render.zig+113-118
...@@ -68,7 +68,7 @@ fn renderRoot(ais: *Ais, tree: ast.Tree) Error!void {...@@ -68,7 +68,7 @@ fn renderRoot(ais: *Ais, tree: ast.Tree) Error!void {
68 const root_decls = tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];68 const root_decls = tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
6969
70 for (root_decls) |decl| {70 for (root_decls) |decl| {
71 try renderContainerDecl(ais, tree, decl, .Newline);71 try renderMember(ais, tree, decl, .Newline);
72 }72 }
73}73}
7474
...@@ -84,7 +84,7 @@ fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, first_token: ast.TokenInde...@@ -84,7 +84,7 @@ fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, first_token: ast.TokenInde
84 }84 }
85}85}
8686
87fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {87fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
88 const token_tags = tree.tokens.items(.tag);88 const token_tags = tree.tokens.items(.tag);
89 const main_tokens = tree.nodes.items(.main_token);89 const main_tokens = tree.nodes.items(.main_token);
90 const datas = tree.nodes.items(.data);90 const datas = tree.nodes.items(.data);
...@@ -158,6 +158,8 @@ fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: S...@@ -158,6 +158,8 @@ fn renderContainerDecl(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: S
158 .ContainerFieldAlign => return renderContainerField(ais, tree, tree.containerFieldAlign(decl), space),158 .ContainerFieldAlign => return renderContainerField(ais, tree, tree.containerFieldAlign(decl), space),
159 .ContainerField => return renderContainerField(ais, tree, tree.containerField(decl), space),159 .ContainerField => return renderContainerField(ais, tree, tree.containerField(decl), space),
160 .Comptime => return renderExpression(ais, tree, decl, space),160 .Comptime => return renderExpression(ais, tree, decl, space),
161
162 .Root => unreachable,
161 else => unreachable,163 else => unreachable,
162 }164 }
163}165}
...@@ -195,7 +197,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac...@@ -195,7 +197,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
195 // return renderToken(ais, tree, any_type.token, space);197 // return renderToken(ais, tree, any_type.token, space);
196 //},198 //},
197 .BlockTwo => {199 .BlockTwo => {
198 var statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs };200 const statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
199 if (datas[node].lhs == 0) {201 if (datas[node].lhs == 0) {
200 return renderBlock(ais, tree, main_tokens[node], statements[0..0], space);202 return renderBlock(ais, tree, main_tokens[node], statements[0..0], space);
201 } else if (datas[node].rhs == 0) {203 } else if (datas[node].rhs == 0) {
...@@ -667,124 +669,29 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac...@@ -667,124 +669,29 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
667 // return renderToken(ais, tree, grouped_expr.rparen, space);669 // return renderToken(ais, tree, grouped_expr.rparen, space);
668 //},670 //},
669671
670 .ContainerDecl => unreachable, // TODO672 .ContainerDecl,
671 .ContainerDeclArg => unreachable, // TODO673 .ContainerDeclComma,
672 .TaggedUnion => unreachable, // TODO674 => return renderContainerDecl(ais, tree, tree.containerDecl(node), space),
673 .TaggedUnionEnumTag => unreachable, // TODO
674 //.ContainerDecl => {
675 // const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
676
677 // if (container_decl.layout_token) |layout_token| {
678 // try renderToken(ais, tree, layout_token, Space.Space);
679 // }
680
681 // switch (container_decl.init_arg_expr) {
682 // .None => {
683 // try renderToken(ais, tree, container_decl.kind_token, Space.Space); // union
684 // },
685 // .Enum => |enum_tag_type| {
686 // try renderToken(ais, tree, container_decl.kind_token, Space.None); // union
687
688 // const lparen = tree.nextToken(container_decl.kind_token);
689 // const enum_token = tree.nextToken(lparen);
690
691 // try renderToken(ais, tree, lparen, Space.None); // (
692 // try renderToken(ais, tree, enum_token, Space.None); // enum
693
694 // if (enum_tag_type) |expr| {
695 // try renderToken(ais, tree, tree.nextToken(enum_token), Space.None); // (
696 // try renderExpression(ais, tree, expr, Space.None);
697
698 // const rparen = tree.nextToken(expr.lastToken());
699 // try renderToken(ais, tree, rparen, Space.None); // )
700 // try renderToken(ais, tree, tree.nextToken(rparen), Space.Space); // )
701 // } else {
702 // try renderToken(ais, tree, tree.nextToken(enum_token), Space.Space); // )
703 // }
704 // },
705 // .Type => |type_expr| {
706 // try renderToken(ais, tree, container_decl.kind_token, Space.None); // union
707
708 // const lparen = tree.nextToken(container_decl.kind_token);
709 // const rparen = tree.nextToken(type_expr.lastToken());
710
711 // try renderToken(ais, tree, lparen, Space.None); // (
712 // try renderExpression(ais, tree, type_expr, Space.None);
713 // try renderToken(ais, tree, rparen, Space.Space); // )
714 // },
715 // }
716
717 // if (container_decl.fields_and_decls_len == 0) {
718 // {
719 // ais.pushIndentNextLine();
720 // defer ais.popIndent();
721 // try renderToken(ais, tree, container_decl.lbrace_token, Space.None); // lbrace
722 // }
723 // return renderToken(ais, tree, container_decl.rbrace_token, space); // rbrace
724 // }
725675
726 // const src_has_trailing_comma = blk: {676 .ContainerDeclTwo, .ContainerDeclTwoComma => {
727 // var maybe_comma = tree.prevToken(container_decl.lastToken());677 var buffer: [2]ast.Node.Index = undefined;
728 // // Doc comments for a field may also appear after the comma, eg.678 return renderContainerDecl(ais, tree, tree.containerDeclTwo(&buffer, node), space);
729 // // field_name: T, // comment attached to field_name679 },
730 // if (tree.token_tags[maybe_comma] == .DocComment)680 .ContainerDeclArg,
731 // maybe_comma = tree.prevToken(maybe_comma);681 .ContainerDeclArgComma,
732 // break :blk tree.token_tags[maybe_comma] == .Comma;682 => return renderContainerDecl(ais, tree, tree.containerDeclArg(node), space),
733 // };
734
735 // const fields_and_decls = container_decl.fieldsAndDecls();
736
737 // // Check if the first declaration and the { are on the same line
738 // const src_has_newline = !tree.tokensOnSameLine(
739 // container_decl.lbrace_token,
740 // fields_and_decls[0].firstToken(),
741 // );
742
743 // // We can only print all the elements in-line if all the
744 // // declarations inside are fields
745 // const src_has_only_fields = blk: {
746 // for (fields_and_decls) |decl| {
747 // if (decl.tag != .ContainerField) break :blk false;
748 // }
749 // break :blk true;
750 // };
751
752 // if (src_has_trailing_comma or !src_has_only_fields) {
753 // // One declaration per line
754 // ais.pushIndentNextLine();
755 // defer ais.popIndent();
756 // try renderToken(ais, tree, container_decl.lbrace_token, .Newline); // lbrace
757
758 // for (fields_and_decls) |decl, i| {
759 // try renderContainerDecl(allocator, ais, tree, decl, .Newline);
760
761 // if (i + 1 < fields_and_decls.len) {
762 // try renderExtraNewline(ais, tree, fields_and_decls[i + 1]);
763 // }
764 // }
765 // } else if (src_has_newline) {
766 // // All the declarations on the same line, but place the items on
767 // // their own line
768 // try renderToken(ais, tree, container_decl.lbrace_token, .Newline); // lbrace
769
770 // ais.pushIndent();
771 // defer ais.popIndent();
772
773 // for (fields_and_decls) |decl, i| {
774 // const space_after_decl: Space = if (i + 1 >= fields_and_decls.len) .Newline else .Space;
775 // try renderContainerDecl(allocator, ais, tree, decl, space_after_decl);
776 // }
777 // } else {
778 // // All the declarations on the same line
779 // try renderToken(ais, tree, container_decl.lbrace_token, .Space); // lbrace
780683
781 // for (fields_and_decls) |decl| {684 .TaggedUnion,
782 // try renderContainerDecl(allocator, ais, tree, decl, .Space);685 .TaggedUnionComma,
783 // }686 => return renderContainerDecl(ais, tree, tree.taggedUnion(node), space),
784 // }
785687
786 // return renderToken(ais, tree, container_decl.rbrace_token, space); // rbrace688 .TaggedUnionTwo, .TaggedUnionTwoComma => {
787 //},689 var buffer: [2]ast.Node.Index = undefined;
690 return renderContainerDecl(ais, tree, tree.taggedUnionTwo(&buffer, node), space);
691 },
692 .TaggedUnionEnumTag,
693 .TaggedUnionEnumTagComma,
694 => return renderContainerDecl(ais, tree, tree.taggedUnionEnumTag(node), space),
788695
789 .ErrorSetDecl => unreachable, // TODO696 .ErrorSetDecl => unreachable, // TODO
790 //.ErrorSetDecl => {697 //.ErrorSetDecl => {
...@@ -1949,6 +1856,94 @@ fn renderArrayInit(...@@ -1949,6 +1856,94 @@ fn renderArrayInit(
1949 }1856 }
1950}1857}
19511858
1859fn renderContainerDecl(
1860 ais: *Ais,
1861 tree: ast.Tree,
1862 container_decl: ast.Full.ContainerDecl,
1863 space: Space,
1864) Error!void {
1865 const token_tags = tree.tokens.items(.tag);
1866 const node_tags = tree.nodes.items(.tag);
1867
1868 if (container_decl.layout_token) |layout_token| {
1869 try renderToken(ais, tree, layout_token, .Space);
1870 }
1871
1872 var lbrace: ast.TokenIndex = undefined;
1873 if (container_decl.ast.enum_token) |enum_token| {
1874 try renderToken(ais, tree, container_decl.ast.main_token, .None); // union
1875 try renderToken(ais, tree, enum_token - 1, .None); // lparen
1876 try renderToken(ais, tree, enum_token, .None); // enum
1877 if (container_decl.ast.arg != 0) {
1878 try renderToken(ais, tree, enum_token + 1, .None); // lparen
1879 try renderExpression(ais, tree, container_decl.ast.arg, .None);
1880 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
1881 try renderToken(ais, tree, rparen, .None); // rparen
1882 try renderToken(ais, tree, rparen + 1, .Space); // rparen
1883 lbrace = rparen + 2;
1884 } else {
1885 try renderToken(ais, tree, enum_token + 1, .Space); // rparen
1886 lbrace = enum_token + 2;
1887 }
1888 } else if (container_decl.ast.arg != 0) {
1889 try renderToken(ais, tree, container_decl.ast.main_token, .None); // union
1890 try renderToken(ais, tree, container_decl.ast.main_token + 1, .None); // lparen
1891 try renderExpression(ais, tree, container_decl.ast.arg, .None);
1892 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
1893 try renderToken(ais, tree, rparen, .Space); // rparen
1894 lbrace = rparen + 1;
1895 } else {
1896 try renderToken(ais, tree, container_decl.ast.main_token, .Space); // union
1897 lbrace = container_decl.ast.main_token + 1;
1898 }
1899
1900 if (container_decl.ast.members.len == 0) {
1901 try renderToken(ais, tree, lbrace, Space.None); // lbrace
1902 return renderToken(ais, tree, lbrace + 1, space); // rbrace
1903 }
1904
1905 const last_member = container_decl.ast.members[container_decl.ast.members.len - 1];
1906 const last_member_token = tree.lastToken(last_member);
1907 const rbrace = switch (token_tags[last_member_token + 1]) {
1908 .DocComment => last_member_token + 2,
1909 .Comma => switch (token_tags[last_member_token + 2]) {
1910 .DocComment => last_member_token + 3,
1911 .RBrace => last_member_token + 2,
1912 else => unreachable,
1913 },
1914 .RBrace => last_member_token + 1,
1915 else => unreachable,
1916 };
1917 const src_has_trailing_comma = token_tags[last_member_token + 1] == .Comma;
1918
1919 if (!src_has_trailing_comma) one_line: {
1920 // We can only print all the members in-line if all the members are fields.
1921 for (container_decl.ast.members) |member| {
1922 if (!node_tags[member].isContainerField()) break :one_line;
1923 }
1924 // All the declarations on the same line.
1925 try renderToken(ais, tree, lbrace, .Space); // lbrace
1926 for (container_decl.ast.members) |member| {
1927 try renderMember(ais, tree, member, .Space);
1928 }
1929 return renderToken(ais, tree, rbrace, space); // rbrace
1930 }
1931
1932 // One member per line.
1933 ais.pushIndent();
1934 try renderToken(ais, tree, lbrace, .Newline); // lbrace
1935 for (container_decl.ast.members) |member, i| {
1936 try renderMember(ais, tree, member, .Newline);
1937
1938 if (i + 1 < container_decl.ast.members.len) {
1939 try renderExtraNewline(ais, tree, container_decl.ast.members[i + 1]);
1940 }
1941 }
1942 ais.popIndent();
1943
1944 return renderToken(ais, tree, rbrace, space); // rbrace
1945}
1946
1952/// Render an expression, and the comma that follows it, if it is present in the source.1947/// Render an expression, and the comma that follows it, if it is present in the source.
1953fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {1948fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
1954 const token_tags = tree.tokens.items(.tag);1949 const token_tags = tree.tokens.items(.tag);