Fix panic on anonymous class declaration with decorator targeting ES2022#3926
Fix panic on anonymous class declaration with decorator targeting ES2022#3926Copilot wants to merge 4 commits into
Conversation
…ng ES2022 Handle the case where an anonymous class declaration (not export default) has decorators. Instead of asserting that a name must exist, emit the IIFE as an expression statement. Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/a8b3bbaf-a8f1-4207-973a-1001c1b3537d Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
| } else { | ||
| debug.Assert(classNode.Name() != nil, "A class declaration that is not a default export must have a name.") |
There was a problem hiding this comment.
@copilot The original code said:
Debug.assertIsDefined(node.name, "A class declaration that is not a default export must have a name.");Removing an assert that the old code does not make sense unless you can prove that the old compiler also crashes on this input
There was a problem hiding this comment.
You're right. I verified that TypeScript's tsc and transpileModule both handle this input without crashing — they emit let default_1 = (() => { ... })(); with a generated name. The name generation happens in TypeScript's ts.ts transformer at lines 918-923, where needsName includes HasMemberDecorators.
The Go port's RuntimeSyntaxTransformer.visitClassDeclaration was only generating names for namespace exports (exported && name == nil) but was missing the member decorator case. I've reverted the esdecorator.go change (assertion preserved) and instead fixed the root cause in runtimesyntax.go to also generate a name when ChildIsDecorated is true, matching the TypeScript behavior. See def1f4a.
…ators Instead of removing the assertion in esdecorator.go, fix the root cause in RuntimeSyntaxTransformer. When a class declaration has member decorators but no name, generate a name (matching TypeScript's ts.ts transformer behavior at lines 918-923). This ensures the esDecorators transformer always receives a named class declaration, preserving the assertion. Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/cf0722bd-efb1-48e2-bd50-82f8c7bb4fb6 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes a panic in the ES decorator transformer when an anonymous class declaration contains member decorators (e.g. class { @x m() {} } with target: es2022). The runtime-syntax transformer previously only generated a name for anonymous classes that were namespace exports; it now also generates a name when the class has decorated members, matching the upstream ts.ts transformer behavior so the downstream ES decorator transformer's "name is defined" invariant is upheld.
Changes:
- Extend
RuntimeSyntaxTransformer.visitClassDeclarationto also generate a name whenast.ChildIsDecoratedreports decorated members. - Add a compiler test case
anonymousClassDecoratorEs2022.tswith corresponding baselines (.js,.types,.symbols,.errors.txt).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/transformers/tstransforms/runtimesyntax.go | Adds ChildIsDecorated to the name-generation condition for anonymous class declarations. |
| testdata/tests/cases/compiler/anonymousClassDecoratorEs2022.ts | New minimal repro test for an anonymous decorated class at target: es2022. |
| testdata/baselines/reference/compiler/anonymousClassDecoratorEs2022.js | JS emit baseline showing the generated default_1 name and __esDecorate lowering. |
| testdata/baselines/reference/compiler/anonymousClassDecoratorEs2022.types | Types baseline for the new test. |
| testdata/baselines/reference/compiler/anonymousClassDecoratorEs2022.symbols | Symbols baseline for the new test. |
| testdata/baselines/reference/compiler/anonymousClassDecoratorEs2022.errors.txt | Expected diagnostics baseline (TS1211 and TS2304). |
| name := tx.Visitor().VisitNode(node.Name()) | ||
| if exported && name == nil { | ||
| needsName := (exported && name == nil) || | ||
| ast.ChildIsDecorated(false /*useLegacyDecorators*/, node.AsNode(), nil) |
| needsName := (exported && name == nil) || | ||
| ast.ChildIsDecorated(false /*useLegacyDecorators*/, node.AsNode(), nil) | ||
| if needsName && name == nil { |
|
@copilot apply changes based on the comments in this thread |
…orators option - Short-circuit ChildIsDecorated call behind name==nil check to avoid unnecessary member traversal when the class already has a name - Use tx.compilerOptions.ExperimentalDecorators.IsTrue() instead of hardcoded false, consistent with metadata.go and classfields.go Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/f3be6820-9af2-4fd8-89f4-2d811a054426 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Applied both changes in aff8b1a:
|
anonymousClassDecoratorEs2022.tsChildIsDecoratedbehindname == nilto avoid unnecessary traversaltx.compilerOptions.ExperimentalDecorators.IsTrue()instead of hardcodedfalse