diff --git a/src/ast/P.zig b/src/ast/P.zig index 0dfe947c85..e322266d75 100644 --- a/src/ast/P.zig +++ b/src/ast/P.zig @@ -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 diff --git a/test/bundler/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js index ba631293ea..a83c185430 100644 --- a/test/bundler/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -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}");