authorgravatar for codroid@gmail.comStevie Hryciw <codroid@gmail.com> 2022-10-29 00:14:51-07:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2022-11-18 19:22:42+00:00
logca9e1760e8e33d17f44835d93103b940873417cf
treed4d91760c89558bbb3ee3bcd6f49238dcb80d3bd
parentd6d4f2481d590eaca6372b1ded00cea2d869aabc

fmt: canonicalize identifiers


9 files changed, 731 insertions(+), 91 deletions(-)

lib/std/zig.zig+1
...@@ -11,6 +11,7 @@ pub const isValidId = fmt.isValidId;...@@ -11,6 +11,7 @@ pub const isValidId = fmt.isValidId;
11pub const parse = @import("zig/parse.zig").parse;11pub const parse = @import("zig/parse.zig").parse;
12pub const string_literal = @import("zig/string_literal.zig");12pub const string_literal = @import("zig/string_literal.zig");
13pub const number_literal = @import("zig/number_literal.zig");13pub const number_literal = @import("zig/number_literal.zig");
14pub const primitives = @import("zig/primitives.zig");
14pub const Ast = @import("zig/Ast.zig");15pub const Ast = @import("zig/Ast.zig");
15pub const system = @import("zig/system.zig");16pub const system = @import("zig/system.zig");
16pub const CrossTarget = @import("zig/CrossTarget.zig");17pub const CrossTarget = @import("zig/CrossTarget.zig");
lib/std/zig/parser_test.zig+376
...@@ -5198,6 +5198,382 @@ test "zig fmt: while continue expr" {...@@ -5198,6 +5198,382 @@ test "zig fmt: while continue expr" {
5198 });5198 });
5199}5199}
52005200
5201test "zig fmt: canonicalize symbols (simple)" {
5202 try testTransform(
5203 \\const val_normal: Normal = .{};
5204 \\const @"val_unesc_me": @"UnescMe" = .{};
5205 \\const @"val_esc!": @"Esc!" = .{};
5206 \\
5207 \\fn fnNormal() void {}
5208 \\fn @"fnUnescMe"() void {}
5209 \\fn @"fnEsc!"() void {}
5210 \\
5211 \\extern fn protoNormal() void;
5212 \\extern fn @"protoUnescMe"() void;
5213 \\extern fn @"protoEsc!"() void;
5214 \\
5215 \\fn fnWithArgs(normal: Normal, @"unesc_me": @"UnescMe", @"esc!": @"Esc!") void {
5216 \\ _ = normal;
5217 \\ _ = @"unesc_me";
5218 \\ _ = @"esc!";
5219 \\}
5220 \\
5221 \\const Normal = struct {};
5222 \\const @"UnescMe" = struct {
5223 \\ @"x": @"X",
5224 \\ const X = union(@"EnumUnesc") {
5225 \\ normal,
5226 \\ @"unesc_me",
5227 \\ @"esc!",
5228 \\ };
5229 \\ const @"EnumUnesc" = enum {
5230 \\ normal,
5231 \\ @"unesc_me",
5232 \\ @"esc!",
5233 \\ };
5234 \\};
5235 \\const @"Esc!" = struct {
5236 \\ normal: bool = false,
5237 \\ @"unesc_me": bool = false,
5238 \\ @"esc!": bool = false,
5239 \\};
5240 \\
5241 \\pub fn main() void {
5242 \\ _ = val_normal;
5243 \\ _ = @"val_normal";
5244 \\ _ = val_unesc_me;
5245 \\ _ = @"val_unesc_me";
5246 \\ _ = @"val_esc!";
5247 \\
5248 \\ fnNormal();
5249 \\ @"fnNormal"();
5250 \\ fnUnescMe();
5251 \\ @"fnUnescMe"();
5252 \\ @"fnEsc!"();
5253 \\
5254 \\ fnWithArgs(1, Normal{}, UnescMe{}, @"Esc!"{});
5255 \\ fnWithArgs(1, @"Normal"{}, @"UnescMe"{}, @"Esc!"{});
5256 \\ fnWithArgs(1, @"Normal"{}, @"Normal"{}, @"Esc!"{});
5257 \\
5258 \\ const local_val1: @"Normal" = .{};
5259 \\ const @"local_val2": UnescMe = .{
5260 \\ .@"x" = .@"unesc_me",
5261 \\ };
5262 \\ fnWithArgs(@"local_val1", @"local_val2", .{ .@"normal" = true, .@"unesc_me" = true, .@"esc!" = true });
5263 \\ fnWithArgs(local_val1, local_val2, .{ .normal = true, .unesc_me = true, .@"esc!" = true });
5264 \\
5265 \\ var x: u8 = 'x';
5266 \\ switch (@"x") {
5267 \\ @"x" => {},
5268 \\ }
5269 \\
5270 \\ _ = @import("std"); // Don't mess with @builtins
5271 \\ // @"comment"
5272 \\}
5273 \\
5274 ,
5275 \\const val_normal: Normal = .{};
5276 \\const val_unesc_me: UnescMe = .{};
5277 \\const @"val_esc!": @"Esc!" = .{};
5278 \\
5279 \\fn fnNormal() void {}
5280 \\fn fnUnescMe() void {}
5281 \\fn @"fnEsc!"() void {}
5282 \\
5283 \\extern fn protoNormal() void;
5284 \\extern fn protoUnescMe() void;
5285 \\extern fn @"protoEsc!"() void;
5286 \\
5287 \\fn fnWithArgs(normal: Normal, unesc_me: UnescMe, @"esc!": @"Esc!") void {
5288 \\ _ = normal;
5289 \\ _ = unesc_me;
5290 \\ _ = @"esc!";
5291 \\}
5292 \\
5293 \\const Normal = struct {};
5294 \\const UnescMe = struct {
5295 \\ x: X,
5296 \\ const X = union(EnumUnesc) {
5297 \\ normal,
5298 \\ unesc_me,
5299 \\ @"esc!",
5300 \\ };
5301 \\ const EnumUnesc = enum {
5302 \\ normal,
5303 \\ unesc_me,
5304 \\ @"esc!",
5305 \\ };
5306 \\};
5307 \\const @"Esc!" = struct {
5308 \\ normal: bool = false,
5309 \\ unesc_me: bool = false,
5310 \\ @"esc!": bool = false,
5311 \\};
5312 \\
5313 \\pub fn main() void {
5314 \\ _ = val_normal;
5315 \\ _ = val_normal;
5316 \\ _ = val_unesc_me;
5317 \\ _ = val_unesc_me;
5318 \\ _ = @"val_esc!";
5319 \\
5320 \\ fnNormal();
5321 \\ fnNormal();
5322 \\ fnUnescMe();
5323 \\ fnUnescMe();
5324 \\ @"fnEsc!"();
5325 \\
5326 \\ fnWithArgs(1, Normal{}, UnescMe{}, @"Esc!"{});
5327 \\ fnWithArgs(1, Normal{}, UnescMe{}, @"Esc!"{});
5328 \\ fnWithArgs(1, Normal{}, Normal{}, @"Esc!"{});
5329 \\
5330 \\ const local_val1: Normal = .{};
5331 \\ const local_val2: UnescMe = .{
5332 \\ .x = .unesc_me,
5333 \\ };
5334 \\ fnWithArgs(local_val1, local_val2, .{ .normal = true, .unesc_me = true, .@"esc!" = true });
5335 \\ fnWithArgs(local_val1, local_val2, .{ .normal = true, .unesc_me = true, .@"esc!" = true });
5336 \\
5337 \\ var x: u8 = 'x';
5338 \\ switch (x) {
5339 \\ x => {},
5340 \\ }
5341 \\
5342 \\ _ = @import("std"); // Don't mess with @builtins
5343 \\ // @"comment"
5344 \\}
5345 \\
5346 );
5347}
5348
5349// Contextually unescape when shadowing primitive types and values.
5350test "zig fmt: canonicalize symbols (primitive types)" {
5351 try testTransform(
5352 \\const @"anyopaque" = struct {
5353 \\ @"u8": @"type" = true,
5354 \\ @"_": @"false" = @"true",
5355 \\ const @"type" = bool;
5356 \\ const @"false" = bool;
5357 \\ const @"true" = false;
5358 \\};
5359 \\
5360 \\const U = union(@"null") {
5361 \\ @"type",
5362 \\ const @"null" = enum {
5363 \\ @"type",
5364 \\ };
5365 \\};
5366 \\
5367 \\test {
5368 \\ const E = enum { @"anyopaque" };
5369 \\ _ = U{ .@"type" = {} };
5370 \\ _ = U.@"type";
5371 \\ _ = E.@"anyopaque";
5372 \\}
5373 \\
5374 \\fn @"i10"(@"void": @"anyopaque", @"type": @"anyopaque".@"type") error{@"null"}!void {
5375 \\ var @"f32" = @"void";
5376 \\ @"f32".@"u8" = false;
5377 \\ _ = @"type";
5378 \\ _ = type;
5379 \\ if (@"f32".@"u8") {
5380 \\ return @"i10"(.{ .@"u8" = true, .@"_" = false }, false);
5381 \\ } else {
5382 \\ return error.@"null";
5383 \\ }
5384 \\}
5385 \\
5386 \\test @"i10" {
5387 \\ try @"i10"(.{}, true);
5388 \\ _ = @"void": while (null) |@"u3"| {
5389 \\ break :@"void" @"u3";
5390 \\ };
5391 \\ _ = @"void": {
5392 \\ break :@"void";
5393 \\ };
5394 \\ for ("hi") |@"u3", @"i4"| {
5395 \\ _ = @"u3";
5396 \\ _ = @"i4";
5397 \\ }
5398 \\ if (false) {} else |@"bool"| {
5399 \\ _ = @"bool";
5400 \\ }
5401 \\}
5402 \\
5403 ,
5404 \\const @"anyopaque" = struct {
5405 \\ u8: @"type" = true,
5406 \\ _: @"false" = @"true",
5407 \\ const @"type" = bool;
5408 \\ const @"false" = bool;
5409 \\ const @"true" = false;
5410 \\};
5411 \\
5412 \\const U = union(@"null") {
5413 \\ type,
5414 \\ const @"null" = enum {
5415 \\ type,
5416 \\ };
5417 \\};
5418 \\
5419 \\test {
5420 \\ const E = enum { anyopaque };
5421 \\ _ = U{ .type = {} };
5422 \\ _ = U.type;
5423 \\ _ = E.anyopaque;
5424 \\}
5425 \\
5426 \\fn @"i10"(@"void": @"anyopaque", @"type": @"anyopaque".type) error{null}!void {
5427 \\ var @"f32" = @"void";
5428 \\ @"f32".u8 = false;
5429 \\ _ = @"type";
5430 \\ _ = type;
5431 \\ if (@"f32".u8) {
5432 \\ return @"i10"(.{ .u8 = true, ._ = false }, false);
5433 \\ } else {
5434 \\ return error.null;
5435 \\ }
5436 \\}
5437 \\
5438 \\test @"i10" {
5439 \\ try @"i10"(.{}, true);
5440 \\ _ = void: while (null) |@"u3"| {
5441 \\ break :void @"u3";
5442 \\ };
5443 \\ _ = void: {
5444 \\ break :void;
5445 \\ };
5446 \\ for ("hi") |@"u3", @"i4"| {
5447 \\ _ = @"u3";
5448 \\ _ = @"i4";
5449 \\ }
5450 \\ if (false) {} else |@"bool"| {
5451 \\ _ = @"bool";
5452 \\ }
5453 \\}
5454 \\
5455 );
5456}
5457
5458// Never unescape names spelled like keywords.
5459test "zig fmt: canonicalize symbols (keywords)" {
5460 try testCanonical(
5461 \\const @"enum" = struct {
5462 \\ @"error": @"struct" = true,
5463 \\ const @"struct" = bool;
5464 \\};
5465 \\
5466 \\fn @"usingnamespace"(@"union": @"enum") error{@"try"}!void {
5467 \\ var @"struct" = @"union";
5468 \\ @"struct".@"error" = false;
5469 \\ if (@"struct".@"error") {
5470 \\ return @"usingnamespace"(.{ .@"error" = false });
5471 \\ } else {
5472 \\ return error.@"try";
5473 \\ }
5474 \\}
5475 \\
5476 \\test @"usingnamespace" {
5477 \\ try @"usingnamespace"(.{});
5478 \\ _ = @"return": {
5479 \\ break :@"return" 4;
5480 \\ };
5481 \\}
5482 \\
5483 );
5484}
5485
5486// Normalize \xNN and \u{NN} escapes and unicode inside @"" escapes.
5487test "zig fmt: canonicalize symbols (character escapes)" {
5488 try testTransform(
5489 \\const @"\x46\x6f\x6f\x64" = struct {
5490 \\ @"\x62\x61\x72\x6E": @"\x43\x72\x61\x62" = false,
5491 \\ @"\u{67}\u{6C}o\u{70}\xFF": @"Cra\x62" = false,
5492 \\ @"\x65\x72\x72\x6F\x72": Crab = true,
5493 \\ @"\x74\x72\x79": Crab = true,
5494 \\ @"\u{74}\u{79}\u{70}\u{65}": @"any\u{6F}\u{70}\u{61}\u{71}\u{75}\u{65}",
5495 \\
5496 \\ const @"\x43\x72\x61\x62" = bool;
5497 \\ const @"\x61\x6E\x79\x6F\x70\x61que" = void;
5498 \\};
5499 \\
5500 \\test "unicode" {
5501 \\ const @"cąbbäge ⚡" = 2;
5502 \\ _ = @"cąbbäge ⚡";
5503 \\ const @"\u{01f422} friend\u{f6}" = 4;
5504 \\ _ = @"🐢 friendö";
5505 \\}
5506 \\
5507 ,
5508 \\const Food = struct {
5509 \\ barn: Crab = false,
5510 \\ @"glop\xFF": Crab = false,
5511 \\ @"error": Crab = true,
5512 \\ @"try": Crab = true,
5513 \\ type: @"anyopaque",
5514 \\
5515 \\ const Crab = bool;
5516 \\ const @"anyopaque" = void;
5517 \\};
5518 \\
5519 \\test "unicode" {
5520 \\ const @"cąbbäge ⚡" = 2;
5521 \\ _ = @"cąbbäge ⚡";
5522 \\ const @"\u{01f422} friend\u{f6}" = 4;
5523 \\ _ = @"🐢 friendö";
5524 \\}
5525 \\
5526 );
5527}
5528
5529test "zig fmt: canonicalize symbols (asm)" {
5530 try testTransform(
5531 \\test "asm" {
5532 \\ const @"null" = usize;
5533 \\ const @"try": usize = 808;
5534 \\ const arg: usize = 2;
5535 \\ _ = asm volatile ("syscall"
5536 \\ : [@"void"] "={rax}" (-> @"null"),
5537 \\ : [@"error"] "{rax}" (@"try"),
5538 \\ [@"arg1"] "{rdi}" (arg),
5539 \\ [arg2] "{rsi}" (arg),
5540 \\ [arg3] "{rdx}" (arg),
5541 \\ : "rcx", "r11"
5542 \\ );
5543 \\
5544 \\ const @"false": usize = 10;
5545 \\ const @"true" = "explode";
5546 \\ _ = asm volatile (@"true"
5547 \\ : [one] "={rax}" (@"false"),
5548 \\ : [two] "{rax}" (@"false"),
5549 \\ );
5550 \\}
5551 \\
5552 ,
5553 \\test "asm" {
5554 \\ const @"null" = usize;
5555 \\ const @"try": usize = 808;
5556 \\ const arg: usize = 2;
5557 \\ _ = asm volatile ("syscall"
5558 \\ : [void] "={rax}" (-> @"null"),
5559 \\ : [@"error"] "{rax}" (@"try"),
5560 \\ [arg1] "{rdi}" (arg),
5561 \\ [arg2] "{rsi}" (arg),
5562 \\ [arg3] "{rdx}" (arg),
5563 \\ : "rcx", "r11"
5564 \\ );
5565 \\
5566 \\ const @"false": usize = 10;
5567 \\ const @"true" = "explode";
5568 \\ _ = asm volatile (@"true"
5569 \\ : [one] "={rax}" (false),
5570 \\ : [two] "{rax}" (@"false"),
5571 \\ );
5572 \\}
5573 \\
5574 );
5575}
5576
5201test "zig fmt: error for missing sentinel value in sentinel slice" {5577test "zig fmt: error for missing sentinel value in sentinel slice" {
5202 try testError(5578 try testError(
5203 \\const foo = foo[0..:];5579 \\const foo = foo[0..:];
lib/std/zig/primitives.zig created+63
...@@ -0,0 +1,63 @@
1const std = @import("std");
2
3/// Set of primitive type and value names.
4/// Does not include `_` or integer type names.
5pub const names = std.ComptimeStringMap(void, .{
6 .{"anyerror"},
7 .{"anyframe"},
8 .{"anyopaque"},
9 .{"bool"},
10 .{"c_int"},
11 .{"c_long"},
12 .{"c_longdouble"},
13 .{"c_longlong"},
14 .{"c_short"},
15 .{"c_uint"},
16 .{"c_ulong"},
17 .{"c_ulonglong"},
18 .{"c_ushort"},
19 .{"comptime_float"},
20 .{"comptime_int"},
21 .{"f128"},
22 .{"f16"},
23 .{"f32"},
24 .{"f64"},
25 .{"f80"},
26 .{"false"},
27 .{"isize"},
28 .{"noreturn"},
29 .{"null"},
30 .{"true"},
31 .{"type"},
32 .{"undefined"},
33 .{"usize"},
34 .{"void"},
35});
36
37/// Returns true if a name matches a primitive type or value, excluding `_`.
38/// Integer type names like `u8` or `i32` are only matched for syntax,
39/// so this will still return true when they have an oversized bit count
40/// or leading zeroes.
41pub fn isPrimitive(name: []const u8) bool {
42 if (names.get(name) != null) return true;
43 if (name.len < 2) return false;
44 const first_c = name[0];
45 if (first_c != 'i' and first_c != 'u') return false;
46 for (name[1..]) |c| switch (c) {
47 '0'...'9' => {},
48 else => return false,
49 };
50 return true;
51}
52
53test "isPrimitive" {
54 const expect = std.testing.expect;
55 try expect(!isPrimitive(""));
56 try expect(!isPrimitive("_"));
57 try expect(!isPrimitive("haberdasher"));
58 try expect(isPrimitive("bool"));
59 try expect(isPrimitive("false"));
60 try expect(isPrimitive("comptime_float"));
61 try expect(isPrimitive("u1"));
62 try expect(isPrimitive("i99999999999999"));
63}
lib/std/zig/render.zig+207-40
...@@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator;...@@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator;
5const meta = std.meta;5const meta = std.meta;
6const Ast = std.zig.Ast;6const Ast = std.zig.Ast;
7const Token = std.zig.Token;7const Token = std.zig.Token;
8const primitives = std.zig.primitives;
89
9const indent_delta = 4;10const indent_delta = 4;
10const asm_indent_delta = 2;11const asm_indent_delta = 2;
...@@ -152,8 +153,10 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac...@@ -152,8 +153,10 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac
152 const test_token = main_tokens[decl];153 const test_token = main_tokens[decl];
153 try renderToken(ais, tree, test_token, .space);154 try renderToken(ais, tree, test_token, .space);
154 const test_name_tag = token_tags[test_token + 1];155 const test_name_tag = token_tags[test_token + 1];
155 if (test_name_tag == .string_literal or test_name_tag == .identifier) {156 switch (test_name_tag) {
156 try renderToken(ais, tree, test_token + 1, .space);157 .string_literal => try renderToken(ais, tree, test_token + 1, .space),
158 .identifier => try renderIdentifier(ais, tree, test_token + 1, .space, .preserve_when_shadowing),
159 else => {},
157 }160 }
158 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);161 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
159 },162 },
...@@ -192,11 +195,10 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -192,11 +195,10 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
192 const lexeme = tokenSliceForRender(tree, token_index);195 const lexeme = tokenSliceForRender(tree, token_index);
193 if (mem.eql(u8, lexeme, "c_void")) {196 if (mem.eql(u8, lexeme, "c_void")) {
194 try ais.writer().writeAll("anyopaque");197 try ais.writer().writeAll("anyopaque");
198 return renderSpace(ais, tree, token_index, lexeme.len, space);
195 } else {199 } else {
196 try ais.writer().writeAll(lexeme);200 return renderIdentifier(ais, tree, token_index, space, .preserve_when_shadowing);
197 }201 }
198
199 return renderSpace(ais, tree, token_index, lexeme.len, space);
200 },202 },
201203
202 .number_literal,204 .number_literal,
...@@ -226,7 +228,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -226,7 +228,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
226 .error_value => {228 .error_value => {
227 try renderToken(ais, tree, main_tokens[node], .none);229 try renderToken(ais, tree, main_tokens[node], .none);
228 try renderToken(ais, tree, main_tokens[node] + 1, .none);230 try renderToken(ais, tree, main_tokens[node] + 1, .none);
229 return renderToken(ais, tree, main_tokens[node] + 2, space);231 return renderIdentifier(ais, tree, main_tokens[node] + 2, space, .eagerly_unquote);
230 },232 },
231233
232 .block_two,234 .block_two,
...@@ -256,7 +258,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -256,7 +258,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
256 try renderToken(ais, tree, defer_token, .space);258 try renderToken(ais, tree, defer_token, .space);
257 if (payload_token != 0) {259 if (payload_token != 0) {
258 try renderToken(ais, tree, payload_token - 1, .none); // |260 try renderToken(ais, tree, payload_token - 1, .none); // |
259 try renderToken(ais, tree, payload_token, .none); // identifier261 try renderIdentifier(ais, tree, payload_token, .none, .preserve_when_shadowing); // identifier
260 try renderToken(ais, tree, payload_token + 1, .space); // |262 try renderToken(ais, tree, payload_token + 1, .space); // |
261 }263 }
262 return renderExpression(gpa, ais, tree, expr, space);264 return renderExpression(gpa, ais, tree, expr, space);
...@@ -294,7 +296,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -294,7 +296,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
294 if (token_tags[fallback_first - 1] == .pipe) {296 if (token_tags[fallback_first - 1] == .pipe) {
295 try renderToken(ais, tree, main_token, .space); // catch keyword297 try renderToken(ais, tree, main_token, .space); // catch keyword
296 try renderToken(ais, tree, main_token + 1, .none); // pipe298 try renderToken(ais, tree, main_token + 1, .none); // pipe
297 try renderToken(ais, tree, main_token + 2, .none); // payload identifier299 try renderIdentifier(ais, tree, main_token + 2, .none, .preserve_when_shadowing); // payload identifier
298 try renderToken(ais, tree, main_token + 3, after_op_space); // pipe300 try renderToken(ais, tree, main_token + 3, after_op_space); // pipe
299 } else {301 } else {
300 assert(token_tags[fallback_first - 1] == .keyword_catch);302 assert(token_tags[fallback_first - 1] == .keyword_catch);
...@@ -320,7 +322,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -320,7 +322,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
320 ais.pushIndentOneShot();322 ais.pushIndentOneShot();
321 }323 }
322324
323 try renderToken(ais, tree, main_token, .none);325 try renderToken(ais, tree, main_token, .none); // .
324326
325 // This check ensures that zag() is indented in the following example:327 // This check ensures that zag() is indented in the following example:
326 // const x = foo328 // const x = foo
...@@ -331,7 +333,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -331,7 +333,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
331 ais.pushIndentOneShot();333 ais.pushIndentOneShot();
332 }334 }
333335
334 return renderToken(ais, tree, field_access.rhs, space);336 return renderIdentifier(ais, tree, field_access.rhs, space, .eagerly_unquote); // field
335 },337 },
336338
337 .error_union,339 .error_union,
...@@ -514,11 +516,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -514,11 +516,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
514 } else if (label_token != 0 and target == 0) {516 } else if (label_token != 0 and target == 0) {
515 try renderToken(ais, tree, main_token, .space); // break keyword517 try renderToken(ais, tree, main_token, .space); // break keyword
516 try renderToken(ais, tree, label_token - 1, .none); // colon518 try renderToken(ais, tree, label_token - 1, .none); // colon
517 try renderToken(ais, tree, label_token, space); // identifier519 try renderIdentifier(ais, tree, label_token, space, .eagerly_unquote); // identifier
518 } else if (label_token != 0 and target != 0) {520 } else if (label_token != 0 and target != 0) {
519 try renderToken(ais, tree, main_token, .space); // break keyword521 try renderToken(ais, tree, main_token, .space); // break keyword
520 try renderToken(ais, tree, label_token - 1, .none); // colon522 try renderToken(ais, tree, label_token - 1, .none); // colon
521 try renderToken(ais, tree, label_token, .space); // identifier523 try renderIdentifier(ais, tree, label_token, .space, .eagerly_unquote); // identifier
522 try renderExpression(gpa, ais, tree, target, space);524 try renderExpression(gpa, ais, tree, target, space);
523 }525 }
524 },526 },
...@@ -529,7 +531,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -529,7 +531,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
529 if (label != 0) {531 if (label != 0) {
530 try renderToken(ais, tree, main_token, .space); // continue532 try renderToken(ais, tree, main_token, .space); // continue
531 try renderToken(ais, tree, label - 1, .none); // :533 try renderToken(ais, tree, label - 1, .none); // :
532 return renderToken(ais, tree, label, space); // label534 return renderIdentifier(ais, tree, label, space, .eagerly_unquote); // label
533 } else {535 } else {
534 return renderToken(ais, tree, main_token, space); // continue536 return renderToken(ais, tree, main_token, space); // continue
535 }537 }
...@@ -590,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -590,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
590 // There is exactly one member and no trailing comma or592 // There is exactly one member and no trailing comma or
591 // comments, so render without surrounding spaces: `error{Foo}`593 // comments, so render without surrounding spaces: `error{Foo}`
592 try renderToken(ais, tree, lbrace, .none);594 try renderToken(ais, tree, lbrace, .none);
593 try renderToken(ais, tree, lbrace + 1, .none); // identifier595 try renderIdentifier(ais, tree, lbrace + 1, .none, .eagerly_unquote); // identifier
594 return renderToken(ais, tree, rbrace, space);596 return renderToken(ais, tree, rbrace, space);
595 } else if (token_tags[rbrace - 1] == .comma) {597 } else if (token_tags[rbrace - 1] == .comma) {
596 // There is a trailing comma so render each member on a new line.598 // There is a trailing comma so render each member on a new line.
...@@ -601,7 +603,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -601,7 +603,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
601 if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);603 if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);
602 switch (token_tags[i]) {604 switch (token_tags[i]) {
603 .doc_comment => try renderToken(ais, tree, i, .newline),605 .doc_comment => try renderToken(ais, tree, i, .newline),
604 .identifier => try renderToken(ais, tree, i, .comma),606 .identifier => try renderIdentifier(ais, tree, i, .comma, .eagerly_unquote),
605 .comma => {},607 .comma => {},
606 else => unreachable,608 else => unreachable,
607 }609 }
...@@ -615,7 +617,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -615,7 +617,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
615 while (i < rbrace) : (i += 1) {617 while (i < rbrace) : (i += 1) {
616 switch (token_tags[i]) {618 switch (token_tags[i]) {
617 .doc_comment => unreachable, // TODO619 .doc_comment => unreachable, // TODO
618 .identifier => try renderToken(ais, tree, i, .comma_space),620 .identifier => try renderIdentifier(ais, tree, i, .comma_space, .eagerly_unquote),
619 .comma => {},621 .comma => {},
620 else => unreachable,622 else => unreachable,
621 }623 }
...@@ -702,7 +704,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -702,7 +704,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
702704
703 .enum_literal => {705 .enum_literal => {
704 try renderToken(ais, tree, main_tokens[node] - 1, .none); // .706 try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
705 return renderToken(ais, tree, main_tokens[node], space); // name707 return renderIdentifier(ais, tree, main_tokens[node], space, .eagerly_unquote); // name
706 },708 },
707709
708 .fn_decl => unreachable,710 .fn_decl => unreachable,
...@@ -887,7 +889,7 @@ fn renderAsmOutput(...@@ -887,7 +889,7 @@ fn renderAsmOutput(
887 const symbolic_name = main_tokens[asm_output];889 const symbolic_name = main_tokens[asm_output];
888890
889 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket891 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
890 try renderToken(ais, tree, symbolic_name, .none); // ident892 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident
891 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket893 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
892 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"894 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
893 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen895 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
...@@ -897,7 +899,7 @@ fn renderAsmOutput(...@@ -897,7 +899,7 @@ fn renderAsmOutput(
897 try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none);899 try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none);
898 return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen900 return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen
899 } else {901 } else {
900 try renderToken(ais, tree, symbolic_name + 4, .none); // ident902 try renderIdentifier(ais, tree, symbolic_name + 4, .none, .eagerly_unquote); // ident
901 return renderToken(ais, tree, symbolic_name + 5, space); // rparen903 return renderToken(ais, tree, symbolic_name + 5, space); // rparen
902 }904 }
903}905}
...@@ -916,7 +918,7 @@ fn renderAsmInput(...@@ -916,7 +918,7 @@ fn renderAsmInput(
916 const symbolic_name = main_tokens[asm_input];918 const symbolic_name = main_tokens[asm_input];
917919
918 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket920 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
919 try renderToken(ais, tree, symbolic_name, .none); // ident921 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident
920 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket922 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
921 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"923 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
922 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen924 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
...@@ -955,7 +957,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec...@@ -955,7 +957,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec
955 Space.space957 Space.space
956 else958 else
957 Space.none;959 Space.none;
958 try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name960 try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
959961
960 if (var_decl.ast.type_node != 0) {962 if (var_decl.ast.type_node != 0) {
961 try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :963 try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :
...@@ -1055,7 +1057,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,...@@ -1055,7 +1057,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
1055 const token_tags = tree.tokens.items(.tag);1057 const token_tags = tree.tokens.items(.tag);
10561058
1057 if (while_node.label_token) |label| {1059 if (while_node.label_token) |label| {
1058 try renderToken(ais, tree, label, .none); // label1060 try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label
1059 try renderToken(ais, tree, label + 1, .space); // :1061 try renderToken(ais, tree, label + 1, .space); // :
1060 }1062 }
10611063
...@@ -1080,11 +1082,11 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,...@@ -1080,11 +1082,11 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
1080 break :blk payload_token;1082 break :blk payload_token;
1081 }1083 }
1082 };1084 };
1083 try renderToken(ais, tree, ident, .none); // identifier1085 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
1084 const pipe = blk: {1086 const pipe = blk: {
1085 if (token_tags[ident + 1] == .comma) {1087 if (token_tags[ident + 1] == .comma) {
1086 try renderToken(ais, tree, ident + 1, .space); // ,1088 try renderToken(ais, tree, ident + 1, .space); // ,
1087 try renderToken(ais, tree, ident + 2, .none); // index1089 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // index
1088 break :blk ident + 3;1090 break :blk ident + 3;
1089 } else {1091 } else {
1090 break :blk ident + 1;1092 break :blk ident + 1;
...@@ -1127,7 +1129,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,...@@ -1127,7 +1129,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
1127 if (while_node.error_token) |error_token| {1129 if (while_node.error_token) |error_token| {
1128 try renderToken(ais, tree, while_node.else_token, .space); // else1130 try renderToken(ais, tree, while_node.else_token, .space); // else
1129 try renderToken(ais, tree, error_token - 1, .none); // |1131 try renderToken(ais, tree, error_token - 1, .none); // |
1130 try renderToken(ais, tree, error_token, .none); // identifier1132 try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier
1131 last_else_token = error_token + 1; // |1133 last_else_token = error_token + 1; // |
1132 }1134 }
11331135
...@@ -1163,10 +1165,10 @@ fn renderContainerField(...@@ -1163,10 +1165,10 @@ fn renderContainerField(
1163 try renderToken(ais, tree, t, .space); // comptime1165 try renderToken(ais, tree, t, .space); // comptime
1164 }1166 }
1165 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {1167 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1166 return renderTokenComma(ais, tree, field.ast.name_token, space); // name1168 return renderIdentifierComma(ais, tree, field.ast.name_token, space, .eagerly_unquote); // name
1167 }1169 }
1168 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {1170 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1169 try renderToken(ais, tree, field.ast.name_token, .none); // name1171 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name
1170 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :1172 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
11711173
1172 if (field.ast.align_expr != 0) {1174 if (field.ast.align_expr != 0) {
...@@ -1182,12 +1184,12 @@ fn renderContainerField(...@@ -1182,12 +1184,12 @@ fn renderContainerField(
1182 }1184 }
1183 }1185 }
1184 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {1186 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1185 try renderToken(ais, tree, field.ast.name_token, .space); // name1187 try renderIdentifier(ais, tree, field.ast.name_token, .space, .eagerly_unquote); // name
1186 try renderToken(ais, tree, field.ast.name_token + 1, .space); // =1188 try renderToken(ais, tree, field.ast.name_token + 1, .space); // =
1187 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1189 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1188 }1190 }
11891191
1190 try renderToken(ais, tree, field.ast.name_token, .none); // name1192 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name
1191 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :1193 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
1192 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1194 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
11931195
...@@ -1294,7 +1296,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1294,7 +1296,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1294 const after_fn_token = fn_proto.ast.fn_token + 1;1296 const after_fn_token = fn_proto.ast.fn_token + 1;
1295 const lparen = if (token_tags[after_fn_token] == .identifier) blk: {1297 const lparen = if (token_tags[after_fn_token] == .identifier) blk: {
1296 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn1298 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
1297 try renderToken(ais, tree, after_fn_token, .none); // name1299 try renderIdentifier(ais, tree, after_fn_token, .none, .preserve_when_shadowing); // name
1298 break :blk after_fn_token + 1;1300 break :blk after_fn_token + 1;
1299 } else blk: {1301 } else blk: {
1300 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn1302 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
...@@ -1383,7 +1385,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1383,7 +1385,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1383 if (token_tags[last_param_token] == .identifier and1385 if (token_tags[last_param_token] == .identifier and
1384 token_tags[last_param_token + 1] == .colon)1386 token_tags[last_param_token + 1] == .colon)
1385 {1387 {
1386 try renderToken(ais, tree, last_param_token, .none); // name1388 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
1387 last_param_token += 1;1389 last_param_token += 1;
1388 try renderToken(ais, tree, last_param_token, .space); // :1390 try renderToken(ais, tree, last_param_token, .space); // :
1389 last_param_token += 1;1391 last_param_token += 1;
...@@ -1432,7 +1434,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1432,7 +1434,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1432 if (token_tags[last_param_token] == .identifier and1434 if (token_tags[last_param_token] == .identifier and
1433 token_tags[last_param_token + 1] == .colon)1435 token_tags[last_param_token + 1] == .colon)
1434 {1436 {
1435 try renderToken(ais, tree, last_param_token, .none); // name1437 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
1436 last_param_token += 1;1438 last_param_token += 1;
1437 try renderToken(ais, tree, last_param_token, .space); // :1439 try renderToken(ais, tree, last_param_token, .space); // :
1438 last_param_token += 1;1440 last_param_token += 1;
...@@ -1545,7 +1547,7 @@ fn renderSwitchCase(...@@ -1545,7 +1547,7 @@ fn renderSwitchCase(
1545 else1547 else
1546 Space.space;1548 Space.space;
1547 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;1549 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
1548 try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space);1550 try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space); // =>
15491551
1550 if (switch_case.payload_token) |payload_token| {1552 if (switch_case.payload_token) |payload_token| {
1551 try renderToken(ais, tree, payload_token - 1, .none); // pipe1553 try renderToken(ais, tree, payload_token - 1, .none); // pipe
...@@ -1553,10 +1555,10 @@ fn renderSwitchCase(...@@ -1553,10 +1555,10 @@ fn renderSwitchCase(
1553 if (token_tags[payload_token] == .asterisk) {1555 if (token_tags[payload_token] == .asterisk) {
1554 try renderToken(ais, tree, payload_token, .none); // asterisk1556 try renderToken(ais, tree, payload_token, .none); // asterisk
1555 }1557 }
1556 try renderToken(ais, tree, ident, .none); // identifier1558 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
1557 if (token_tags[ident + 1] == .comma) {1559 if (token_tags[ident + 1] == .comma) {
1558 try renderToken(ais, tree, ident + 1, .space); // ,1560 try renderToken(ais, tree, ident + 1, .space); // ,
1559 try renderToken(ais, tree, ident + 2, .none); // identifier1561 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // identifier
1560 try renderToken(ais, tree, ident + 3, pre_target_space); // pipe1562 try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
1561 } else {1563 } else {
1562 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe1564 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
...@@ -1581,8 +1583,8 @@ fn renderBlock(...@@ -1581,8 +1583,8 @@ fn renderBlock(
1581 if (token_tags[lbrace - 1] == .colon and1583 if (token_tags[lbrace - 1] == .colon and
1582 token_tags[lbrace - 2] == .identifier)1584 token_tags[lbrace - 2] == .identifier)
1583 {1585 {
1584 try renderToken(ais, tree, lbrace - 2, .none);1586 try renderIdentifier(ais, tree, lbrace - 2, .none, .eagerly_unquote); // identifier
1585 try renderToken(ais, tree, lbrace - 1, .space);1587 try renderToken(ais, tree, lbrace - 1, .space); // :
1586 }1588 }
15871589
1588 ais.pushIndentNextLine();1590 ais.pushIndentNextLine();
...@@ -1635,7 +1637,7 @@ fn renderStructInit(...@@ -1635,7 +1637,7 @@ fn renderStructInit(
1635 try renderToken(ais, tree, struct_init.ast.lbrace, .newline);1637 try renderToken(ais, tree, struct_init.ast.lbrace, .newline);
16361638
1637 try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // .1639 try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // .
1638 try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name1640 try renderIdentifier(ais, tree, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
1639 try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // =1641 try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // =
1640 try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);1642 try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);
16411643
...@@ -1643,7 +1645,7 @@ fn renderStructInit(...@@ -1643,7 +1645,7 @@ fn renderStructInit(
1643 const init_token = tree.firstToken(field_init);1645 const init_token = tree.firstToken(field_init);
1644 try renderExtraNewlineToken(ais, tree, init_token - 3);1646 try renderExtraNewlineToken(ais, tree, init_token - 3);
1645 try renderToken(ais, tree, init_token - 3, .none); // .1647 try renderToken(ais, tree, init_token - 3, .none); // .
1646 try renderToken(ais, tree, init_token - 2, .space); // name1648 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
1647 try renderToken(ais, tree, init_token - 1, .space); // =1649 try renderToken(ais, tree, init_token - 1, .space); // =
1648 try renderExpression(gpa, ais, tree, field_init, .comma);1650 try renderExpression(gpa, ais, tree, field_init, .comma);
1649 }1651 }
...@@ -1656,7 +1658,7 @@ fn renderStructInit(...@@ -1656,7 +1658,7 @@ fn renderStructInit(
1656 for (struct_init.ast.fields) |field_init| {1658 for (struct_init.ast.fields) |field_init| {
1657 const init_token = tree.firstToken(field_init);1659 const init_token = tree.firstToken(field_init);
1658 try renderToken(ais, tree, init_token - 3, .none); // .1660 try renderToken(ais, tree, init_token - 3, .none); // .
1659 try renderToken(ais, tree, init_token - 2, .space); // name1661 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
1660 try renderToken(ais, tree, init_token - 1, .space); // =1662 try renderToken(ais, tree, init_token - 1, .space); // =
1661 try renderExpression(gpa, ais, tree, field_init, .comma_space);1663 try renderExpression(gpa, ais, tree, field_init, .comma_space);
1662 }1664 }
...@@ -2310,6 +2312,19 @@ fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) E...@@ -2310,6 +2312,19 @@ fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) E
2310 }2312 }
2311}2313}
23122314
2315/// Render an identifier, and the comma that follows it, if it is present in the source.
2316/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2317fn renderIdentifierComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {
2318 const token_tags = tree.tokens.items(.tag);
2319 const maybe_comma = token + 1;
2320 if (token_tags[maybe_comma] == .comma and space != .comma) {
2321 try renderIdentifier(ais, tree, token, .none, quote);
2322 return renderToken(ais, tree, maybe_comma, space);
2323 } else {
2324 return renderIdentifier(ais, tree, token, space, quote);
2325 }
2326}
2327
2313const Space = enum {2328const Space = enum {
2314 /// Output the token lexeme only.2329 /// Output the token lexeme only.
2315 none,2330 none,
...@@ -2377,6 +2392,158 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us...@@ -2377,6 +2392,158 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
2377 }2392 }
2378}2393}
23792394
2395const QuoteBehavior = enum {
2396 preserve_when_shadowing,
2397 eagerly_unquote,
2398};
2399
2400fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {
2401 const token_tags = tree.tokens.items(.tag);
2402 assert(token_tags[token_index] == .identifier);
2403 const lexeme = tokenSliceForRender(tree, token_index);
2404 if (lexeme[0] != '@') {
2405 return renderToken(ais, tree, token_index, space);
2406 }
2407
2408 assert(lexeme.len >= 3);
2409 assert(lexeme[0] == '@');
2410 assert(lexeme[1] == '\"');
2411 assert(lexeme[lexeme.len - 1] == '\"');
2412 const contents = lexeme[2 .. lexeme.len - 1]; // inside the @"" quotation
2413
2414 // Empty name can't be unquoted.
2415 if (contents.len == 0) {
2416 return renderQuotedIdentifier(ais, tree, token_index, space, false);
2417 }
2418
2419 // Special case for _ which would incorrectly be rejected by isValidId below.
2420 if (contents.len == 1 and contents[0] == '_') switch (quote) {
2421 .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),
2422 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2423 };
2424
2425 // Scan the entire name for characters that would (after un-escaping) be illegal in a symbol,
2426 // i.e. contents don't match: [A-Za-z_][A-Za-z0-9_]*
2427 var contents_i: usize = 0;
2428 while (contents_i < contents.len) {
2429 switch (contents[contents_i]) {
2430 '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false),
2431 'A'...'Z', 'a'...'z', '_' => {},
2432 '\\' => {
2433 var esc_offset = contents_i;
2434 const res = std.zig.string_literal.parseEscapeSequence(contents, &esc_offset);
2435 switch (res) {
2436 .success => |char| switch (char) {
2437 '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false),
2438 'A'...'Z', 'a'...'z', '_' => {},
2439 else => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2440 },
2441 .failure => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2442 }
2443 contents_i += esc_offset;
2444 continue;
2445 },
2446 else => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2447 }
2448 contents_i += 1;
2449 }
2450
2451 // Read enough of the name (while un-escaping) to determine if it's a keyword or primitive.
2452 // If it's too long to fit in this buffer, we know it's neither and quoting is unnecessary.
2453 // If we read the whole thing, we have to do further checks.
2454 const longest_keyword_or_primitive_len = comptime blk: {
2455 var longest = 0;
2456 for (primitives.names.kvs) |kv| {
2457 if (kv.key.len > longest) longest = kv.key.len;
2458 }
2459 for (std.zig.Token.keywords.kvs) |kv| {
2460 if (kv.key.len > longest) longest = kv.key.len;
2461 }
2462 break :blk longest;
2463 };
2464 var buf: [longest_keyword_or_primitive_len]u8 = undefined;
2465
2466 contents_i = 0;
2467 var buf_i: usize = 0;
2468 while (contents_i < contents.len and buf_i < longest_keyword_or_primitive_len) {
2469 if (contents[contents_i] == '\\') {
2470 const res = std.zig.string_literal.parseEscapeSequence(contents, &contents_i).success;
2471 buf[buf_i] = @intCast(u8, res);
2472 buf_i += 1;
2473 } else {
2474 buf[buf_i] = contents[contents_i];
2475 contents_i += 1;
2476 buf_i += 1;
2477 }
2478 }
2479
2480 // We read the whole thing, so it could be a keyword or primitive.
2481 if (contents_i == contents.len) {
2482 if (!std.zig.isValidId(buf[0..buf_i])) {
2483 return renderQuotedIdentifier(ais, tree, token_index, space, false);
2484 }
2485 if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) {
2486 .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),
2487 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2488 };
2489 }
2490
2491 try renderQuotedIdentifier(ais, tree, token_index, space, true);
2492}
2493
2494// Renders a @"" quoted identifier, normalizing escapes.
2495// Unnecessary escapes are un-escaped, and \u escapes are normalized to \x when they fit.
2496// If unquote is true, the @"" is removed and the result is a bare symbol whose validity is asserted.
2497fn renderQuotedIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, comptime unquote: bool) !void {
2498 const token_tags = tree.tokens.items(.tag);
2499 assert(token_tags[token_index] == .identifier);
2500 const lexeme = tokenSliceForRender(tree, token_index);
2501 assert(lexeme.len >= 3 and lexeme[0] == '@');
2502
2503 if (!unquote) try ais.writer().writeAll("@\"");
2504 const contents = lexeme[2 .. lexeme.len - 1];
2505 try renderIdentifierContents(ais.writer(), contents);
2506 if (!unquote) try ais.writer().writeByte('\"');
2507
2508 try renderSpace(ais, tree, token_index, lexeme.len, space);
2509}
2510
2511fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {
2512 var pos: usize = 0;
2513 while (pos < bytes.len) {
2514 const byte = bytes[pos];
2515 switch (byte) {
2516 '\\' => {
2517 const old_pos = pos;
2518 const res = std.zig.string_literal.parseEscapeSequence(bytes, &pos);
2519 const escape_sequence = bytes[old_pos..pos];
2520 switch (res) {
2521 .success => |codepoint| {
2522 if (codepoint <= 0x7f) {
2523 const buf = [1]u8{@intCast(u8, codepoint)};
2524 try std.fmt.format(writer, "{}", .{std.zig.fmtEscapes(&buf)});
2525 } else {
2526 try writer.writeAll(escape_sequence);
2527 }
2528 },
2529 .failure => {
2530 try writer.writeAll(escape_sequence);
2531 },
2532 }
2533 },
2534 0x00...('\\' - 1), ('\\' + 1)...0x7f => {
2535 const buf = [1]u8{@intCast(u8, byte)};
2536 try std.fmt.format(writer, "{}", .{std.zig.fmtEscapes(&buf)});
2537 pos += 1;
2538 },
2539 0x80...0xff => {
2540 try writer.writeByte(byte);
2541 pos += 1;
2542 },
2543 }
2544 }
2545}
2546
2380/// Returns true if there exists a line comment between any of the tokens from2547/// Returns true if there exists a line comment between any of the tokens from
2381/// `start_token` to `end_token`. This is used to determine if e.g. a2548/// `start_token` to `end_token`. This is used to determine if e.g. a
2382/// fn_proto should be wrapped and have a trailing comma inserted even if2549/// fn_proto should be wrapped and have a trailing comma inserted even if
lib/std/zig/string_literal.zig+40-2
...@@ -63,7 +63,7 @@ pub fn parseCharLiteral(slice: []const u8) ParsedCharLiteral {...@@ -63,7 +63,7 @@ pub fn parseCharLiteral(slice: []const u8) ParsedCharLiteral {
6363
64/// Parse an escape sequence from `slice[offset..]`. If parsing is successful,64/// Parse an escape sequence from `slice[offset..]`. If parsing is successful,
65/// offset is updated to reflect the characters consumed.65/// offset is updated to reflect the characters consumed.
66fn parseEscapeSequence(slice: []const u8, offset: *usize) ParsedCharLiteral {66pub fn parseEscapeSequence(slice: []const u8, offset: *usize) ParsedCharLiteral {
67 assert(slice.len > offset.*);67 assert(slice.len > offset.*);
68 assert(slice[offset.*] == '\\');68 assert(slice[offset.*] == '\\');
6969
...@@ -274,12 +274,50 @@ pub fn parseAlloc(allocator: std.mem.Allocator, bytes: []const u8) ParseError![]...@@ -274,12 +274,50 @@ pub fn parseAlloc(allocator: std.mem.Allocator, bytes: []const u8) ParseError![]
274 var buf = std.ArrayList(u8).init(allocator);274 var buf = std.ArrayList(u8).init(allocator);
275 defer buf.deinit();275 defer buf.deinit();
276276
277 switch (try parseAppend(&buf, bytes)) {277 switch (try parseWrite(buf.writer(), bytes)) {
278 .success => return buf.toOwnedSlice(),278 .success => return buf.toOwnedSlice(),
279 .failure => return error.InvalidLiteral,279 .failure => return error.InvalidLiteral,
280 }280 }
281}281}
282282
283/// Parses `bytes` as a Zig string literal and writes the result to the std.io.Writer type.
284/// Asserts `bytes` has '"' at beginning and end.
285pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result {
286 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
287
288 var index: usize = 1;
289 while (true) {
290 const b = bytes[index];
291
292 switch (b) {
293 '\\' => {
294 const escape_char_index = index + 1;
295 const result = parseEscapeSequence(bytes, &index);
296 switch (result) {
297 .success => |codepoint| {
298 if (bytes[escape_char_index] == 'u') {
299 var buf: [3]u8 = undefined;
300 const len = utf8Encode(codepoint, &buf) catch {
301 return Result{ .failure = .{ .invalid_unicode_codepoint = escape_char_index + 1 } };
302 };
303 try writer.writeAll(buf[0..len]);
304 } else {
305 try writer.writeByte(@intCast(u8, codepoint));
306 }
307 },
308 .failure => |err| return Result{ .failure = err },
309 }
310 },
311 '\n' => return Result{ .failure = .{ .invalid_character = index } },
312 '"' => return Result.success,
313 else => {
314 try writer.writeByte(b);
315 index += 1;
316 },
317 }
318 } else unreachable; // TODO should not need else unreachable on while(true)
319}
320
283test "parse" {321test "parse" {
284 const expect = std.testing.expect;322 const expect = std.testing.expect;
285 const expectError = std.testing.expectError;323 const expectError = std.testing.expectError;
src/AstGen.zig+22-44
...@@ -10,6 +10,8 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged;...@@ -10,6 +10,8 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const StringIndexAdapter = std.hash_map.StringIndexAdapter;10const StringIndexAdapter = std.hash_map.StringIndexAdapter;
11const StringIndexContext = std.hash_map.StringIndexContext;11const StringIndexContext = std.hash_map.StringIndexContext;
1212
13const isPrimitive = std.zig.primitives.isPrimitive;
14
13const Zir = @import("Zir.zig");15const Zir = @import("Zir.zig");
14const refToIndex = Zir.refToIndex;16const refToIndex = Zir.refToIndex;
15const indexToRef = Zir.indexToRef;17const indexToRef = Zir.indexToRef;
...@@ -4237,33 +4239,7 @@ fn testDecl(...@@ -4237,33 +4239,7 @@ fn testDecl(
42374239
4238 // if not @"" syntax, just use raw token slice4240 // if not @"" syntax, just use raw token slice
4239 if (ident_name_raw[0] != '@') {4241 if (ident_name_raw[0] != '@') {
4240 if (primitives.get(ident_name_raw)) |_| return astgen.failTok(test_name_token, "cannot test a primitive", .{});4242 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
4241
4242 if (ident_name_raw.len >= 2) integer: {
4243 const first_c = ident_name_raw[0];
4244 if (first_c == 'i' or first_c == 'u') {
4245 _ = switch (first_c == 'i') {
4246 true => .signed,
4247 false => .unsigned,
4248 };
4249 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
4250 return astgen.failTok(
4251 test_name_token,
4252 "primitive integer type '{s}' has leading zero",
4253 .{ident_name_raw},
4254 );
4255 }
4256 _ = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
4257 error.Overflow => return astgen.failTok(
4258 test_name_token,
4259 "primitive integer type '{s}' exceeds maximum bit width of 65535",
4260 .{ident_name_raw},
4261 ),
4262 error.InvalidCharacter => break :integer,
4263 };
4264 return astgen.failTok(test_name_token, "cannot test a primitive", .{});
4265 }
4266 }
4267 }4243 }
42684244
4269 // Local variables, including function parameters.4245 // Local variables, including function parameters.
...@@ -7108,7 +7084,7 @@ fn identifier(...@@ -7108,7 +7084,7 @@ fn identifier(
71087084
7109 // if not @"" syntax, just use raw token slice7085 // if not @"" syntax, just use raw token slice
7110 if (ident_name_raw[0] != '@') {7086 if (ident_name_raw[0] != '@') {
7111 if (primitives.get(ident_name_raw)) |zir_const_ref| {7087 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
7112 return rvalue(gz, ri, zir_const_ref, ident);7088 return rvalue(gz, ri, zir_const_ref, ident);
7113 }7089 }
71147090
...@@ -8751,7 +8727,7 @@ fn calleeExpr(...@@ -8751,7 +8727,7 @@ fn calleeExpr(
8751 }8727 }
8752}8728}
87538729
8754const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{8730const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{
8755 .{ "anyerror", .anyerror_type },8731 .{ "anyerror", .anyerror_type },
8756 .{ "anyframe", .anyframe_type },8732 .{ "anyframe", .anyframe_type },
8757 .{ "anyopaque", .anyopaque_type },8733 .{ "anyopaque", .anyopaque_type },
...@@ -8795,6 +8771,21 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{...@@ -8795,6 +8771,21 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{
8795 .{ "void", .void_type },8771 .{ "void", .void_type },
8796});8772});
87978773
8774comptime {
8775 // These checks ensure that std.zig.primitives stays in synce with the primitive->Zir map.
8776 const primitives = std.zig.primitives;
8777 for (primitive_instrs.kvs) |kv| {
8778 if (!primitives.isPrimitive(kv.key)) {
8779 @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(kv.value) ++ "'");
8780 }
8781 }
8782 for (primitives.names.kvs) |kv| {
8783 if (primitive_instrs.get(kv.key) == null) {
8784 @compileError("std.zig.primitives entry '" ++ kv.key ++ "' does not have a corresponding Zir instr");
8785 }
8786 }
8787}
8788
8798fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool {8789fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool {
8799 const node_tags = tree.nodes.items(.tag);8790 const node_tags = tree.nodes.items(.tag);
8800 const node_datas = tree.nodes.items(.data);8791 const node_datas = tree.nodes.items(.data);
...@@ -9458,7 +9449,7 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In...@@ -9458,7 +9449,7 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
9458 .identifier => {9449 .identifier => {
9459 const main_tokens = tree.nodes.items(.main_token);9450 const main_tokens = tree.nodes.items(.main_token);
9460 const ident_bytes = tree.tokenSlice(main_tokens[node]);9451 const ident_bytes = tree.tokenSlice(main_tokens[node]);
9461 if (primitives.get(ident_bytes)) |primitive| switch (primitive) {9452 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
9462 .anyerror_type,9453 .anyerror_type,
9463 .anyframe_type,9454 .anyframe_type,
9464 .anyopaque_type,9455 .anyopaque_type,
...@@ -9702,7 +9693,7 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -9702,7 +9693,7 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
9702 .identifier => {9693 .identifier => {
9703 const main_tokens = tree.nodes.items(.main_token);9694 const main_tokens = tree.nodes.items(.main_token);
9704 const ident_bytes = tree.tokenSlice(main_tokens[node]);9695 const ident_bytes = tree.tokenSlice(main_tokens[node]);
9705 if (primitives.get(ident_bytes)) |primitive| switch (primitive) {9696 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
9706 .anyerror_type,9697 .anyerror_type,
9707 .anyframe_type,9698 .anyframe_type,
9708 .anyopaque_type,9699 .anyopaque_type,
...@@ -12045,19 +12036,6 @@ fn nullTerminatedString(astgen: AstGen, index: usize) [*:0]const u8 {...@@ -12045,19 +12036,6 @@ fn nullTerminatedString(astgen: AstGen, index: usize) [*:0]const u8 {
12045 return @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + index;12036 return @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + index;
12046}12037}
1204712038
12048pub fn isPrimitive(name: []const u8) bool {
12049 if (primitives.get(name) != null) return true;
12050 if (name.len < 2) return false;
12051 const first_c = name[0];
12052 if (first_c != 'i' and first_c != 'u') return false;
12053 if (parseBitCount(name[1..])) |_| {
12054 return true;
12055 } else |err| switch (err) {
12056 error.Overflow => return true,
12057 error.InvalidCharacter => return false,
12058 }
12059}
12060
12061/// Local variables shadowing detection, including function parameters.12039/// Local variables shadowing detection, including function parameters.
12062fn detectLocalShadowing(12040fn detectLocalShadowing(
12063 astgen: *AstGen,12041 astgen: *AstGen,
src/stage1/astgen.cpp+21
...@@ -3879,6 +3879,27 @@ static Stage1ZirInst *astgen_identifier(Stage1AstGen *ag, Scope *scope, AstNode...@@ -3879,6 +3879,27 @@ static Stage1ZirInst *astgen_identifier(Stage1AstGen *ag, Scope *scope, AstNode
3879 }3879 }
3880 }3880 }
38813881
3882 {
3883 Stage1ZirInst *value = nullptr;
3884 if (buf_eql_str(variable_name, "null")) {
3885 value = ir_build_const_null(ag, scope, node);
3886 } else if (buf_eql_str(variable_name, "true")) {
3887 value = ir_build_const_bool(ag, scope, node, true);
3888 } else if (buf_eql_str(variable_name, "false")) {
3889 value = ir_build_const_bool(ag, scope, node, false);
3890 } else if (buf_eql_str(variable_name, "undefined")) {
3891 value = ir_build_const_undefined(ag, scope, node);
3892 }
3893
3894 if (value != nullptr) {
3895 if (lval == LValPtr || lval == LValAssign) {
3896 return ir_build_ref_src(ag, scope, node, value);
3897 } else {
3898 return ir_expr_wrap(ag, scope, value, result_loc);
3899 }
3900 }
3901 }
3902
3882 ZigType *primitive_type;3903 ZigType *primitive_type;
3883 if ((err = get_primitive_type(ag->codegen, variable_name, &primitive_type))) {3904 if ((err = get_primitive_type(ag->codegen, variable_name, &primitive_type))) {
3884 if (err == ErrorOverflow) {3905 if (err == ErrorOverflow) {
src/stage1/parser.cpp-4
...@@ -1617,11 +1617,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {...@@ -1617,11 +1617,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
1617// / INTEGER1617// / INTEGER
1618// / KEYWORD_comptime TypeExpr1618// / KEYWORD_comptime TypeExpr
1619// / KEYWORD_error DOT IDENTIFIER1619// / KEYWORD_error DOT IDENTIFIER
1620// / KEYWORD_false
1621// / KEYWORD_null
1622// / KEYWORD_promise1620// / KEYWORD_promise
1623// / KEYWORD_true
1624// / KEYWORD_undefined
1625// / KEYWORD_unreachable1621// / KEYWORD_unreachable
1626// / STRINGLITERAL1622// / STRINGLITERAL
1627// / SwitchExpr1623// / SwitchExpr
src/translate_c/ast.zig+1-1
...@@ -827,7 +827,7 @@ const Context = struct {...@@ -827,7 +827,7 @@ const Context = struct {
827 }827 }
828828
829 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {829 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
830 if (@import("../AstGen.zig").isPrimitive(bytes))830 if (std.zig.primitives.isPrimitive(bytes))
831 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});831 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
832 return c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(bytes)});832 return c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(bytes)});
833 }833 }