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;
1111pub const parse = @import("zig/parse.zig").parse;
1212pub const string_literal = @import("zig/string_literal.zig");
1313pub const number_literal = @import("zig/number_literal.zig");
14pub const primitives = @import("zig/primitives.zig");
1415pub const Ast = @import("zig/Ast.zig");
1516pub const system = @import("zig/system.zig");
1617pub const CrossTarget = @import("zig/CrossTarget.zig");
lib/std/zig/parser_test.zig+376
......@@ -5198,6 +5198,382 @@ test "zig fmt: while continue expr" {
51985198 });
51995199}
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
52015577test "zig fmt: error for missing sentinel value in sentinel slice" {
52025578 try testError(
52035579 \\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;
55const meta = std.meta;
66const Ast = std.zig.Ast;
77const Token = std.zig.Token;
8const primitives = std.zig.primitives;
89
910const indent_delta = 4;
1011const asm_indent_delta = 2;
......@@ -152,8 +153,10 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac
152153 const test_token = main_tokens[decl];
153154 try renderToken(ais, tree, test_token, .space);
154155 const test_name_tag = token_tags[test_token + 1];
155 if (test_name_tag == .string_literal or test_name_tag == .identifier) {
156 try renderToken(ais, tree, test_token + 1, .space);
156 switch (test_name_tag) {
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 => {},
157160 }
158161 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
159162 },
......@@ -192,11 +195,10 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
192195 const lexeme = tokenSliceForRender(tree, token_index);
193196 if (mem.eql(u8, lexeme, "c_void")) {
194197 try ais.writer().writeAll("anyopaque");
198 return renderSpace(ais, tree, token_index, lexeme.len, space);
195199 } else {
196 try ais.writer().writeAll(lexeme);
200 return renderIdentifier(ais, tree, token_index, space, .preserve_when_shadowing);
197201 }
198
199 return renderSpace(ais, tree, token_index, lexeme.len, space);
200202 },
201203
202204 .number_literal,
......@@ -226,7 +228,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
226228 .error_value => {
227229 try renderToken(ais, tree, main_tokens[node], .none);
228230 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);
230232 },
231233
232234 .block_two,
......@@ -256,7 +258,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
256258 try renderToken(ais, tree, defer_token, .space);
257259 if (payload_token != 0) {
258260 try renderToken(ais, tree, payload_token - 1, .none); // |
259 try renderToken(ais, tree, payload_token, .none); // identifier
261 try renderIdentifier(ais, tree, payload_token, .none, .preserve_when_shadowing); // identifier
260262 try renderToken(ais, tree, payload_token + 1, .space); // |
261263 }
262264 return renderExpression(gpa, ais, tree, expr, space);
......@@ -294,7 +296,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
294296 if (token_tags[fallback_first - 1] == .pipe) {
295297 try renderToken(ais, tree, main_token, .space); // catch keyword
296298 try renderToken(ais, tree, main_token + 1, .none); // pipe
297 try renderToken(ais, tree, main_token + 2, .none); // payload identifier
299 try renderIdentifier(ais, tree, main_token + 2, .none, .preserve_when_shadowing); // payload identifier
298300 try renderToken(ais, tree, main_token + 3, after_op_space); // pipe
299301 } else {
300302 assert(token_tags[fallback_first - 1] == .keyword_catch);
......@@ -320,7 +322,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
320322 ais.pushIndentOneShot();
321323 }
322324
323 try renderToken(ais, tree, main_token, .none);
325 try renderToken(ais, tree, main_token, .none); // .
324326
325327 // This check ensures that zag() is indented in the following example:
326328 // const x = foo
......@@ -331,7 +333,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
331333 ais.pushIndentOneShot();
332334 }
333335
334 return renderToken(ais, tree, field_access.rhs, space);
336 return renderIdentifier(ais, tree, field_access.rhs, space, .eagerly_unquote); // field
335337 },
336338
337339 .error_union,
......@@ -514,11 +516,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
514516 } else if (label_token != 0 and target == 0) {
515517 try renderToken(ais, tree, main_token, .space); // break keyword
516518 try renderToken(ais, tree, label_token - 1, .none); // colon
517 try renderToken(ais, tree, label_token, space); // identifier
519 try renderIdentifier(ais, tree, label_token, space, .eagerly_unquote); // identifier
518520 } else if (label_token != 0 and target != 0) {
519521 try renderToken(ais, tree, main_token, .space); // break keyword
520522 try renderToken(ais, tree, label_token - 1, .none); // colon
521 try renderToken(ais, tree, label_token, .space); // identifier
523 try renderIdentifier(ais, tree, label_token, .space, .eagerly_unquote); // identifier
522524 try renderExpression(gpa, ais, tree, target, space);
523525 }
524526 },
......@@ -529,7 +531,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
529531 if (label != 0) {
530532 try renderToken(ais, tree, main_token, .space); // continue
531533 try renderToken(ais, tree, label - 1, .none); // :
532 return renderToken(ais, tree, label, space); // label
534 return renderIdentifier(ais, tree, label, space, .eagerly_unquote); // label
533535 } else {
534536 return renderToken(ais, tree, main_token, space); // continue
535537 }
......@@ -590,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
590592 // There is exactly one member and no trailing comma or
591593 // comments, so render without surrounding spaces: `error{Foo}`
592594 try renderToken(ais, tree, lbrace, .none);
593 try renderToken(ais, tree, lbrace + 1, .none); // identifier
595 try renderIdentifier(ais, tree, lbrace + 1, .none, .eagerly_unquote); // identifier
594596 return renderToken(ais, tree, rbrace, space);
595597 } else if (token_tags[rbrace - 1] == .comma) {
596598 // 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,
601603 if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);
602604 switch (token_tags[i]) {
603605 .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),
605607 .comma => {},
606608 else => unreachable,
607609 }
......@@ -615,7 +617,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
615617 while (i < rbrace) : (i += 1) {
616618 switch (token_tags[i]) {
617619 .doc_comment => unreachable, // TODO
618 .identifier => try renderToken(ais, tree, i, .comma_space),
620 .identifier => try renderIdentifier(ais, tree, i, .comma_space, .eagerly_unquote),
619621 .comma => {},
620622 else => unreachable,
621623 }
......@@ -702,7 +704,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
702704
703705 .enum_literal => {
704706 try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
705 return renderToken(ais, tree, main_tokens[node], space); // name
707 return renderIdentifier(ais, tree, main_tokens[node], space, .eagerly_unquote); // name
706708 },
707709
708710 .fn_decl => unreachable,
......@@ -887,7 +889,7 @@ fn renderAsmOutput(
887889 const symbolic_name = main_tokens[asm_output];
888890
889891 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
890 try renderToken(ais, tree, symbolic_name, .none); // ident
892 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident
891893 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
892894 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
893895 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
......@@ -897,7 +899,7 @@ fn renderAsmOutput(
897899 try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none);
898900 return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen
899901 } else {
900 try renderToken(ais, tree, symbolic_name + 4, .none); // ident
902 try renderIdentifier(ais, tree, symbolic_name + 4, .none, .eagerly_unquote); // ident
901903 return renderToken(ais, tree, symbolic_name + 5, space); // rparen
902904 }
903905}
......@@ -916,7 +918,7 @@ fn renderAsmInput(
916918 const symbolic_name = main_tokens[asm_input];
917919
918920 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
919 try renderToken(ais, tree, symbolic_name, .none); // ident
921 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident
920922 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
921923 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
922924 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
955957 Space.space
956958 else
957959 Space.none;
958 try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name
960 try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
959961
960962 if (var_decl.ast.type_node != 0) {
961963 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,
10551057 const token_tags = tree.tokens.items(.tag);
10561058
10571059 if (while_node.label_token) |label| {
1058 try renderToken(ais, tree, label, .none); // label
1060 try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label
10591061 try renderToken(ais, tree, label + 1, .space); // :
10601062 }
10611063
......@@ -1080,11 +1082,11 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
10801082 break :blk payload_token;
10811083 }
10821084 };
1083 try renderToken(ais, tree, ident, .none); // identifier
1085 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
10841086 const pipe = blk: {
10851087 if (token_tags[ident + 1] == .comma) {
10861088 try renderToken(ais, tree, ident + 1, .space); // ,
1087 try renderToken(ais, tree, ident + 2, .none); // index
1089 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // index
10881090 break :blk ident + 3;
10891091 } else {
10901092 break :blk ident + 1;
......@@ -1127,7 +1129,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
11271129 if (while_node.error_token) |error_token| {
11281130 try renderToken(ais, tree, while_node.else_token, .space); // else
11291131 try renderToken(ais, tree, error_token - 1, .none); // |
1130 try renderToken(ais, tree, error_token, .none); // identifier
1132 try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier
11311133 last_else_token = error_token + 1; // |
11321134 }
11331135
......@@ -1163,10 +1165,10 @@ fn renderContainerField(
11631165 try renderToken(ais, tree, t, .space); // comptime
11641166 }
11651167 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1166 return renderTokenComma(ais, tree, field.ast.name_token, space); // name
1168 return renderIdentifierComma(ais, tree, field.ast.name_token, space, .eagerly_unquote); // name
11671169 }
11681170 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1169 try renderToken(ais, tree, field.ast.name_token, .none); // name
1171 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name
11701172 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
11711173
11721174 if (field.ast.align_expr != 0) {
......@@ -1182,12 +1184,12 @@ fn renderContainerField(
11821184 }
11831185 }
11841186 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1185 try renderToken(ais, tree, field.ast.name_token, .space); // name
1187 try renderIdentifier(ais, tree, field.ast.name_token, .space, .eagerly_unquote); // name
11861188 try renderToken(ais, tree, field.ast.name_token + 1, .space); // =
11871189 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
11881190 }
11891191
1190 try renderToken(ais, tree, field.ast.name_token, .none); // name
1192 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name
11911193 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
11921194 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
12941296 const after_fn_token = fn_proto.ast.fn_token + 1;
12951297 const lparen = if (token_tags[after_fn_token] == .identifier) blk: {
12961298 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
1297 try renderToken(ais, tree, after_fn_token, .none); // name
1299 try renderIdentifier(ais, tree, after_fn_token, .none, .preserve_when_shadowing); // name
12981300 break :blk after_fn_token + 1;
12991301 } else blk: {
13001302 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
13831385 if (token_tags[last_param_token] == .identifier and
13841386 token_tags[last_param_token + 1] == .colon)
13851387 {
1386 try renderToken(ais, tree, last_param_token, .none); // name
1388 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
13871389 last_param_token += 1;
13881390 try renderToken(ais, tree, last_param_token, .space); // :
13891391 last_param_token += 1;
......@@ -1432,7 +1434,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
14321434 if (token_tags[last_param_token] == .identifier and
14331435 token_tags[last_param_token + 1] == .colon)
14341436 {
1435 try renderToken(ais, tree, last_param_token, .none); // name
1437 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
14361438 last_param_token += 1;
14371439 try renderToken(ais, tree, last_param_token, .space); // :
14381440 last_param_token += 1;
......@@ -1545,7 +1547,7 @@ fn renderSwitchCase(
15451547 else
15461548 Space.space;
15471549 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
15501552 if (switch_case.payload_token) |payload_token| {
15511553 try renderToken(ais, tree, payload_token - 1, .none); // pipe
......@@ -1553,10 +1555,10 @@ fn renderSwitchCase(
15531555 if (token_tags[payload_token] == .asterisk) {
15541556 try renderToken(ais, tree, payload_token, .none); // asterisk
15551557 }
1556 try renderToken(ais, tree, ident, .none); // identifier
1558 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
15571559 if (token_tags[ident + 1] == .comma) {
15581560 try renderToken(ais, tree, ident + 1, .space); // ,
1559 try renderToken(ais, tree, ident + 2, .none); // identifier
1561 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // identifier
15601562 try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
15611563 } else {
15621564 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
......@@ -1581,8 +1583,8 @@ fn renderBlock(
15811583 if (token_tags[lbrace - 1] == .colon and
15821584 token_tags[lbrace - 2] == .identifier)
15831585 {
1584 try renderToken(ais, tree, lbrace - 2, .none);
1585 try renderToken(ais, tree, lbrace - 1, .space);
1586 try renderIdentifier(ais, tree, lbrace - 2, .none, .eagerly_unquote); // identifier
1587 try renderToken(ais, tree, lbrace - 1, .space); // :
15861588 }
15871589
15881590 ais.pushIndentNextLine();
......@@ -1635,7 +1637,7 @@ fn renderStructInit(
16351637 try renderToken(ais, tree, struct_init.ast.lbrace, .newline);
16361638
16371639 try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // .
1638 try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name
1640 try renderIdentifier(ais, tree, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
16391641 try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // =
16401642 try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);
16411643
......@@ -1643,7 +1645,7 @@ fn renderStructInit(
16431645 const init_token = tree.firstToken(field_init);
16441646 try renderExtraNewlineToken(ais, tree, init_token - 3);
16451647 try renderToken(ais, tree, init_token - 3, .none); // .
1646 try renderToken(ais, tree, init_token - 2, .space); // name
1648 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
16471649 try renderToken(ais, tree, init_token - 1, .space); // =
16481650 try renderExpression(gpa, ais, tree, field_init, .comma);
16491651 }
......@@ -1656,7 +1658,7 @@ fn renderStructInit(
16561658 for (struct_init.ast.fields) |field_init| {
16571659 const init_token = tree.firstToken(field_init);
16581660 try renderToken(ais, tree, init_token - 3, .none); // .
1659 try renderToken(ais, tree, init_token - 2, .space); // name
1661 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
16601662 try renderToken(ais, tree, init_token - 1, .space); // =
16611663 try renderExpression(gpa, ais, tree, field_init, .comma_space);
16621664 }
......@@ -2310,6 +2312,19 @@ fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) E
23102312 }
23112313}
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
23132328const Space = enum {
23142329 /// Output the token lexeme only.
23152330 none,
......@@ -2377,6 +2392,158 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
23772392 }
23782393}
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
23802547/// Returns true if there exists a line comment between any of the tokens from
23812548/// `start_token` to `end_token`. This is used to determine if e.g. a
23822549/// 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 {
6363
6464/// Parse an escape sequence from `slice[offset..]`. If parsing is successful,
6565/// 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 {
6767 assert(slice.len > offset.*);
6868 assert(slice[offset.*] == '\\');
6969
......@@ -274,12 +274,50 @@ pub fn parseAlloc(allocator: std.mem.Allocator, bytes: []const u8) ParseError![]
274274 var buf = std.ArrayList(u8).init(allocator);
275275 defer buf.deinit();
276276
277 switch (try parseAppend(&buf, bytes)) {
277 switch (try parseWrite(buf.writer(), bytes)) {
278278 .success => return buf.toOwnedSlice(),
279279 .failure => return error.InvalidLiteral,
280280 }
281281}
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
283321test "parse" {
284322 const expect = std.testing.expect;
285323 const expectError = std.testing.expectError;
src/AstGen.zig+22-44
......@@ -10,6 +10,8 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged;
1010const StringIndexAdapter = std.hash_map.StringIndexAdapter;
1111const StringIndexContext = std.hash_map.StringIndexContext;
1212
13const isPrimitive = std.zig.primitives.isPrimitive;
14
1315const Zir = @import("Zir.zig");
1416const refToIndex = Zir.refToIndex;
1517const indexToRef = Zir.indexToRef;
......@@ -4237,33 +4239,7 @@ fn testDecl(
42374239
42384240 // if not @"" syntax, just use raw token slice
42394241 if (ident_name_raw[0] != '@') {
4240 if (primitives.get(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 }
4242 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
42674243 }
42684244
42694245 // Local variables, including function parameters.
......@@ -7108,7 +7084,7 @@ fn identifier(
71087084
71097085 // if not @"" syntax, just use raw token slice
71107086 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| {
71127088 return rvalue(gz, ri, zir_const_ref, ident);
71137089 }
71147090
......@@ -8751,7 +8727,7 @@ fn calleeExpr(
87518727 }
87528728}
87538729
8754const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{
8730const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{
87558731 .{ "anyerror", .anyerror_type },
87568732 .{ "anyframe", .anyframe_type },
87578733 .{ "anyopaque", .anyopaque_type },
......@@ -8795,6 +8771,21 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{
87958771 .{ "void", .void_type },
87968772});
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
87988789fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool {
87998790 const node_tags = tree.nodes.items(.tag);
88008791 const node_datas = tree.nodes.items(.data);
......@@ -9458,7 +9449,7 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
94589449 .identifier => {
94599450 const main_tokens = tree.nodes.items(.main_token);
94609451 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) {
94629453 .anyerror_type,
94639454 .anyframe_type,
94649455 .anyopaque_type,
......@@ -9702,7 +9693,7 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
97029693 .identifier => {
97039694 const main_tokens = tree.nodes.items(.main_token);
97049695 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) {
97069697 .anyerror_type,
97079698 .anyframe_type,
97089699 .anyopaque_type,
......@@ -12045,19 +12036,6 @@ fn nullTerminatedString(astgen: AstGen, index: usize) [*:0]const u8 {
1204512036 return @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + index;
1204612037}
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
1206112039/// Local variables shadowing detection, including function parameters.
1206212040fn detectLocalShadowing(
1206312041 astgen: *AstGen,
src/stage1/astgen.cpp+21
......@@ -3879,6 +3879,27 @@ static Stage1ZirInst *astgen_identifier(Stage1AstGen *ag, Scope *scope, AstNode
38793879 }
38803880 }
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
38823903 ZigType *primitive_type;
38833904 if ((err = get_primitive_type(ag->codegen, variable_name, &primitive_type))) {
38843905 if (err == ErrorOverflow) {
src/stage1/parser.cpp-4
......@@ -1617,11 +1617,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
16171617// / INTEGER
16181618// / KEYWORD_comptime TypeExpr
16191619// / KEYWORD_error DOT IDENTIFIER
1620// / KEYWORD_false
1621// / KEYWORD_null
16221620// / KEYWORD_promise
1623// / KEYWORD_true
1624// / KEYWORD_undefined
16251621// / KEYWORD_unreachable
16261622// / STRINGLITERAL
16271623// / SwitchExpr
src/translate_c/ast.zig+1-1
......@@ -827,7 +827,7 @@ const Context = struct {
827827 }
828828
829829 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
830 if (@import("../AstGen.zig").isPrimitive(bytes))
830 if (std.zig.primitives.isPrimitive(bytes))
831831 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
832832 return c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(bytes)});
833833 }