authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-08 10:01:33+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-14 07:40:05+00:00
log00969062a9d5e8229737f0ec38a8af91822caf31
tree0ffc03a74a69ad3ea53b48fb72491c453bf62119
parent778ab767b1e367233a1a3284e2c24d2c27b44602
signaturelock-open Commit is signed but in an unrecognized format.

compiler: detect duplicate test names in AstGen

There is no reason to perform this detection during semantic analysis. In fact, doing so is problematic, because we wish to utilize detection of existing decls in a namespace in incremental compilation.

3 files changed, 56 insertions(+), 21 deletions(-)

lib/std/zig/AstGen.zig+49-1
......@@ -13496,6 +13496,15 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
1349613496 const node_tags = tree.nodes.items(.tag);
1349713497 const main_tokens = tree.nodes.items(.main_token);
1349813498 const token_tags = tree.tokens.items(.tag);
13499
13500 // We don't have shadowing for test names, so we just track those for duplicate reporting locally.
13501 var named_tests: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{};
13502 var decltests: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{};
13503 defer {
13504 named_tests.deinit(gpa);
13505 decltests.deinit(gpa);
13506 }
13507
1349913508 var decl_count: u32 = 0;
1350013509 for (members) |member_node| {
1350113510 const name_token = switch (node_tags[member_node]) {
......@@ -13525,11 +13534,50 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
1352513534 break :blk ident;
1352613535 },
1352713536
13528 .@"comptime", .@"usingnamespace", .test_decl => {
13537 .@"comptime", .@"usingnamespace" => {
1352913538 decl_count += 1;
1353013539 continue;
1353113540 },
1353213541
13542 .test_decl => {
13543 decl_count += 1;
13544 // We don't want shadowing detection here, and test names work a bit differently, so
13545 // we must do the redeclaration detection ourselves.
13546 const test_name_token = main_tokens[member_node] + 1;
13547 switch (token_tags[test_name_token]) {
13548 else => {}, // unnamed test
13549 .string_literal => {
13550 const name = try astgen.strLitAsString(test_name_token);
13551 const gop = try named_tests.getOrPut(gpa, name.index);
13552 if (gop.found_existing) {
13553 const name_slice = astgen.string_bytes.items[@intFromEnum(name.index)..][0..name.len];
13554 const name_duped = try gpa.dupe(u8, name_slice);
13555 defer gpa.free(name_duped);
13556 try astgen.appendErrorNodeNotes(member_node, "duplicate test name '{s}'", .{name_duped}, &.{
13557 try astgen.errNoteNode(gop.value_ptr.*, "other test here", .{}),
13558 });
13559 } else {
13560 gop.value_ptr.* = member_node;
13561 }
13562 },
13563 .identifier => {
13564 const name = try astgen.identAsString(test_name_token);
13565 const gop = try decltests.getOrPut(gpa, name);
13566 if (gop.found_existing) {
13567 const name_slice = mem.span(astgen.nullTerminatedString(name));
13568 const name_duped = try gpa.dupe(u8, name_slice);
13569 defer gpa.free(name_duped);
13570 try astgen.appendErrorNodeNotes(member_node, "duplicate decltest '{s}'", .{name_duped}, &.{
13571 try astgen.errNoteNode(gop.value_ptr.*, "other decltest here", .{}),
13572 });
13573 } else {
13574 gop.value_ptr.* = member_node;
13575 }
13576 },
13577 }
13578 continue;
13579 },
13580
1353313581 else => continue,
1353413582 };
1353513583
src/Module.zig+5-18
......@@ -4128,6 +4128,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void
41284128 .@"comptime" => info: {
41294129 const i = iter.comptime_index;
41304130 iter.comptime_index += 1;
4131 // TODO: avoid collisions with named decls with this name
41314132 break :info .{
41324133 try ip.getOrPutStringFmt(gpa, "comptime_{d}", .{i}),
41334134 .@"comptime",
......@@ -4137,6 +4138,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void
41374138 .@"usingnamespace" => info: {
41384139 const i = iter.usingnamespace_index;
41394140 iter.usingnamespace_index += 1;
4141 // TODO: avoid collisions with named decls with this name
41404142 break :info .{
41414143 try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i}),
41424144 .@"usingnamespace",
......@@ -4146,6 +4148,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void
41464148 .unnamed_test => info: {
41474149 const i = iter.unnamed_test_index;
41484150 iter.unnamed_test_index += 1;
4151 // TODO: avoid collisions with named decls with this name
41494152 break :info .{
41504153 try ip.getOrPutStringFmt(gpa, "test_{d}", .{i}),
41514154 .@"test",
......@@ -4155,6 +4158,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void
41554158 .decltest => info: {
41564159 assert(declaration.flags.has_doc_comment);
41574160 const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end]));
4161 // TODO: avoid collisions with named decls with this name
41584162 break :info .{
41594163 try ip.getOrPutStringFmt(gpa, "decltest.{s}", .{name}),
41604164 .@"test",
......@@ -4162,6 +4166,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void
41624166 };
41634167 },
41644168 _ => if (declaration.name.isNamedTest(zir)) .{
4169 // TODO: avoid collisions with named decls with this name
41654170 try ip.getOrPutStringFmt(gpa, "test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}),
41664171 .@"test",
41674172 true,
......@@ -4226,24 +4231,6 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void
42264231 }
42274232 const decl_index = gop.key_ptr.*;
42284233 const decl = zcu.declPtr(decl_index);
4229 if (kind == .@"test") {
4230 const src_loc = SrcLoc{
4231 .file_scope = decl.getFileScope(zcu),
4232 .parent_decl_node = decl.src_node,
4233 .lazy = .{ .token_offset = 1 },
4234 };
4235 const msg = try ErrorMsg.create(gpa, src_loc, "duplicate test name: {}", .{
4236 decl_name.fmt(ip),
4237 });
4238 errdefer msg.destroy(gpa);
4239 try zcu.failed_decls.putNoClobber(gpa, decl_index, msg);
4240 const other_src_loc = SrcLoc{
4241 .file_scope = namespace.file_scope,
4242 .parent_decl_node = decl_node,
4243 .lazy = .{ .token_offset = 1 },
4244 };
4245 try zcu.errNoteNonLazy(other_src_loc, msg, "other test here", .{});
4246 }
42474234 // Update the AST node of the decl; even if its contents are unchanged, it may
42484235 // have been re-ordered.
42494236 decl.src_node = decl_node;
test/cases/compile_errors/invalid_duplicate_test_decl_name.zig+2-2
......@@ -6,5 +6,5 @@ test "thingy" {}
66// target=native
77// is_test=true
88//
9// :1:6: error: duplicate test name: test.thingy
10// :2:6: note: other test here
9// :2:1: error: duplicate test name 'thingy'
10// :1:1: note: other test here