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;...@@ -3511,3 +3511,7 @@ const Token = std.zig.Token;
3511const Ast = @This();3511const Ast = @This();
3512const Allocator = std.mem.Allocator;3512const Allocator = std.mem.Allocator;
3513const Parse = @import("Parse.zig");3513const 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)" {...@@ -5732,6 +5732,62 @@ test "zig fmt: canonicalize symbols (asm)" {
5732 );5732 );
5733}5733}
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
5735test "zig fmt: error for missing sentinel value in sentinel slice" {5791test "zig fmt: error for missing sentinel value in sentinel slice" {
5736 try testError(5792 try testError(
5737 \\const foo = foo[0..:];5793 \\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 {...@@ -40,27 +40,28 @@ 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 var is_tuple = true;43 const container: Container = for (members) |member| {
44 for (members) |member| {44 if (tree.fullContainerField(member)) |field| if (!field.ast.tuple_like) break .other;
45 const container_field = tree.fullContainerField(member) orelse continue;45 } else .tuple;
46 if (!container_field.ast.tuple_like) {46 try renderMember(gpa, ais, tree, container, members[0], .newline);
47 is_tuple = false;
48 break;
49 }
50 }
51 try renderMember(gpa, ais, tree, members[0], is_tuple, .newline);
52 for (members[1..]) |member| {47 for (members[1..]) |member| {
53 try renderExtraNewline(ais, tree, member);48 try renderExtraNewline(ais, tree, member);
54 try renderMember(gpa, ais, tree, member, is_tuple, .newline);49 try renderMember(gpa, ais, tree, container, member, .newline);
55 }50 }
56}51}
5752
53const Container = enum {
54 @"enum",
55 tuple,
56 other,
57};
58
58fn renderMember(59fn renderMember(
59 gpa: Allocator,60 gpa: Allocator,
60 ais: *Ais,61 ais: *Ais,
61 tree: Ast,62 tree: Ast,
63 container: Container,
62 decl: Ast.Node.Index,64 decl: Ast.Node.Index,
63 is_tuple: bool,
64 space: Space,65 space: Space,
65) Error!void {66) Error!void {
66 const token_tags = tree.tokens.items(.tag);67 const token_tags = tree.tokens.items(.tag);
...@@ -180,7 +181,7 @@ fn renderMember(...@@ -180,7 +181,7 @@ fn renderMember(
180 .container_field_init,181 .container_field_init,
181 .container_field_align,182 .container_field_align,
182 .container_field,183 .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
185 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),186 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
186187
...@@ -1279,19 +1280,23 @@ fn renderContainerField(...@@ -1279,19 +1280,23 @@ fn renderContainerField(
1279 gpa: Allocator,1280 gpa: Allocator,
1280 ais: *Ais,1281 ais: *Ais,
1281 tree: Ast,1282 tree: Ast,
1283 container: Container,
1282 field_param: Ast.full.ContainerField,1284 field_param: Ast.full.ContainerField,
1283 is_tuple: bool,
1284 space: Space,1285 space: Space,
1285) Error!void {1286) Error!void {
1286 var field = field_param;1287 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
1289 if (field.comptime_token) |t| {1294 if (field.comptime_token) |t| {
1290 try renderToken(ais, tree, t, .space); // comptime1295 try renderToken(ais, tree, t, .space); // comptime
1291 }1296 }
1292 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {1297 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1293 if (field.ast.align_expr != 0) {1298 if (field.ast.align_expr != 0) {
1294 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name1299 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name
1295 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;1300 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1296 const align_kw = lparen_token - 1;1301 const align_kw = lparen_token - 1;
1297 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;1302 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
...@@ -1300,11 +1305,11 @@ fn renderContainerField(...@@ -1300,11 +1305,11 @@ fn renderContainerField(
1300 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment1305 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1301 return renderToken(ais, tree, rparen_token, .space); // )1306 return renderToken(ais, tree, rparen_token, .space); // )
1302 }1307 }
1303 return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name1308 return renderIdentifierComma(ais, tree, field.ast.main_token, space, quote); // name
1304 }1309 }
1305 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {1310 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1306 if (!field.ast.tuple_like) {1311 if (!field.ast.tuple_like) {
1307 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name1312 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name
1308 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :1313 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1309 }1314 }
13101315
...@@ -1321,7 +1326,7 @@ fn renderContainerField(...@@ -1321,7 +1326,7 @@ fn renderContainerField(
1321 }1326 }
1322 }1327 }
1323 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {1328 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1324 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name1329 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name
1325 if (field.ast.align_expr != 0) {1330 if (field.ast.align_expr != 0) {
1326 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;1331 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1327 const align_kw = lparen_token - 1;1332 const align_kw = lparen_token - 1;
...@@ -1335,7 +1340,7 @@ fn renderContainerField(...@@ -1335,7 +1340,7 @@ fn renderContainerField(
1335 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1340 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1336 }1341 }
1337 if (!field.ast.tuple_like) {1342 if (!field.ast.tuple_like) {
1338 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name1343 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name
1339 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :1344 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1340 }1345 }
1341 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1346 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
...@@ -2054,13 +2059,12 @@ fn renderContainerDecl(...@@ -2054,13 +2059,12 @@ fn renderContainerDecl(
2054 try renderToken(ais, tree, layout_token, .space);2059 try renderToken(ais, tree, layout_token, .space);
2055 }2060 }
20562061
2057 var is_tuple = token_tags[container_decl.ast.main_token] == .keyword_struct;2062 const container: Container = switch (token_tags[container_decl.ast.main_token]) {
2058 if (is_tuple) for (container_decl.ast.members) |member| {2063 .keyword_enum => .@"enum",
2059 const container_field = tree.fullContainerField(member) orelse continue;2064 .keyword_struct => for (container_decl.ast.members) |member| {
2060 if (!container_field.ast.tuple_like) {2065 if (tree.fullContainerField(member)) |field| if (!field.ast.tuple_like) break .other;
2061 is_tuple = false;2066 } else .tuple,
2062 break;2067 else => .other,
2063 }
2064 };2068 };
20652069
2066 var lbrace: Ast.TokenIndex = undefined;2070 var lbrace: Ast.TokenIndex = undefined;
...@@ -2129,7 +2133,7 @@ fn renderContainerDecl(...@@ -2129,7 +2133,7 @@ fn renderContainerDecl(
2129 // Print all the declarations on the same line.2133 // Print all the declarations on the same line.
2130 try renderToken(ais, tree, lbrace, .space); // lbrace2134 try renderToken(ais, tree, lbrace, .space); // lbrace
2131 for (container_decl.ast.members) |member| {2135 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);
2133 }2137 }
2134 return renderToken(ais, tree, rbrace, space); // rbrace2138 return renderToken(ais, tree, rbrace, space); // rbrace
2135 }2139 }
...@@ -2147,9 +2151,9 @@ fn renderContainerDecl(...@@ -2147,9 +2151,9 @@ fn renderContainerDecl(
2147 .container_field_init,2151 .container_field_init,
2148 .container_field_align,2152 .container_field_align,
2149 .container_field,2153 .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),
2153 }2157 }
2154 }2158 }
2155 ais.popIndent();2159 ais.popIndent();
...@@ -2565,6 +2569,7 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us...@@ -2565,6 +2569,7 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
2565const QuoteBehavior = enum {2569const QuoteBehavior = enum {
2566 preserve_when_shadowing,2570 preserve_when_shadowing,
2567 eagerly_unquote,2571 eagerly_unquote,
2572 eagerly_unquote_except_underscore,
2568};2573};
25692574
2570fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {2575fn 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...@@ -2589,7 +2594,9 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
2589 // Special case for _ which would incorrectly be rejected by isValidId below.2594 // Special case for _ which would incorrectly be rejected by isValidId below.
2590 if (contents.len == 1 and contents[0] == '_') switch (quote) {2595 if (contents.len == 1 and contents[0] == '_') switch (quote) {
2591 .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),2596 .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),
2593 };2600 };
25942601
2595 // Scan the entire name for characters that would (after un-escaping) be illegal in a symbol,2602 // 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...@@ -2653,7 +2660,9 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
2653 return renderQuotedIdentifier(ais, tree, token_index, space, false);2660 return renderQuotedIdentifier(ais, tree, token_index, space, false);
2654 }2661 }
2655 if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) {2662 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),
2657 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),2666 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2658 };2667 };
2659 }2668 }