authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-21 23:25:16+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-23 12:13:39+02:00
log6fb689e97a4ec05fd97dfa05cc203e9bcb7cffa1
tree85f80dde57f698b4e84be75756a33eaf19c5d276
parent32ce2f91a92c23d46c6836a6dd68ae0f08bb04c5

parser: allow unnamed fields in structs


4 files changed, 209 insertions(+), 151 deletions(-)

lib/std/zig/Ast.zig+21-9
...@@ -559,6 +559,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -559,6 +559,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
559 .container_field,559 .container_field,
560 => {560 => {
561 const name_token = main_tokens[n];561 const name_token = main_tokens[n];
562 if (token_tags[name_token + 1] != .colon) return name_token - end_offset;
562 if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) {563 if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) {
563 end_offset += 1;564 end_offset += 1;
564 }565 }
...@@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {...@@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {
1320 assert(tree.nodes.items(.tag)[node] == .container_field);1321 assert(tree.nodes.items(.tag)[node] == .container_field);
1321 const data = tree.nodes.items(.data)[node];1322 const data = tree.nodes.items(.data)[node];
1322 const extra = tree.extraData(data.rhs, Node.ContainerField);1323 const extra = tree.extraData(data.rhs, Node.ContainerField);
1324 const main_token = tree.nodes.items(.main_token)[node];
1323 return tree.fullContainerField(.{1325 return tree.fullContainerField(.{
1324 .name_token = tree.nodes.items(.main_token)[node],1326 .main_token = main_token,
1325 .type_expr = data.lhs,1327 .type_expr = data.lhs,
1326 .value_expr = extra.value_expr,1328 .value_expr = extra.value_expr,
1327 .align_expr = extra.align_expr,1329 .align_expr = extra.align_expr,
1330 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
1328 });1331 });
1329}1332}
13301333
1331pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {1334pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {
1332 assert(tree.nodes.items(.tag)[node] == .container_field_init);1335 assert(tree.nodes.items(.tag)[node] == .container_field_init);
1333 const data = tree.nodes.items(.data)[node];1336 const data = tree.nodes.items(.data)[node];
1337 const main_token = tree.nodes.items(.main_token)[node];
1334 return tree.fullContainerField(.{1338 return tree.fullContainerField(.{
1335 .name_token = tree.nodes.items(.main_token)[node],1339 .main_token = main_token,
1336 .type_expr = data.lhs,1340 .type_expr = data.lhs,
1337 .value_expr = data.rhs,1341 .value_expr = data.rhs,
1338 .align_expr = 0,1342 .align_expr = 0,
1343 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
1339 });1344 });
1340}1345}
13411346
1342pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {1347pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {
1343 assert(tree.nodes.items(.tag)[node] == .container_field_align);1348 assert(tree.nodes.items(.tag)[node] == .container_field_align);
1344 const data = tree.nodes.items(.data)[node];1349 const data = tree.nodes.items(.data)[node];
1350 const main_token = tree.nodes.items(.main_token)[node];
1345 return tree.fullContainerField(.{1351 return tree.fullContainerField(.{
1346 .name_token = tree.nodes.items(.main_token)[node],1352 .main_token = main_token,
1347 .type_expr = data.lhs,1353 .type_expr = data.lhs,
1348 .value_expr = 0,1354 .value_expr = 0,
1349 .align_expr = data.rhs,1355 .align_expr = data.rhs,
1356 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
1350 });1357 });
1351}1358}
13521359
...@@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont...@@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont
1944 .ast = info,1951 .ast = info,
1945 .comptime_token = null,1952 .comptime_token = null,
1946 };1953 };
1947 // comptime name: type = init,1954 if (token_tags[info.main_token] == .keyword_comptime) {
1948 // ^1955 // comptime type = init,
1949 if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) {1956 // ^
1950 result.comptime_token = info.name_token - 1;1957 result.comptime_token = info.main_token;
1958 } else if (info.main_token > 0 and token_tags[info.main_token - 1] == .keyword_comptime) {
1959 // comptime name: type = init,
1960 // ^
1961 result.comptime_token = info.main_token - 1;
1951 }1962 }
1952 return result;1963 return result;
1953}1964}
...@@ -2256,14 +2267,15 @@ pub const full = struct {...@@ -2256,14 +2267,15 @@ pub const full = struct {
2256 ast: Components,2267 ast: Components,
22572268
2258 pub const Components = struct {2269 pub const Components = struct {
2259 name_token: TokenIndex,2270 main_token: TokenIndex,
2260 type_expr: Node.Index,2271 type_expr: Node.Index,
2261 value_expr: Node.Index,2272 value_expr: Node.Index,
2262 align_expr: Node.Index,2273 align_expr: Node.Index,
2274 tuple_like: bool,
2263 };2275 };
22642276
2265 pub fn firstToken(cf: ContainerField) TokenIndex {2277 pub fn firstToken(cf: ContainerField) TokenIndex {
2266 return cf.comptime_token orelse cf.ast.name_token;2278 return cf.comptime_token orelse cf.ast.main_token;
2267 }2279 }
2268 };2280 };
22692281
lib/std/zig/parse.zig+107-113
...@@ -272,53 +272,6 @@ const Parser = struct {...@@ -272,53 +272,6 @@ const Parser = struct {
272 trailing = false;272 trailing = false;
273 },273 },
274 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {274 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
275 .identifier => {
276 p.tok_i += 1;
277 const identifier = p.tok_i;
278 defer last_field = identifier;
279 const container_field = try p.expectContainerFieldRecoverable();
280 if (container_field != 0) {
281 switch (field_state) {
282 .none => field_state = .seen,
283 .err, .seen => {},
284 .end => |node| {
285 try p.warnMsg(.{
286 .tag = .decl_between_fields,
287 .token = p.nodes.items(.main_token)[node],
288 });
289 try p.warnMsg(.{
290 .tag = .previous_field,
291 .is_note = true,
292 .token = last_field,
293 });
294 try p.warnMsg(.{
295 .tag = .next_field,
296 .is_note = true,
297 .token = identifier,
298 });
299 // Continue parsing; error will be reported later.
300 field_state = .err;
301 },
302 }
303 try p.scratch.append(p.gpa, container_field);
304 switch (p.token_tags[p.tok_i]) {
305 .comma => {
306 p.tok_i += 1;
307 trailing = true;
308 continue;
309 },
310 .r_brace, .eof => {
311 trailing = false;
312 break;
313 },
314 else => {},
315 }
316 // There is not allowed to be a decl after a field with no comma.
317 // Report error but recover parser.
318 try p.warn(.expected_comma_after_field);
319 p.findNextContainerMember();
320 }
321 },
322 .l_brace => {275 .l_brace => {
323 if (doc_comment) |some| {276 if (doc_comment) |some| {
324 try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });277 try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
...@@ -349,53 +302,15 @@ const Parser = struct {...@@ -349,53 +302,15 @@ const Parser = struct {
349 },302 },
350 else => {303 else => {
351 p.tok_i += 1;304 p.tok_i += 1;
352 try p.warn(.expected_block_or_field);305 const identifier = p.tok_i;
353 },306 defer last_field = identifier;
354 },307 const container_field = p.expectContainerField() catch |err| switch (err) {
355 .keyword_pub => {308 error.OutOfMemory => return error.OutOfMemory,
356 p.tok_i += 1;309 error.ParseError => {
357 const top_level_decl = try p.expectTopLevelDeclRecoverable();310 p.findNextContainerMember();
358 if (top_level_decl != 0) {311 continue;
359 if (field_state == .seen) {312 },
360 field_state = .{ .end = top_level_decl };313 };
361 }
362 try p.scratch.append(p.gpa, top_level_decl);
363 }
364 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
365 },
366 .keyword_usingnamespace => {
367 const node = try p.expectUsingNamespaceRecoverable();
368 if (node != 0) {
369 if (field_state == .seen) {
370 field_state = .{ .end = node };
371 }
372 try p.scratch.append(p.gpa, node);
373 }
374 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
375 },
376 .keyword_const,
377 .keyword_var,
378 .keyword_threadlocal,
379 .keyword_export,
380 .keyword_extern,
381 .keyword_inline,
382 .keyword_noinline,
383 .keyword_fn,
384 => {
385 const top_level_decl = try p.expectTopLevelDeclRecoverable();
386 if (top_level_decl != 0) {
387 if (field_state == .seen) {
388 field_state = .{ .end = top_level_decl };
389 }
390 try p.scratch.append(p.gpa, top_level_decl);
391 }
392 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
393 },
394 .identifier => {
395 const identifier = p.tok_i;
396 defer last_field = identifier;
397 const container_field = try p.expectContainerFieldRecoverable();
398 if (container_field != 0) {
399 switch (field_state) {314 switch (field_state) {
400 .none => field_state = .seen,315 .none => field_state = .seen,
401 .err, .seen => {},316 .err, .seen => {},
...@@ -435,7 +350,46 @@ const Parser = struct {...@@ -435,7 +350,46 @@ const Parser = struct {
435 // Report error but recover parser.350 // Report error but recover parser.
436 try p.warn(.expected_comma_after_field);351 try p.warn(.expected_comma_after_field);
437 p.findNextContainerMember();352 p.findNextContainerMember();
353 },
354 },
355 .keyword_pub => {
356 p.tok_i += 1;
357 const top_level_decl = try p.expectTopLevelDeclRecoverable();
358 if (top_level_decl != 0) {
359 if (field_state == .seen) {
360 field_state = .{ .end = top_level_decl };
361 }
362 try p.scratch.append(p.gpa, top_level_decl);
363 }
364 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
365 },
366 .keyword_usingnamespace => {
367 const node = try p.expectUsingNamespaceRecoverable();
368 if (node != 0) {
369 if (field_state == .seen) {
370 field_state = .{ .end = node };
371 }
372 try p.scratch.append(p.gpa, node);
373 }
374 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
375 },
376 .keyword_const,
377 .keyword_var,
378 .keyword_threadlocal,
379 .keyword_export,
380 .keyword_extern,
381 .keyword_inline,
382 .keyword_noinline,
383 .keyword_fn,
384 => {
385 const top_level_decl = try p.expectTopLevelDeclRecoverable();
386 if (top_level_decl != 0) {
387 if (field_state == .seen) {
388 field_state = .{ .end = top_level_decl };
389 }
390 try p.scratch.append(p.gpa, top_level_decl);
438 }391 }
392 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
439 },393 },
440 .eof, .r_brace => {394 .eof, .r_brace => {
441 if (doc_comment) |tok| {395 if (doc_comment) |tok| {
...@@ -451,11 +405,57 @@ const Parser = struct {...@@ -451,11 +405,57 @@ const Parser = struct {
451 error.OutOfMemory => return error.OutOfMemory,405 error.OutOfMemory => return error.OutOfMemory,
452 error.ParseError => false,406 error.ParseError => false,
453 };407 };
454 if (!c_container) {408 if (c_container) continue;
455 try p.warn(.expected_container_members);409
456 // This was likely not supposed to end yet; try to find the next declaration.410 const identifier = p.tok_i;
457 p.findNextContainerMember();411 defer last_field = identifier;
412 const container_field = p.expectContainerField() catch |err| switch (err) {
413 error.OutOfMemory => return error.OutOfMemory,
414 error.ParseError => {
415 p.findNextContainerMember();
416 continue;
417 },
418 };
419 switch (field_state) {
420 .none => field_state = .seen,
421 .err, .seen => {},
422 .end => |node| {
423 try p.warnMsg(.{
424 .tag = .decl_between_fields,
425 .token = p.nodes.items(.main_token)[node],
426 });
427 try p.warnMsg(.{
428 .tag = .previous_field,
429 .is_note = true,
430 .token = last_field,
431 });
432 try p.warnMsg(.{
433 .tag = .next_field,
434 .is_note = true,
435 .token = identifier,
436 });
437 // Continue parsing; error will be reported later.
438 field_state = .err;
439 },
458 }440 }
441 try p.scratch.append(p.gpa, container_field);
442 switch (p.token_tags[p.tok_i]) {
443 .comma => {
444 p.tok_i += 1;
445 trailing = true;
446 continue;
447 },
448 .r_brace, .eof => {
449 trailing = false;
450 break;
451 },
452 else => {},
453 }
454 // There is not allowed to be a decl after a field with no comma.
455 // Report error but recover parser.
456 try p.warn(.expected_comma_after_field);
457 p.findNextContainerMember();
458 continue;
459 },459 },
460 }460 }
461 }461 }
...@@ -875,12 +875,16 @@ const Parser = struct {...@@ -875,12 +875,16 @@ const Parser = struct {
875875
876 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?876 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
877 fn expectContainerField(p: *Parser) !Node.Index {877 fn expectContainerField(p: *Parser) !Node.Index {
878 var main_token = p.tok_i;
878 _ = p.eatToken(.keyword_comptime);879 _ = p.eatToken(.keyword_comptime);
879 const name_token = p.assertToken(.identifier);880 const tuple_like = p.token_tags[p.tok_i] != .identifier or p.token_tags[p.tok_i + 1] != .colon;
881 if (!tuple_like) {
882 main_token = p.assertToken(.identifier);
883 }
880884
881 var align_expr: Node.Index = 0;885 var align_expr: Node.Index = 0;
882 var type_expr: Node.Index = 0;886 var type_expr: Node.Index = 0;
883 if (p.eatToken(.colon)) |_| {887 if (p.eatToken(.colon) != null or tuple_like) |_| {
884 type_expr = try p.expectTypeExpr();888 type_expr = try p.expectTypeExpr();
885 align_expr = try p.parseByteAlign();889 align_expr = try p.parseByteAlign();
886 }890 }
...@@ -890,7 +894,7 @@ const Parser = struct {...@@ -890,7 +894,7 @@ const Parser = struct {
890 if (align_expr == 0) {894 if (align_expr == 0) {
891 return p.addNode(.{895 return p.addNode(.{
892 .tag = .container_field_init,896 .tag = .container_field_init,
893 .main_token = name_token,897 .main_token = main_token,
894 .data = .{898 .data = .{
895 .lhs = type_expr,899 .lhs = type_expr,
896 .rhs = value_expr,900 .rhs = value_expr,
...@@ -899,7 +903,7 @@ const Parser = struct {...@@ -899,7 +903,7 @@ const Parser = struct {
899 } else if (value_expr == 0) {903 } else if (value_expr == 0) {
900 return p.addNode(.{904 return p.addNode(.{
901 .tag = .container_field_align,905 .tag = .container_field_align,
902 .main_token = name_token,906 .main_token = main_token,
903 .data = .{907 .data = .{
904 .lhs = type_expr,908 .lhs = type_expr,
905 .rhs = align_expr,909 .rhs = align_expr,
...@@ -908,7 +912,7 @@ const Parser = struct {...@@ -908,7 +912,7 @@ const Parser = struct {
908 } else {912 } else {
909 return p.addNode(.{913 return p.addNode(.{
910 .tag = .container_field,914 .tag = .container_field,
911 .main_token = name_token,915 .main_token = main_token,
912 .data = .{916 .data = .{
913 .lhs = type_expr,917 .lhs = type_expr,
914 .rhs = try p.addExtra(Node.ContainerField{918 .rhs = try p.addExtra(Node.ContainerField{
...@@ -920,16 +924,6 @@ const Parser = struct {...@@ -920,16 +924,6 @@ const Parser = struct {
920 }924 }
921 }925 }
922926
923 fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
924 return p.expectContainerField() catch |err| switch (err) {
925 error.OutOfMemory => return error.OutOfMemory,
926 error.ParseError => {
927 p.findNextContainerMember();
928 return null_node;
929 },
930 };
931 }
932
933 /// Statement927 /// Statement
934 /// <- KEYWORD_comptime? VarDecl928 /// <- KEYWORD_comptime? VarDecl
935 /// / KEYWORD_comptime BlockExprStatement929 /// / KEYWORD_comptime BlockExprStatement
lib/std/zig/parser_test.zig+15-11
...@@ -1,3 +1,15 @@...@@ -1,3 +1,15 @@
1test "zig fmt: tuple struct" {
2 try testCanonical(
3 \\const T = struct {
4 \\ comptime u32,
5 \\ *u32 = 1,
6 \\ // needs to be wrapped in parentheses to not be parsed as a function decl
7 \\ (fn () void) align(1),
8 \\};
9 \\
10 );
11}
12
1test "zig fmt: preserves clobbers in inline asm with stray comma" {13test "zig fmt: preserves clobbers in inline asm with stray comma" {
2 try testCanonical(14 try testCanonical(
3 \\fn foo() void {15 \\fn foo() void {
...@@ -265,14 +277,6 @@ test "zig fmt: decl between fields" {...@@ -265,14 +277,6 @@ test "zig fmt: decl between fields" {
265 });277 });
266}278}
267279
268test "zig fmt: eof after missing comma" {
269 try testError(
270 \\foo()
271 , &[_]Error{
272 .expected_comma_after_field,
273 });
274}
275
276test "zig fmt: errdefer with payload" {280test "zig fmt: errdefer with payload" {
277 try testCanonical(281 try testCanonical(
278 \\pub fn main() anyerror!void {282 \\pub fn main() anyerror!void {
...@@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" {...@@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" {
5732test "recovery: invalid container members" {5736test "recovery: invalid container members" {
5733 try testError(5737 try testError(
5734 \\usingnamespace;5738 \\usingnamespace;
5735 \\foo+5739 \\@foo()+
5736 \\bar@,5740 \\@bar()@,
5737 \\while (a == 2) { test "" {}}5741 \\while (a == 2) { test "" {}}
5738 \\test "" {5742 \\test "" {
5739 \\ a & b5743 \\ a & b
...@@ -5741,7 +5745,7 @@ test "recovery: invalid container members" {...@@ -5741,7 +5745,7 @@ test "recovery: invalid container members" {
5741 , &[_]Error{5745 , &[_]Error{
5742 .expected_expr,5746 .expected_expr,
5743 .expected_comma_after_field,5747 .expected_comma_after_field,
5744 .expected_container_members,5748 .expected_type_expr,
5745 .expected_semi_after_stmt,5749 .expected_semi_after_stmt,
5746 });5750 });
5747}5751}
lib/std/zig/render.zig+66-18
...@@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void {...@@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void {
40/// Render all members in the given slice, keeping empty lines where appropriate40/// Render all members in the given slice, keeping empty lines where appropriate
41fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void {41fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void {
42 if (members.len == 0) return;42 if (members.len == 0) return;
43 try renderMember(gpa, ais, tree, members[0], .newline);43 var any_non_tuple_like_fields = false;
44 for (members) |member| {
45 const tuple_like = switch (tree.nodes.items(.tag)[member]) {
46 .container_field_init => tree.containerFieldInit(member).ast.tuple_like,
47 .container_field_align => tree.containerFieldAlign(member).ast.tuple_like,
48 .container_field => tree.containerField(member).ast.tuple_like,
49 else => continue,
50 };
51 if (!tuple_like) {
52 any_non_tuple_like_fields = true;
53 break;
54 }
55 }
56 try renderMember(gpa, ais, tree, members[0], any_non_tuple_like_fields, .newline);
44 for (members[1..]) |member| {57 for (members[1..]) |member| {
45 try renderExtraNewline(ais, tree, member);58 try renderExtraNewline(ais, tree, member);
46 try renderMember(gpa, ais, tree, member, .newline);59 try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .newline);
47 }60 }
48}61}
4962
50fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, space: Space) Error!void {63fn renderMember(
64 gpa: Allocator,
65 ais: *Ais,
66 tree: Ast,
67 decl: Ast.Node.Index,
68 any_non_tuple_like_fields: bool,
69 space: Space,
70) Error!void {
51 const token_tags = tree.tokens.items(.tag);71 const token_tags = tree.tokens.items(.tag);
52 const main_tokens = tree.nodes.items(.main_token);72 const main_tokens = tree.nodes.items(.main_token);
53 const datas = tree.nodes.items(.data);73 const datas = tree.nodes.items(.data);
...@@ -161,9 +181,9 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac...@@ -161,9 +181,9 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac
161 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);181 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
162 },182 },
163183
164 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space),184 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), any_non_tuple_like_fields, space),
165 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space),185 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), any_non_tuple_like_fields, space),
166 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space),186 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), any_non_tuple_like_fields, space),
167 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),187 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
168188
169 .root => unreachable,189 .root => unreachable,
...@@ -1158,18 +1178,31 @@ fn renderContainerField(...@@ -1158,18 +1178,31 @@ fn renderContainerField(
1158 gpa: Allocator,1178 gpa: Allocator,
1159 ais: *Ais,1179 ais: *Ais,
1160 tree: Ast,1180 tree: Ast,
1161 field: Ast.full.ContainerField,1181 field_param: Ast.full.ContainerField,
1182 any_non_tuple_like_fields: bool,
1162 space: Space,1183 space: Space,
1163) Error!void {1184) Error!void {
1185 var field = field_param;
1186 if (field.ast.tuple_like and any_non_tuple_like_fields and field.ast.type_expr != 0
1187 and tree.nodes.items(.tag)[field.ast.type_expr] == .identifier
1188 ) {
1189 const ident = tree.nodes.items(.main_token)[field.ast.type_expr];
1190 field.ast.tuple_like = false;
1191 field.ast.main_token = ident;
1192 field.ast.type_expr = 0;
1193 }
1194
1164 if (field.comptime_token) |t| {1195 if (field.comptime_token) |t| {
1165 try renderToken(ais, tree, t, .space); // comptime1196 try renderToken(ais, tree, t, .space); // comptime
1166 }1197 }
1167 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {1198 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1168 return renderIdentifierComma(ais, tree, field.ast.name_token, space, .eagerly_unquote); // name1199 return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name
1169 }1200 }
1170 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {1201 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1171 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name1202 if (!field.ast.tuple_like) {
1172 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :1203 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
1204 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1205 }
11731206
1174 if (field.ast.align_expr != 0) {1207 if (field.ast.align_expr != 0) {
1175 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1208 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
...@@ -1184,13 +1217,14 @@ fn renderContainerField(...@@ -1184,13 +1217,14 @@ fn renderContainerField(
1184 }1217 }
1185 }1218 }
1186 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {1219 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1187 try renderIdentifier(ais, tree, field.ast.name_token, .space, .eagerly_unquote); // name1220 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
1188 try renderToken(ais, tree, field.ast.name_token + 1, .space); // =1221 try renderToken(ais, tree, field.ast.main_token + 1, .space); // =
1189 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1222 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1190 }1223 }
11911224 if (!field.ast.tuple_like) {
1192 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name1225 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
1193 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :1226 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1227 }
1194 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1228 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
11951229
1196 if (field.ast.align_expr != 0) {1230 if (field.ast.align_expr != 0) {
...@@ -1901,6 +1935,20 @@ fn renderContainerDecl(...@@ -1901,6 +1935,20 @@ fn renderContainerDecl(
1901 try renderToken(ais, tree, layout_token, .space);1935 try renderToken(ais, tree, layout_token, .space);
1902 }1936 }
19031937
1938 var any_non_tuple_like_fields = token_tags[container_decl.ast.main_token] != .keyword_struct;
1939 if (!any_non_tuple_like_fields) for (container_decl.ast.members) |member| {
1940 const tuple_like = switch (tree.nodes.items(.tag)[member]) {
1941 .container_field_init => tree.containerFieldInit(member).ast.tuple_like,
1942 .container_field_align => tree.containerFieldAlign(member).ast.tuple_like,
1943 .container_field => tree.containerField(member).ast.tuple_like,
1944 else => continue,
1945 };
1946 if (!tuple_like) {
1947 any_non_tuple_like_fields = true;
1948 break;
1949 }
1950 };
1951
1904 var lbrace: Ast.TokenIndex = undefined;1952 var lbrace: Ast.TokenIndex = undefined;
1905 if (container_decl.ast.enum_token) |enum_token| {1953 if (container_decl.ast.enum_token) |enum_token| {
1906 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union1954 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
...@@ -1967,7 +2015,7 @@ fn renderContainerDecl(...@@ -1967,7 +2015,7 @@ fn renderContainerDecl(
1967 // Print all the declarations on the same line.2015 // Print all the declarations on the same line.
1968 try renderToken(ais, tree, lbrace, .space); // lbrace2016 try renderToken(ais, tree, lbrace, .space); // lbrace
1969 for (container_decl.ast.members) |member| {2017 for (container_decl.ast.members) |member| {
1970 try renderMember(gpa, ais, tree, member, .space);2018 try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .space);
1971 }2019 }
1972 return renderToken(ais, tree, rbrace, space); // rbrace2020 return renderToken(ais, tree, rbrace, space); // rbrace
1973 }2021 }
...@@ -1985,9 +2033,9 @@ fn renderContainerDecl(...@@ -1985,9 +2033,9 @@ fn renderContainerDecl(
1985 .container_field_init,2033 .container_field_init,
1986 .container_field_align,2034 .container_field_align,
1987 .container_field,2035 .container_field,
1988 => try renderMember(gpa, ais, tree, member, .comma),2036 => try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .comma),
19892037
1990 else => try renderMember(gpa, ais, tree, member, .newline),2038 else => try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .newline),
1991 }2039 }
1992 }2040 }
1993 ais.popIndent();2041 ais.popIndent();