authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-28 17:50:10+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-29 20:39:11+01:00
log401910a2ca8e4b9689180c63d0b8e190adcf7cfa
tree681fb4f8bdfe0fe3fd2eeedfd9c25f96b91ab844
parente9a00ba7f4ef2546cd0c98559002431c749374fe
signaturelock-open Commit is signed but in an unrecognized format.

AstGen: disallow fields and decls from sharing names

This is a mini-proposal which is accepted as part of #9938. This compiler and standard library need some changes to obey this rule.

1 files changed, 152 insertions(+), 196 deletions(-)

lib/std/zig/AstGen.zig+152-196
...@@ -4053,7 +4053,7 @@ fn fnDecl(...@@ -4053,7 +4053,7 @@ fn fnDecl(
4053 // The source slice is added towards the *end* of this function.4053 // The source slice is added towards the *end* of this function.
4054 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));4054 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
40554055
4056 // missing function name already happened in scanDecls()4056 // missing function name already happened in scanContainer()
4057 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;4057 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
40584058
4059 // We insert this at the beginning so that its instruction index marks the4059 // We insert this at the beginning so that its instruction index marks the
...@@ -5019,7 +5019,7 @@ fn structDeclInner(...@@ -5019,7 +5019,7 @@ fn structDeclInner(
5019 }5019 }
5020 };5020 };
50215021
5022 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);5022 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct");
5023 const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count);5023 const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count);
50245024
5025 const bits_per_field = 4;5025 const bits_per_field = 4;
...@@ -5088,15 +5088,6 @@ fn structDeclInner(...@@ -5088,15 +5088,6 @@ fn structDeclInner(
5088 astgen.src_hasher.update(tree.getNodeSource(backing_int_node));5088 astgen.src_hasher.update(tree.getNodeSource(backing_int_node));
5089 }5089 }
50905090
5091 var sfba = std.heap.stackFallback(256, astgen.arena);
5092 const sfba_allocator = sfba.get();
5093
5094 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
5095 try duplicate_names.ensureTotalCapacity(field_count);
5096
5097 // When there aren't errors, use this to avoid a second iteration.
5098 var any_duplicate = false;
5099
5100 var known_non_opv = false;5091 var known_non_opv = false;
5101 var known_comptime_only = false;5092 var known_comptime_only = false;
5102 var any_comptime_fields = false;5093 var any_comptime_fields = false;
...@@ -5117,16 +5108,6 @@ fn structDeclInner(...@@ -5117,16 +5108,6 @@ fn structDeclInner(
5117 assert(!member.ast.tuple_like);5108 assert(!member.ast.tuple_like);
51185109
5119 wip_members.appendToField(@intFromEnum(field_name));5110 wip_members.appendToField(@intFromEnum(field_name));
5120
5121 const gop = try duplicate_names.getOrPut(field_name);
5122
5123 if (gop.found_existing) {
5124 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5125 any_duplicate = true;
5126 } else {
5127 gop.value_ptr.* = .{};
5128 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5129 }
5130 } else if (!member.ast.tuple_like) {5111 } else if (!member.ast.tuple_like) {
5131 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});5112 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
5132 }5113 }
...@@ -5211,32 +5192,6 @@ fn structDeclInner(...@@ -5211,32 +5192,6 @@ fn structDeclInner(
5211 }5192 }
5212 }5193 }
52135194
5214 if (any_duplicate) {
5215 var it = duplicate_names.iterator();
5216
5217 while (it.next()) |entry| {
5218 const record = entry.value_ptr.*;
5219 if (record.items.len > 1) {
5220 var error_notes = std.ArrayList(u32).init(astgen.arena);
5221
5222 for (record.items[1..]) |duplicate| {
5223 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5224 }
5225
5226 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
5227
5228 try astgen.appendErrorTokNotes(
5229 record.items[0],
5230 "duplicate struct field name",
5231 .{},
5232 error_notes.items,
5233 );
5234 }
5235 }
5236
5237 return error.AnalysisFail;
5238 }
5239
5240 var fields_hash: std.zig.SrcHash = undefined;5195 var fields_hash: std.zig.SrcHash = undefined;
5241 astgen.src_hasher.final(&fields_hash);5196 astgen.src_hasher.final(&fields_hash);
52425197
...@@ -5317,7 +5272,7 @@ fn unionDeclInner(...@@ -5317,7 +5272,7 @@ fn unionDeclInner(
5317 };5272 };
5318 defer block_scope.unstack();5273 defer block_scope.unstack();
53195274
5320 const decl_count = try astgen.scanDecls(&namespace, members);5275 const decl_count = try astgen.scanContainer(&namespace, members, .@"union");
5321 const field_count: u32 = @intCast(members.len - decl_count);5276 const field_count: u32 = @intCast(members.len - decl_count);
53225277
5323 if (layout != .auto and (auto_enum_tok != null or arg_node != 0)) {5278 if (layout != .auto and (auto_enum_tok != null or arg_node != 0)) {
...@@ -5348,15 +5303,6 @@ fn unionDeclInner(...@@ -5348,15 +5303,6 @@ fn unionDeclInner(
5348 astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node));5303 astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node));
5349 }5304 }
53505305
5351 var sfba = std.heap.stackFallback(256, astgen.arena);
5352 const sfba_allocator = sfba.get();
5353
5354 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
5355 try duplicate_names.ensureTotalCapacity(field_count);
5356
5357 // When there aren't errors, use this to avoid a second iteration.
5358 var any_duplicate = false;
5359
5360 for (members) |member_node| {5306 for (members) |member_node| {
5361 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {5307 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
5362 .decl => continue,5308 .decl => continue,
...@@ -5374,16 +5320,6 @@ fn unionDeclInner(...@@ -5374,16 +5320,6 @@ fn unionDeclInner(
5374 const field_name = try astgen.identAsString(member.ast.main_token);5320 const field_name = try astgen.identAsString(member.ast.main_token);
5375 wip_members.appendToField(@intFromEnum(field_name));5321 wip_members.appendToField(@intFromEnum(field_name));
53765322
5377 const gop = try duplicate_names.getOrPut(field_name);
5378
5379 if (gop.found_existing) {
5380 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5381 any_duplicate = true;
5382 } else {
5383 gop.value_ptr.* = .{};
5384 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5385 }
5386
5387 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());5323 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
5388 wip_members.appendToField(@intFromEnum(doc_comment_index));5324 wip_members.appendToField(@intFromEnum(doc_comment_index));
53895325
...@@ -5438,32 +5374,6 @@ fn unionDeclInner(...@@ -5438,32 +5374,6 @@ fn unionDeclInner(
5438 }5374 }
5439 }5375 }
54405376
5441 if (any_duplicate) {
5442 var it = duplicate_names.iterator();
5443
5444 while (it.next()) |entry| {
5445 const record = entry.value_ptr.*;
5446 if (record.items.len > 1) {
5447 var error_notes = std.ArrayList(u32).init(astgen.arena);
5448
5449 for (record.items[1..]) |duplicate| {
5450 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5451 }
5452
5453 try error_notes.append(try astgen.errNoteNode(node, "union declared here", .{}));
5454
5455 try astgen.appendErrorTokNotes(
5456 record.items[0],
5457 "duplicate union field name",
5458 .{},
5459 error_notes.items,
5460 );
5461 }
5462 }
5463
5464 return error.AnalysisFail;
5465 }
5466
5467 var fields_hash: std.zig.SrcHash = undefined;5377 var fields_hash: std.zig.SrcHash = undefined;
5468 astgen.src_hasher.final(&fields_hash);5378 astgen.src_hasher.final(&fields_hash);
54695379
...@@ -5666,7 +5576,7 @@ fn containerDecl(...@@ -5666,7 +5576,7 @@ fn containerDecl(
5666 };5576 };
5667 defer block_scope.unstack();5577 defer block_scope.unstack();
56685578
5669 _ = try astgen.scanDecls(&namespace, container_decl.ast.members);5579 _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum");
5670 namespace.base.tag = .namespace;5580 namespace.base.tag = .namespace;
56715581
5672 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)5582 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
...@@ -5687,15 +5597,6 @@ fn containerDecl(...@@ -5687,15 +5597,6 @@ fn containerDecl(
5687 }5597 }
5688 astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)});5598 astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)});
56895599
5690 var sfba = std.heap.stackFallback(256, astgen.arena);
5691 const sfba_allocator = sfba.get();
5692
5693 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
5694 try duplicate_names.ensureTotalCapacity(counts.total_fields);
5695
5696 // When there aren't errors, use this to avoid a second iteration.
5697 var any_duplicate = false;
5698
5699 for (container_decl.ast.members) |member_node| {5600 for (container_decl.ast.members) |member_node| {
5700 if (member_node == counts.nonexhaustive_node)5601 if (member_node == counts.nonexhaustive_node)
5701 continue;5602 continue;
...@@ -5712,16 +5613,6 @@ fn containerDecl(...@@ -5712,16 +5613,6 @@ fn containerDecl(
5712 const field_name = try astgen.identAsString(member.ast.main_token);5613 const field_name = try astgen.identAsString(member.ast.main_token);
5713 wip_members.appendToField(@intFromEnum(field_name));5614 wip_members.appendToField(@intFromEnum(field_name));
57145615
5715 const gop = try duplicate_names.getOrPut(field_name);
5716
5717 if (gop.found_existing) {
5718 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5719 any_duplicate = true;
5720 } else {
5721 gop.value_ptr.* = .{};
5722 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5723 }
5724
5725 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());5616 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
5726 wip_members.appendToField(@intFromEnum(doc_comment_index));5617 wip_members.appendToField(@intFromEnum(doc_comment_index));
57275618
...@@ -5748,32 +5639,6 @@ fn containerDecl(...@@ -5748,32 +5639,6 @@ fn containerDecl(
5748 }5639 }
5749 }5640 }
57505641
5751 if (any_duplicate) {
5752 var it = duplicate_names.iterator();
5753
5754 while (it.next()) |entry| {
5755 const record = entry.value_ptr.*;
5756 if (record.items.len > 1) {
5757 var error_notes = std.ArrayList(u32).init(astgen.arena);
5758
5759 for (record.items[1..]) |duplicate| {
5760 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5761 }
5762
5763 try error_notes.append(try astgen.errNoteNode(node, "enum declared here", .{}));
5764
5765 try astgen.appendErrorTokNotes(
5766 record.items[0],
5767 "duplicate enum field name",
5768 .{},
5769 error_notes.items,
5770 );
5771 }
5772 }
5773
5774 return error.AnalysisFail;
5775 }
5776
5777 if (!block_scope.isEmpty()) {5642 if (!block_scope.isEmpty()) {
5778 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);5643 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
5779 }5644 }
...@@ -5833,7 +5698,7 @@ fn containerDecl(...@@ -5833,7 +5698,7 @@ fn containerDecl(
5833 };5698 };
5834 defer block_scope.unstack();5699 defer block_scope.unstack();
58355700
5836 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);5701 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque");
58375702
5838 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);5703 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
5839 defer wip_members.deinit();5704 defer wip_members.deinit();
...@@ -13594,31 +13459,67 @@ fn advanceSourceCursor(astgen: *AstGen, end: usize) void {...@@ -13594,31 +13459,67 @@ fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
13594 astgen.source_column = column;13459 astgen.source_column = column;
13595}13460}
1359613461
13597fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !u32 {13462/// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations.
13463/// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls).
13464fn scanContainer(
13465 astgen: *AstGen,
13466 namespace: *Scope.Namespace,
13467 members: []const Ast.Node.Index,
13468 container_kind: enum { @"struct", @"union", @"enum", @"opaque" },
13469) !u32 {
13598 const gpa = astgen.gpa;13470 const gpa = astgen.gpa;
13599 const tree = astgen.tree;13471 const tree = astgen.tree;
13600 const node_tags = tree.nodes.items(.tag);13472 const node_tags = tree.nodes.items(.tag);
13601 const main_tokens = tree.nodes.items(.main_token);13473 const main_tokens = tree.nodes.items(.main_token);
13602 const token_tags = tree.tokens.items(.tag);13474 const token_tags = tree.tokens.items(.tag);
1360313475
13604 // We don't have shadowing for test names, so we just track those for duplicate reporting locally.13476 // This type forms a linked list of source tokens declaring the same name.
13605 var named_tests: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{};13477 const NameEntry = struct {
13606 var decltests: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{};13478 tok: Ast.TokenIndex,
13479 /// Using a linked list here simplifies memory management, and is acceptable since
13480 ///ewntries are only allocated in error situations. The entries are allocated into the
13481 /// AstGen arena.
13482 next: ?*@This(),
13483 };
13484
13485 // The maps below are allocated into this SFBA to avoid using the GPA for small namespaces.
13486 var sfba_state = std.heap.stackFallback(512, astgen.gpa);
13487 const sfba = sfba_state.get();
13488
13489 var names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .{};
13490 var test_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .{};
13491 var decltest_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .{};
13607 defer {13492 defer {
13608 named_tests.deinit(gpa);13493 names.deinit(sfba);
13609 decltests.deinit(gpa);13494 test_names.deinit(sfba);
13495 decltest_names.deinit(sfba);
13610 }13496 }
1361113497
13498 var any_duplicates = false;
13612 var decl_count: u32 = 0;13499 var decl_count: u32 = 0;
13613 for (members) |member_node| {13500 for (members) |member_node| {
13614 const name_token = switch (node_tags[member_node]) {13501 const Kind = enum { decl, field };
13502 const kind: Kind, const name_token = switch (node_tags[member_node]) {
13503 .container_field_init,
13504 .container_field_align,
13505 .container_field,
13506 => blk: {
13507 var full = tree.fullContainerField(member_node).?;
13508 switch (container_kind) {
13509 .@"struct", .@"opaque" => {},
13510 .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree.nodes),
13511 }
13512 if (full.ast.tuple_like) continue;
13513 break :blk .{ .field, full.ast.main_token };
13514 },
13515
13615 .global_var_decl,13516 .global_var_decl,
13616 .local_var_decl,13517 .local_var_decl,
13617 .simple_var_decl,13518 .simple_var_decl,
13618 .aligned_var_decl,13519 .aligned_var_decl,
13619 => blk: {13520 => blk: {
13620 decl_count += 1;13521 decl_count += 1;
13621 break :blk main_tokens[member_node] + 1;13522 break :blk .{ .decl, main_tokens[member_node] + 1 };
13622 },13523 },
1362313524
13624 .fn_proto_simple,13525 .fn_proto_simple,
...@@ -13630,12 +13531,10 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast....@@ -13630,12 +13531,10 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
13630 decl_count += 1;13531 decl_count += 1;
13631 const ident = main_tokens[member_node] + 1;13532 const ident = main_tokens[member_node] + 1;
13632 if (token_tags[ident] != .identifier) {13533 if (token_tags[ident] != .identifier) {
13633 switch (astgen.failNode(member_node, "missing function name", .{})) {13534 try astgen.appendErrorNode(member_node, "missing function name", .{});
13634 error.AnalysisFail => continue,13535 continue;
13635 error.OutOfMemory => return error.OutOfMemory,
13636 }
13637 }13536 }
13638 break :blk ident;13537 break :blk .{ .decl, ident };
13639 },13538 },
1364013539
13641 .@"comptime", .@"usingnamespace" => {13540 .@"comptime", .@"usingnamespace" => {
...@@ -13648,70 +13547,87 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast....@@ -13648,70 +13547,87 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
13648 // We don't want shadowing detection here, and test names work a bit differently, so13547 // We don't want shadowing detection here, and test names work a bit differently, so
13649 // we must do the redeclaration detection ourselves.13548 // we must do the redeclaration detection ourselves.
13650 const test_name_token = main_tokens[member_node] + 1;13549 const test_name_token = main_tokens[member_node] + 1;
13550 const new_ent: NameEntry = .{
13551 .tok = test_name_token,
13552 .next = null,
13553 };
13651 switch (token_tags[test_name_token]) {13554 switch (token_tags[test_name_token]) {
13652 else => {}, // unnamed test13555 else => {}, // unnamed test
13653 .string_literal => {13556 .string_literal => {
13654 const name = try astgen.strLitAsString(test_name_token);13557 const name = try astgen.strLitAsString(test_name_token);
13655 const gop = try named_tests.getOrPut(gpa, name.index);13558 const gop = try test_names.getOrPut(sfba, name.index);
13656 if (gop.found_existing) {13559 if (gop.found_existing) {
13657 const name_slice = astgen.string_bytes.items[@intFromEnum(name.index)..][0..name.len];13560 var e = gop.value_ptr;
13658 const name_duped = try gpa.dupe(u8, name_slice);13561 while (e.next) |n| e = n;
13659 defer gpa.free(name_duped);13562 e.next = try astgen.arena.create(NameEntry);
13660 try astgen.appendErrorNodeNotes(member_node, "duplicate test name '{s}'", .{name_duped}, &.{13563 e.next.?.* = new_ent;
13661 try astgen.errNoteNode(gop.value_ptr.*, "other test here", .{}),13564 any_duplicates = true;
13662 });
13663 } else {13565 } else {
13664 gop.value_ptr.* = member_node;13566 gop.value_ptr.* = new_ent;
13665 }13567 }
13666 },13568 },
13667 .identifier => {13569 .identifier => {
13668 const name = try astgen.identAsString(test_name_token);13570 const name = try astgen.identAsString(test_name_token);
13669 const gop = try decltests.getOrPut(gpa, name);13571 const gop = try decltest_names.getOrPut(sfba, name);
13670 if (gop.found_existing) {13572 if (gop.found_existing) {
13671 const name_slice = mem.span(astgen.nullTerminatedString(name));13573 var e = gop.value_ptr;
13672 const name_duped = try gpa.dupe(u8, name_slice);13574 while (e.next) |n| e = n;
13673 defer gpa.free(name_duped);13575 e.next = try astgen.arena.create(NameEntry);
13674 try astgen.appendErrorNodeNotes(member_node, "duplicate decltest '{s}'", .{name_duped}, &.{13576 e.next.?.* = new_ent;
13675 try astgen.errNoteNode(gop.value_ptr.*, "other decltest here", .{}),13577 any_duplicates = true;
13676 });
13677 } else {13578 } else {
13678 gop.value_ptr.* = member_node;13579 gop.value_ptr.* = new_ent;
13679 }13580 }
13680 },13581 },
13681 }13582 }
13682 continue;13583 continue;
13683 },13584 },
1368413585
13685 else => continue,13586 else => unreachable,
13686 };13587 };
1368713588
13589 const name_str_index = try astgen.identAsString(name_token);
13590
13591 if (kind == .decl) {
13592 // Put the name straight into `decls`, even if there are compile errors.
13593 // This avoids incorrect "undeclared identifier" errors later on.
13594 try namespace.decls.put(gpa, name_str_index, member_node);
13595 }
13596
13597 {
13598 const gop = try names.getOrPut(sfba, name_str_index);
13599 const new_ent: NameEntry = .{
13600 .tok = name_token,
13601 .next = null,
13602 };
13603 if (gop.found_existing) {
13604 var e = gop.value_ptr;
13605 while (e.next) |n| e = n;
13606 e.next = try astgen.arena.create(NameEntry);
13607 e.next.?.* = new_ent;
13608 any_duplicates = true;
13609 continue;
13610 } else {
13611 gop.value_ptr.* = new_ent;
13612 }
13613 }
13614
13615 // For fields, we only needed the duplicate check! Decls have some more checks to do, though.
13616 switch (kind) {
13617 .decl => {},
13618 .field => continue,
13619 }
13620
13688 const token_bytes = astgen.tree.tokenSlice(name_token);13621 const token_bytes = astgen.tree.tokenSlice(name_token);
13689 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {13622 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
13690 switch (astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{13623 try astgen.appendErrorTokNotes(name_token, "name shadows primitive '{s}'", .{
13691 token_bytes,13624 token_bytes,
13692 }, &[_]u32{13625 }, &.{
13693 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{13626 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
13694 token_bytes,13627 token_bytes,
13695 }),13628 }),
13696 })) {13629 });
13697 error.AnalysisFail => continue,13630 continue;
13698 error.OutOfMemory => return error.OutOfMemory,
13699 }
13700 }
13701
13702 const name_str_index = try astgen.identAsString(name_token);
13703 const gop = try namespace.decls.getOrPut(gpa, name_str_index);
13704 if (gop.found_existing) {
13705 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(name_str_index)));
13706 defer gpa.free(name);
13707 switch (astgen.failNodeNotes(member_node, "redeclaration of '{s}'", .{
13708 name,
13709 }, &[_]u32{
13710 try astgen.errNoteNode(gop.value_ptr.*, "other declaration here", .{}),
13711 })) {
13712 error.AnalysisFail => continue,
13713 error.OutOfMemory => return error.OutOfMemory,
13714 }
13715 }13631 }
1371613632
13717 var s = namespace.parent;13633 var s = namespace.parent;
...@@ -13719,30 +13635,32 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast....@@ -13719,30 +13635,32 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
13719 .local_val => {13635 .local_val => {
13720 const local_val = s.cast(Scope.LocalVal).?;13636 const local_val = s.cast(Scope.LocalVal).?;
13721 if (local_val.name == name_str_index) {13637 if (local_val.name == name_str_index) {
13722 return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{13638 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13723 token_bytes, @tagName(local_val.id_cat),13639 token_bytes, @tagName(local_val.id_cat),
13724 }, &[_]u32{13640 }, &.{
13725 try astgen.errNoteTok(13641 try astgen.errNoteTok(
13726 local_val.token_src,13642 local_val.token_src,
13727 "previous declaration here",13643 "previous declaration here",
13728 .{},13644 .{},
13729 ),13645 ),
13730 });13646 });
13647 break;
13731 }13648 }
13732 s = local_val.parent;13649 s = local_val.parent;
13733 },13650 },
13734 .local_ptr => {13651 .local_ptr => {
13735 const local_ptr = s.cast(Scope.LocalPtr).?;13652 const local_ptr = s.cast(Scope.LocalPtr).?;
13736 if (local_ptr.name == name_str_index) {13653 if (local_ptr.name == name_str_index) {
13737 return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{13654 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13738 token_bytes, @tagName(local_ptr.id_cat),13655 token_bytes, @tagName(local_ptr.id_cat),
13739 }, &[_]u32{13656 }, &.{
13740 try astgen.errNoteTok(13657 try astgen.errNoteTok(
13741 local_ptr.token_src,13658 local_ptr.token_src,
13742 "previous declaration here",13659 "previous declaration here",
13743 .{},13660 .{},
13744 ),13661 ),
13745 });13662 });
13663 break;
13746 }13664 }
13747 s = local_ptr.parent;13665 s = local_ptr.parent;
13748 },13666 },
...@@ -13751,8 +13669,46 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast....@@ -13751,8 +13669,46 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
13751 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,13669 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
13752 .top => break,13670 .top => break,
13753 };13671 };
13754 gop.value_ptr.* = member_node;
13755 }13672 }
13673
13674 if (!any_duplicates) return decl_count;
13675
13676 for (names.keys(), names.values()) |name, first| {
13677 if (first.next == null) continue;
13678 var notes: std.ArrayListUnmanaged(u32) = .{};
13679 var prev: NameEntry = first;
13680 while (prev.next) |cur| : (prev = cur.*) {
13681 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate name here", .{}));
13682 }
13683 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
13684 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
13685 try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);
13686 }
13687
13688 for (test_names.keys(), test_names.values()) |name, first| {
13689 if (first.next == null) continue;
13690 var notes: std.ArrayListUnmanaged(u32) = .{};
13691 var prev: NameEntry = first;
13692 while (prev.next) |cur| : (prev = cur.*) {
13693 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate test here", .{}));
13694 }
13695 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
13696 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
13697 try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);
13698 }
13699
13700 for (decltest_names.keys(), decltest_names.values()) |name, first| {
13701 if (first.next == null) continue;
13702 var notes: std.ArrayListUnmanaged(u32) = .{};
13703 var prev: NameEntry = first;
13704 while (prev.next) |cur| : (prev = cur.*) {
13705 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate decltest here", .{}));
13706 }
13707 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
13708 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
13709 try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);
13710 }
13711
13756 return decl_count;13712 return decl_count;
13757}13713}
1375813714