Skip to content

Commit d7403cb

Browse files
committed
AstGen: catch duplicate field names
1 parent 3f4456b commit d7403cb

File tree

1 file changed

+99
-1
lines changed

1 file changed

+99
-1
lines changed

src/stage2/AstGen.zig

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,6 +1722,57 @@ fn structInitExpr(
17221722
}
17231723
}
17241724

1725+
{
1726+
var sfba = std.heap.stackFallback(256, astgen.arena);
1727+
const sfba_allocator = sfba.get();
1728+
1729+
var duplicate_names = std.AutoArrayHashMap(u32, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
1730+
defer duplicate_names.deinit();
1731+
try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
1732+
1733+
// When there aren't errors, use this to avoid a second iteration.
1734+
var any_duplicate = false;
1735+
1736+
for (struct_init.ast.fields) |field| {
1737+
const name_token = tree.firstToken(field) - 2;
1738+
const name_index = try astgen.identAsString(name_token);
1739+
1740+
const gop = try duplicate_names.getOrPut(name_index);
1741+
1742+
if (gop.found_existing) {
1743+
try gop.value_ptr.append(sfba_allocator, name_token);
1744+
any_duplicate = true;
1745+
} else {
1746+
gop.value_ptr.* = .{};
1747+
try gop.value_ptr.append(sfba_allocator, name_token);
1748+
}
1749+
}
1750+
1751+
if (any_duplicate) {
1752+
var it = duplicate_names.iterator();
1753+
1754+
while (it.next()) |entry| {
1755+
const record = entry.value_ptr.*;
1756+
if (record.items.len > 1) {
1757+
var error_notes = std.ArrayList(u32).init(astgen.arena);
1758+
1759+
for (record.items[1..]) |duplicate| {
1760+
try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
1761+
}
1762+
1763+
try astgen.appendErrorTokNotes(
1764+
record.items[0],
1765+
"duplicate field",
1766+
.{},
1767+
error_notes.items,
1768+
);
1769+
}
1770+
}
1771+
1772+
return error.AnalysisFail;
1773+
}
1774+
}
1775+
17251776
if (struct_init.ast.type_expr != 0) {
17261777
// Typed inits do not use RLS for language simplicity.
17271778
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
@@ -4880,6 +4931,15 @@ fn structDeclInner(
48804931
}
48814932
};
48824933

4934+
var sfba = std.heap.stackFallback(256, astgen.arena);
4935+
const sfba_allocator = sfba.get();
4936+
4937+
var duplicate_names = std.AutoArrayHashMap(u32, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
4938+
try duplicate_names.ensureTotalCapacity(field_count);
4939+
4940+
// When there aren't errors, use this to avoid a second iteration.
4941+
var any_duplicate = false;
4942+
48834943
var known_non_opv = false;
48844944
var known_comptime_only = false;
48854945
var any_comptime_fields = false;
@@ -4892,11 +4952,21 @@ fn structDeclInner(
48924952
};
48934953

48944954
if (!is_tuple) {
4955+
const field_name = try astgen.identAsString(member.ast.main_token);
48954956
member.convertToNonTupleLike(astgen.tree.nodes);
48964957
assert(!member.ast.tuple_like);
48974958

4898-
const field_name = try astgen.identAsString(member.ast.main_token);
48994959
wip_members.appendToField(field_name);
4960+
4961+
const gop = try duplicate_names.getOrPut(field_name);
4962+
4963+
if (gop.found_existing) {
4964+
try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4965+
any_duplicate = true;
4966+
} else {
4967+
gop.value_ptr.* = .{};
4968+
try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4969+
}
49004970
} else if (!member.ast.tuple_like) {
49014971
return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
49024972
}
@@ -4978,6 +5048,34 @@ fn structDeclInner(
49785048
}
49795049
}
49805050

5051+
if (any_duplicate) {
5052+
var it = duplicate_names.iterator();
5053+
5054+
while (it.next()) |entry| {
5055+
const record = entry.value_ptr.*;
5056+
if (record.items.len > 1) {
5057+
var error_notes = std.ArrayList(u32).init(astgen.arena);
5058+
5059+
for (record.items[1..]) |duplicate| {
5060+
try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
5061+
}
5062+
5063+
try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
5064+
5065+
try astgen.appendErrorTokNotes(
5066+
record.items[0],
5067+
"duplicate struct field: '{s}'",
5068+
.{try astgen.identifierTokenString(record.items[0])},
5069+
error_notes.items,
5070+
);
5071+
}
5072+
}
5073+
5074+
return error.AnalysisFail;
5075+
}
5076+
5077+
duplicate_names.deinit();
5078+
49815079
try gz.setStruct(decl_inst, .{
49825080
.src_node = node,
49835081
.layout = layout,

0 commit comments

Comments
 (0)