mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(transpiler): avoid private name collisions in auto-accessor lowering
Generated backing field names for computed auto-accessors (#a, #b, ...) now skip names already used by existing private fields in the class. Private names are not subject to automatic renaming, so collisions would produce duplicate private name errors at runtime. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4883,13 +4883,30 @@ pub fn NewParser_(
|
||||
break :blk std.fmt.allocPrint(p.allocator, "#{s}", .{str.data}) catch unreachable;
|
||||
}
|
||||
}
|
||||
// Computed key -> counter-based name
|
||||
const offset: u8 = @intCast(@min(auto_accessor_count, 25));
|
||||
const name = std.fmt.allocPrint(p.allocator, "#{c}", .{
|
||||
@as(u8, 'a' + offset),
|
||||
}) catch unreachable;
|
||||
auto_accessor_count += 1;
|
||||
break :blk name;
|
||||
// Computed key -> counter-based name, skipping any
|
||||
// that collide with existing private names in the class
|
||||
// (private names are not subject to automatic renaming).
|
||||
while (true) : (auto_accessor_count += 1) {
|
||||
const offset: u8 = @intCast(@min(auto_accessor_count, 25));
|
||||
const candidate = std.fmt.allocPrint(p.allocator, "#{c}", .{
|
||||
@as(u8, 'a' + offset),
|
||||
}) catch unreachable;
|
||||
var collides = false;
|
||||
for (properties) |other| {
|
||||
if (other.key != null and other.key.?.data == .e_private_identifier) {
|
||||
const other_name = p.loadNameFromRef(other.key.?.data.e_private_identifier.ref);
|
||||
if (strings.eql(candidate, other_name)) {
|
||||
collides = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!collides) {
|
||||
auto_accessor_count += 1;
|
||||
break :blk candidate;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
};
|
||||
|
||||
// Create backing field symbol
|
||||
|
||||
@@ -885,6 +885,12 @@ function foo() {}
|
||||
exp("class Foo { declare accessor x: number }", "class Foo {\n}");
|
||||
exp("abstract class Foo { abstract accessor x: number }", "class Foo {\n}");
|
||||
|
||||
// Computed backing field skips existing private names to avoid collisions
|
||||
exp(
|
||||
"class Foo { accessor [x]; #a = 1 }",
|
||||
"var _a;\n\nclass Foo {\n #b;\n get [_a = x]() {\n return this.#b;\n }\n set [_a](_) {\n this.#b = _;\n }\n #a = 1;\n}",
|
||||
);
|
||||
|
||||
// Contextual keyword edge cases (not auto-accessor syntax)
|
||||
exp("class Foo { accessor() {} }", "class Foo {\n accessor() {}\n}");
|
||||
exp("class Foo { accessor = 1 }", "class Foo {\n accessor = 1;\n}");
|
||||
|
||||
Reference in New Issue
Block a user