From d7403cba5fdb0622d5db0a1e22578cf0b9a535f1 Mon Sep 17 00:00:00 2001 From: nullptrdevs <16590917+nullptrdevs@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:34:37 -0800 Subject: [PATCH] AstGen: catch duplicate field names --- src/stage2/AstGen.zig | 100 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/stage2/AstGen.zig b/src/stage2/AstGen.zig index fa8cd4a1aa..54895e086b 100644 --- a/src/stage2/AstGen.zig +++ b/src/stage2/AstGen.zig @@ -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); @@ -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; @@ -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", .{}); } @@ -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,