Skip to content

Commit

Permalink
AstGen: catch duplicate field names
Browse files Browse the repository at this point in the history
  • Loading branch information
llogick committed Nov 16, 2023
1 parent 3f49caf commit 848c8b4
Showing 1 changed file with 99 additions and 1 deletion.
100 changes: 99 additions & 1 deletion src/stage2/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1722,6 +1722,57 @@ fn structInitExpr(
}
}

{
var sfba = std.heap.stackFallback(256, astgen.arena);
const sfba_allocator = sfba.get();

var duplicate_names = std.AutoArrayHashMap(u32, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
defer duplicate_names.deinit();
try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));

// When there aren't errors, use this to avoid a second iteration.
var any_duplicate = false;

for (struct_init.ast.fields) |field| {
const name_token = tree.firstToken(field) - 2;
const name_index = try astgen.identAsString(name_token);

const gop = try duplicate_names.getOrPut(name_index);

if (gop.found_existing) {
try gop.value_ptr.append(sfba_allocator, name_token);
any_duplicate = true;
} else {
gop.value_ptr.* = .{};
try gop.value_ptr.append(sfba_allocator, name_token);
}
}

if (any_duplicate) {
var it = duplicate_names.iterator();

while (it.next()) |entry| {
const record = entry.value_ptr.*;
if (record.items.len > 1) {
var error_notes = std.ArrayList(u32).init(astgen.arena);

for (record.items[1..]) |duplicate| {
try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
}

try astgen.appendErrorTokNotes(
record.items[0],
"duplicate field",
.{},
error_notes.items,
);
}
}

return error.AnalysisFail;
}
}

if (struct_init.ast.type_expr != 0) {
// Typed inits do not use RLS for language simplicity.
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
Expand Down Expand Up @@ -4880,6 +4931,15 @@ fn structDeclInner(
}
};

var sfba = std.heap.stackFallback(256, astgen.arena);
const sfba_allocator = sfba.get();

var duplicate_names = std.AutoArrayHashMap(u32, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
try duplicate_names.ensureTotalCapacity(field_count);

// When there aren't errors, use this to avoid a second iteration.
var any_duplicate = false;

var known_non_opv = false;
var known_comptime_only = false;
var any_comptime_fields = false;
Expand All @@ -4892,11 +4952,21 @@ fn structDeclInner(
};

if (!is_tuple) {
const field_name = try astgen.identAsString(member.ast.main_token);
member.convertToNonTupleLike(astgen.tree.nodes);
assert(!member.ast.tuple_like);

const field_name = try astgen.identAsString(member.ast.main_token);
wip_members.appendToField(field_name);

const gop = try duplicate_names.getOrPut(field_name);

if (gop.found_existing) {
try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
any_duplicate = true;
} else {
gop.value_ptr.* = .{};
try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
}
} else if (!member.ast.tuple_like) {
return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
}
Expand Down Expand Up @@ -4978,6 +5048,34 @@ fn structDeclInner(
}
}

if (any_duplicate) {
var it = duplicate_names.iterator();

while (it.next()) |entry| {
const record = entry.value_ptr.*;
if (record.items.len > 1) {
var error_notes = std.ArrayList(u32).init(astgen.arena);

for (record.items[1..]) |duplicate| {
try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
}

try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));

try astgen.appendErrorTokNotes(
record.items[0],
"duplicate struct field: '{s}'",
.{try astgen.identifierTokenString(record.items[0])},
error_notes.items,
);
}
}

return error.AnalysisFail;
}

duplicate_names.deinit();

try gz.setStruct(decl_inst, .{
.src_node = node,
.layout = layout,
Expand Down

0 comments on commit 848c8b4

Please sign in to comment.