| ... | @@ -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)); |
| 4055 | | 4055 | |
| 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; |
| 4058 | | 4058 | |
| 4059 | // We insert this at the beginning so that its instruction index marks the | 4059 | // 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 | }; |
| 5021 | | 5021 | |
| 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); |
| 5024 | | 5024 | |
| 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 | } |
| 5090 | | 5090 | |
| 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); |
| 5118 | | 5109 | |
| 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 | } |
| 5213 | | 5194 | |
| 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); |
| 5242 | | 5197 | |
| ... | @@ -5317,7 +5272,7 @@ fn unionDeclInner( | ... | @@ -5317,7 +5272,7 @@ fn unionDeclInner( |
| 5317 | }; | 5272 | }; |
| 5318 | defer block_scope.unstack(); | 5273 | defer block_scope.unstack(); |
| 5319 | | 5274 | |
| 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); |
| 5322 | | 5277 | |
| 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 | } |
| 5350 | | 5305 | |
| 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)); |
| 5376 | | 5322 | |
| 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)); |
| 5389 | | 5325 | |
| ... | @@ -5438,32 +5374,6 @@ fn unionDeclInner( | ... | @@ -5438,32 +5374,6 @@ fn unionDeclInner( |
| 5438 | } | 5374 | } |
| 5439 | } | 5375 | } |
| 5440 | | 5376 | |
| 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); |
| 5469 | | 5379 | |
| ... | @@ -5666,7 +5576,7 @@ fn containerDecl( | ... | @@ -5666,7 +5576,7 @@ fn containerDecl( |
| 5666 | }; | 5576 | }; |
| 5667 | defer block_scope.unstack(); | 5577 | defer block_scope.unstack(); |
| 5668 | | 5578 | |
| 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; |
| 5671 | | 5581 | |
| 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)}); |
| 5689 | | 5599 | |
| 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)); |
| 5714 | | 5615 | |
| 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)); |
| 5727 | | 5618 | |
| ... | @@ -5748,32 +5639,6 @@ fn containerDecl( | ... | @@ -5748,32 +5639,6 @@ fn containerDecl( |
| 5748 | } | 5639 | } |
| 5749 | } | 5640 | } |
| 5750 | | 5641 | |
| 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(); |
| 5835 | | 5700 | |
| 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"); |
| 5837 | | 5702 | |
| 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 | } |
| 13596 | | 13461 | |
| 13597 | fn 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). |
| | 13464 | fn 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); |
| 13603 | | 13475 | |
| 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 | } |
| 13611 | | 13497 | |
| | 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 | }, |
| 13623 | | 13524 | |
| 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 | }, |
| 13640 | | 13539 | |
| 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, so | 13547 | // 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 test | 13555 | 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 | }, |
| 13684 | | 13585 | |
| 13685 | else => continue, | 13586 | else => unreachable, |
| 13686 | }; | 13587 | }; |
| 13687 | | 13588 | |
| | 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 | } |
| 13716 | | 13632 | |
| 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 | } |
| 13758 | | 13714 | |