authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-07 21:53:17-04:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-05-08 10:58:31+03:00
logbac3a28214d07f761e25fcc12a6708f70c8ccc01
tree2d67ae67ed9a416c2d8a02ee21365a4fdbe9331c
parentff59c4584041517a2ee07cccf923ef5460032d68

fmt: avoid canonicalizing enum fields named `@"_"` to `_`

This can be used to escape the usual meaning of `_` to indicate a non-exhaustive enum and create an enum tag that is a literal underscore, so zig fmt should allow this syntax. Before, zig fmt changes const E = enum { @"_" }; to the semantically different const E = enum { _ }; After, it remains the same.

3 files changed, 100 insertions(+), 31 deletions(-)

lib/std/zig/Ast.zig+4
......@@ -3511,3 +3511,7 @@ const Token = std.zig.Token;
35113511const Ast = @This();
35123512const Allocator = std.mem.Allocator;
35133513const Parse = @import("Parse.zig");
3514
3515test {
3516 testing.refAllDecls(@This());
3517}
lib/std/zig/parser_test.zig+56
......@@ -5732,6 +5732,62 @@ test "zig fmt: canonicalize symbols (asm)" {
57325732 );
57335733}
57345734
5735test "zig fmt: don't canonicalize _ in enums" {
5736 try testTransform(
5737 \\const A = enum {
5738 \\ first,
5739 \\ second,
5740 \\ third,
5741 \\ _,
5742 \\};
5743 \\const B = enum {
5744 \\ @"_",
5745 \\ @"__",
5746 \\ @"___",
5747 \\ @"____",
5748 \\};
5749 \\const C = struct {
5750 \\ @"_": u8,
5751 \\ @"__": u8,
5752 \\ @"___": u8,
5753 \\ @"____": u8,
5754 \\};
5755 \\const D = union {
5756 \\ @"_": u8,
5757 \\ @"__": u8,
5758 \\ @"___": u8,
5759 \\ @"____": u8,
5760 \\};
5761 \\
5762 ,
5763 \\const A = enum {
5764 \\ first,
5765 \\ second,
5766 \\ third,
5767 \\ _,
5768 \\};
5769 \\const B = enum {
5770 \\ @"_",
5771 \\ __,
5772 \\ ___,
5773 \\ ____,
5774 \\};
5775 \\const C = struct {
5776 \\ _: u8,
5777 \\ __: u8,
5778 \\ ___: u8,
5779 \\ ____: u8,
5780 \\};
5781 \\const D = union {
5782 \\ _: u8,
5783 \\ __: u8,
5784 \\ ___: u8,
5785 \\ ____: u8,
5786 \\};
5787 \\
5788 );
5789}
5790
57355791test "zig fmt: error for missing sentinel value in sentinel slice" {
57365792 try testError(
57375793 \\const foo = foo[0..:];
lib/std/zig/render.zig+40-31
......@@ -40,27 +40,28 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void {
4040/// Render all members in the given slice, keeping empty lines where appropriate
4141fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void {
4242 if (members.len == 0) return;
43 var is_tuple = true;
44 for (members) |member| {
45 const container_field = tree.fullContainerField(member) orelse continue;
46 if (!container_field.ast.tuple_like) {
47 is_tuple = false;
48 break;
49 }
50 }
51 try renderMember(gpa, ais, tree, members[0], is_tuple, .newline);
43 const container: Container = for (members) |member| {
44 if (tree.fullContainerField(member)) |field| if (!field.ast.tuple_like) break .other;
45 } else .tuple;
46 try renderMember(gpa, ais, tree, container, members[0], .newline);
5247 for (members[1..]) |member| {
5348 try renderExtraNewline(ais, tree, member);
54 try renderMember(gpa, ais, tree, member, is_tuple, .newline);
49 try renderMember(gpa, ais, tree, container, member, .newline);
5550 }
5651}
5752
53const Container = enum {
54 @"enum",
55 tuple,
56 other,
57};
58
5859fn renderMember(
5960 gpa: Allocator,
6061 ais: *Ais,
6162 tree: Ast,
63 container: Container,
6264 decl: Ast.Node.Index,
63 is_tuple: bool,
6465 space: Space,
6566) Error!void {
6667 const token_tags = tree.tokens.items(.tag);
......@@ -180,7 +181,7 @@ fn renderMember(
180181 .container_field_init,
181182 .container_field_align,
182183 .container_field,
183 => return renderContainerField(gpa, ais, tree, tree.fullContainerField(decl).?, is_tuple, space),
184 => return renderContainerField(gpa, ais, tree, container, tree.fullContainerField(decl).?, space),
184185
185186 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
186187
......@@ -1279,19 +1280,23 @@ fn renderContainerField(
12791280 gpa: Allocator,
12801281 ais: *Ais,
12811282 tree: Ast,
1283 container: Container,
12821284 field_param: Ast.full.ContainerField,
1283 is_tuple: bool,
12841285 space: Space,
12851286) Error!void {
12861287 var field = field_param;
1287 if (!is_tuple) field.convertToNonTupleLike(tree.nodes);
1288 if (container != .tuple) field.convertToNonTupleLike(tree.nodes);
1289 const quote: QuoteBehavior = switch (container) {
1290 .@"enum" => .eagerly_unquote_except_underscore,
1291 .tuple, .other => .eagerly_unquote,
1292 };
12881293
12891294 if (field.comptime_token) |t| {
12901295 try renderToken(ais, tree, t, .space); // comptime
12911296 }
12921297 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
12931298 if (field.ast.align_expr != 0) {
1294 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
1299 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name
12951300 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
12961301 const align_kw = lparen_token - 1;
12971302 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
......@@ -1300,11 +1305,11 @@ fn renderContainerField(
13001305 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
13011306 return renderToken(ais, tree, rparen_token, .space); // )
13021307 }
1303 return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name
1308 return renderIdentifierComma(ais, tree, field.ast.main_token, space, quote); // name
13041309 }
13051310 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
13061311 if (!field.ast.tuple_like) {
1307 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
1312 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name
13081313 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
13091314 }
13101315
......@@ -1321,7 +1326,7 @@ fn renderContainerField(
13211326 }
13221327 }
13231328 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1324 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
1329 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name
13251330 if (field.ast.align_expr != 0) {
13261331 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
13271332 const align_kw = lparen_token - 1;
......@@ -1335,7 +1340,7 @@ fn renderContainerField(
13351340 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
13361341 }
13371342 if (!field.ast.tuple_like) {
1338 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
1343 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name
13391344 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
13401345 }
13411346 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
......@@ -2054,13 +2059,12 @@ fn renderContainerDecl(
20542059 try renderToken(ais, tree, layout_token, .space);
20552060 }
20562061
2057 var is_tuple = token_tags[container_decl.ast.main_token] == .keyword_struct;
2058 if (is_tuple) for (container_decl.ast.members) |member| {
2059 const container_field = tree.fullContainerField(member) orelse continue;
2060 if (!container_field.ast.tuple_like) {
2061 is_tuple = false;
2062 break;
2063 }
2062 const container: Container = switch (token_tags[container_decl.ast.main_token]) {
2063 .keyword_enum => .@"enum",
2064 .keyword_struct => for (container_decl.ast.members) |member| {
2065 if (tree.fullContainerField(member)) |field| if (!field.ast.tuple_like) break .other;
2066 } else .tuple,
2067 else => .other,
20642068 };
20652069
20662070 var lbrace: Ast.TokenIndex = undefined;
......@@ -2129,7 +2133,7 @@ fn renderContainerDecl(
21292133 // Print all the declarations on the same line.
21302134 try renderToken(ais, tree, lbrace, .space); // lbrace
21312135 for (container_decl.ast.members) |member| {
2132 try renderMember(gpa, ais, tree, member, is_tuple, .space);
2136 try renderMember(gpa, ais, tree, container, member, .space);
21332137 }
21342138 return renderToken(ais, tree, rbrace, space); // rbrace
21352139 }
......@@ -2147,9 +2151,9 @@ fn renderContainerDecl(
21472151 .container_field_init,
21482152 .container_field_align,
21492153 .container_field,
2150 => try renderMember(gpa, ais, tree, member, is_tuple, .comma),
2154 => try renderMember(gpa, ais, tree, container, member, .comma),
21512155
2152 else => try renderMember(gpa, ais, tree, member, is_tuple, .newline),
2156 else => try renderMember(gpa, ais, tree, container, member, .newline),
21532157 }
21542158 }
21552159 ais.popIndent();
......@@ -2565,6 +2569,7 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
25652569const QuoteBehavior = enum {
25662570 preserve_when_shadowing,
25672571 eagerly_unquote,
2572 eagerly_unquote_except_underscore,
25682573};
25692574
25702575fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {
......@@ -2589,7 +2594,9 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
25892594 // Special case for _ which would incorrectly be rejected by isValidId below.
25902595 if (contents.len == 1 and contents[0] == '_') switch (quote) {
25912596 .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),
2592 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2597 .eagerly_unquote_except_underscore,
2598 .preserve_when_shadowing,
2599 => return renderQuotedIdentifier(ais, tree, token_index, space, false),
25932600 };
25942601
25952602 // Scan the entire name for characters that would (after un-escaping) be illegal in a symbol,
......@@ -2653,7 +2660,9 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
26532660 return renderQuotedIdentifier(ais, tree, token_index, space, false);
26542661 }
26552662 if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) {
2656 .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),
2663 .eagerly_unquote,
2664 .eagerly_unquote_except_underscore,
2665 => return renderQuotedIdentifier(ais, tree, token_index, space, true),
26572666 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
26582667 };
26592668 }