Files
camc314 c1369a71ff fix(codegen): resolve private names in class heritage (#25588)
### Summary

Fix private-member mangling for class heritage expressions.

The code generator previously activated a class's private-member mapping before emitting that class's decorators and `extends` expression. A private identifier in those expressions is evaluated in the enclosing lexical class scope, not the class currently being declared.

This change:

- reserves each class's mapping ID in AST traversal order;
- activates that mapping only immediately before emitting its class body;
- adds a mangler/codegen regression test covering a nested class with identically named private members.

### Reproduction

```js
class HasOuterBrand {}
class MissingOuterBrand {}

class Outer {
  #x;

  create(o) {
    return class Inner extends (
      #x in o ? HasOuterBrand : MissingOuterBrand
    ) {
      #x;
    };
  }
}
```

`#x` in `Inner`'s heritage expression refers to `Outer.#x`. `Inner.#x` is not in scope until the `Inner` class body.

### Scope model

```text
Outer class body: active private mapping
  Outer.#x -> #e

  create(o) {
    class Inner extends (#x in o ? ... : ...)
                        │
                        └─ resolves in Outer scope: #e

    Inner class body: active private mapping
      Inner.#x -> #t
  }
```

### Before

Codegen entered `Inner` before emitting the entire class header:

```rust
p.enter_class();
p.print_decorators(&self.decorators, ctx);
...
heritage.expression.print_expr(...);
...
self.body.print(p, ctx);
```

That made private-identifier lookup prefer `Inner`'s mapping while printing `extends`, producing the wrong brand:

```js
return class t extends (#t in e ? HasOuterBrand : MissingOuterBrand) {
  #t;
};
```

The `#t` in the heritage expression is invalid because `Inner.#t` is not yet in scope.

### After

The class ID is reserved first, preserving mapping IDs assigned in traversal order, but the class mapping is pushed only for the body:

```rust
let class_id = p.reserve_class();

p.print_decorators(&self.decorators, ctx);
...
heritage.expression.print_expr(...);

p.enter_class(class_id);
self.body.print(p, ctx);
p.exit_class();
```

```text
Class-ID allocation              Active private-name scope
────────────────────             ─────────────────────────────
reserve Outer ID                 enclosing class / none
  reserve nested header IDs      enclosing class / none
enter Outer ID                   Outer body
  enter Inner ID                 Inner body
```

Separating reservation from activation matters: moving allocation until after heritage could shift IDs for nested classes encountered in a heritage expression and break the mangler's precomputed mapping association.

### Regression coverage

The existing minifier mangler integration harness now snapshots this case with private-member mappings enabled. It confirms that the generated heritage expression uses Outer’s mapping (`#e`) while Inner’s field uses its distinct mapping (`#t`):

```js
class Outer {
  #e;
  create(e) {
    return class t extends (#e in e ? HasOuterBrand : MissingOuterBrand) {
      #t;
    };
  }
}
```
2026-08-13 13:17:31 +00:00
..

Oxc Codegen

High-performance code generation from AST back to JavaScript/TypeScript source code.

Overview

This crate converts AST nodes back into source code strings, supporting JavaScript, TypeScript, and JSX. It's designed for speed and correctness, producing clean, readable output.

Key Features

  • Fast code generation: Optimized for performance with minimal allocations
  • Source map support: Generate accurate source maps during output
  • Configurable formatting: Control whitespace, semicolons, and other formatting options
  • Comment preservation: Maintain comments during code generation
  • Binary expression optimization: Intelligent parentheses insertion

Architecture

Code Generation Pipeline

  1. AST Traversal: Walk through AST nodes systematically
  2. Token Generation: Convert nodes to appropriate tokens/strings
  3. Formatting: Apply whitespace, indentation, and style rules
  4. Source Mapping: Track original source positions if enabled

Design Principles

  • Correctness: Generated code must be functionally equivalent to original
  • Performance: Minimize string allocations and copying
  • Readability: Produce clean, well-formatted output
  • Fidelity: Preserve semantic meaning and behavior

The codegen is adapted from esbuild's approach, optimized for Rust and oxc's AST structure.