From e44484926f440e69f613dc1dad8c7fc193a21026 Mon Sep 17 00:00:00 2001 From: Cheng Date: Sat, 24 Oct 2026 18:43:49 +0800 Subject: [PATCH 1/2] Report inaccessible heritage types instead of crashing during declaration emit --- .../transformers/declarations/diagnostics.go | 2 +- .../transformers/declarations/util.go | 1 + ...ionEmitInaccessibleHeritageType.errors.txt | 33 +++++++++++++++++++ ...declarationEmitInaccessibleHeritageType.js | 26 +++++++++++++++ ...rationEmitInaccessibleHeritageType.symbols | 31 +++++++++++++++++ ...larationEmitInaccessibleHeritageType.types | 22 +++++++++++++ ...declarationEmitInaccessibleHeritageType.ts | 14 ++++++++ 7 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js create mode 100644 tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types create mode 100644 tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts diff --git a/tsc/internal/transformers/declarations/diagnostics.go b/tsc/internal/transformers/declarations/diagnostics.go index 1050afa933eff..999d5cd735252 100644 --- a/tsc/internal/transformers/declarations/diagnostics.go +++ b/tsc/internal/transformers/declarations/diagnostics.go @@ -150,7 +150,7 @@ func createGetSymbolAccessibilityDiagnosticForNode(node *ast.Node) GetSymbolAcce return wrapSimpleDiagnosticSelector(node, getParameterDeclarationTypeVisibilityDiagnosticMessage) } else if ast.IsTypeParameterDeclaration(node) { return wrapSimpleDiagnosticSelector(node, getTypeParameterConstraintVisibilityDiagnosticMessage) - } else if ast.IsExpressionWithTypeArguments(node) { + } else if ast.IsExpressionWithTypeArguments(node) || (ast.IsTypeReferenceNode(node) && ast.IsHeritageClause(node.Parent)) { // unique node selection behavior, inline closure return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { var diagnosticMessage *diagnostics.Message diff --git a/tsc/internal/transformers/declarations/util.go b/tsc/internal/transformers/declarations/util.go index 12bfff9600c0d..58eb4fdd82973 100644 --- a/tsc/internal/transformers/declarations/util.go +++ b/tsc/internal/transformers/declarations/util.go @@ -37,6 +37,7 @@ func canProduceDiagnostics(node *ast.Node) bool { ast.IsParameterDeclaration(node) || ast.IsTypeParameterDeclaration(node) || ast.IsExpressionWithTypeArguments(node) || + (ast.IsTypeReferenceNode(node) && ast.IsHeritageClause(node.Parent)) || ast.IsImportEqualsDeclaration(node) || ast.IsTypeAliasDeclaration(node) || ast.IsJSTypeAliasDeclaration(node) || diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt new file mode 100644 index 0000000000000..d2fe1a0d13bc4 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt @@ -0,0 +1,33 @@ +implements.ts(1,1): error TS1344: A label is not allowed here. +implements.ts(2,27): error TS4019: Implements clause of exported class 'C' has or is using private name 'I'. +interface.ts(1,1): error TS1344: A label is not allowed here. +interface.ts(2,28): error TS4022: 'extends' clause of exported interface 'J' has or is using private name 'I'. +typeArgument.ts(1,1): error TS1344: A label is not allowed here. +typeArgument.ts(3,33): error TS4022: 'extends' clause of exported interface 'J' has or is using private name 'I'. + + +==== interface.ts (2 errors) ==== + label: interface I {} + ~~~~~ +!!! error TS1344: A label is not allowed here. + export interface J extends I {} + ~ +!!! error TS4022: 'extends' clause of exported interface 'J' has or is using private name 'I'. + +==== implements.ts (2 errors) ==== + label: interface I {} + ~~~~~ +!!! error TS1344: A label is not allowed here. + export class C implements I {} + ~ +!!! error TS4019: Implements clause of exported class 'C' has or is using private name 'I'. + +==== typeArgument.ts (2 errors) ==== + label: interface I {} + ~~~~~ +!!! error TS1344: A label is not allowed here. + interface Base {} + export interface J extends Base {} + ~ +!!! error TS4022: 'extends' clause of exported interface 'J' has or is using private name 'I'. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js new file mode 100644 index 0000000000000..ef0549f5a388a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js @@ -0,0 +1,26 @@ +//// [tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts] //// + +//// [interface.ts] +label: interface I {} +export interface J extends I {} + +//// [implements.ts] +label: interface I {} +export class C implements I {} + +//// [typeArgument.ts] +label: interface I {} +interface Base {} +export interface J extends Base {} + + +//// [interface.js] +label: ; +export {}; +//// [implements.js] +label: ; +export class C { +} +//// [typeArgument.js] +label: ; +export {}; diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols new file mode 100644 index 0000000000000..35d2e3f0d1c46 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts] //// + +=== interface.ts === +label: interface I {} +>I : Symbol(I, Decl(interface.ts, 0, 6)) + +export interface J extends I {} +>J : Symbol(J, Decl(interface.ts, 0, 21)) +>I : Symbol(I, Decl(interface.ts, 0, 6)) + +=== implements.ts === +label: interface I {} +>I : Symbol(I, Decl(implements.ts, 0, 6)) + +export class C implements I {} +>C : Symbol(C, Decl(implements.ts, 0, 21)) +>I : Symbol(I, Decl(implements.ts, 0, 6)) + +=== typeArgument.ts === +label: interface I {} +>I : Symbol(I, Decl(typeArgument.ts, 0, 6)) + +interface Base {} +>Base : Symbol(Base, Decl(typeArgument.ts, 0, 21)) +>T : Symbol(T, Decl(typeArgument.ts, 1, 15)) + +export interface J extends Base {} +>J : Symbol(J, Decl(typeArgument.ts, 1, 20)) +>Base : Symbol(Base, Decl(typeArgument.ts, 0, 21)) +>I : Symbol(I, Decl(typeArgument.ts, 0, 6)) + diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types new file mode 100644 index 0000000000000..8b6a2c9dfd9d1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts] //// + +=== interface.ts === +label: interface I {} +>label : any + +export interface J extends I {} + +=== implements.ts === +label: interface I {} +>label : any + +export class C implements I {} +>C : C + +=== typeArgument.ts === +label: interface I {} +>label : any + +interface Base {} +export interface J extends Base {} + diff --git a/tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts b/tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts new file mode 100644 index 0000000000000..b842d263b93a1 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts @@ -0,0 +1,14 @@ +// @declaration: true + +// @filename: interface.ts +label: interface I {} +export interface J extends I {} + +// @filename: implements.ts +label: interface I {} +export class C implements I {} + +// @filename: typeArgument.ts +label: interface I {} +interface Base {} +export interface J extends Base {} From 69f47794f9dcef7f057bf4e225451a007a34d607 Mon Sep 17 00:00:00 2001 From: Cheng Date: Sun, 25 Oct 2026 11:23:11 +0800 Subject: [PATCH 2/2] Preserve diagnostic context for class expression heritage types --- .../transformers/declarations/diagnostics.go | 5 ++++- .../transformers/declarations/util.go | 4 +++- ...ionEmitInaccessibleHeritageType.errors.txt | 20 +++++++++++++++++++ ...declarationEmitInaccessibleHeritageType.js | 16 +++++++++++++++ ...rationEmitInaccessibleHeritageType.symbols | 15 ++++++++++++++ ...larationEmitInaccessibleHeritageType.types | 17 ++++++++++++++++ ...declarationEmitInaccessibleHeritageType.ts | 8 ++++++++ 7 files changed, 83 insertions(+), 2 deletions(-) diff --git a/tsc/internal/transformers/declarations/diagnostics.go b/tsc/internal/transformers/declarations/diagnostics.go index 999d5cd735252..9be56fa0a37fb 100644 --- a/tsc/internal/transformers/declarations/diagnostics.go +++ b/tsc/internal/transformers/declarations/diagnostics.go @@ -150,7 +150,10 @@ func createGetSymbolAccessibilityDiagnosticForNode(node *ast.Node) GetSymbolAcce return wrapSimpleDiagnosticSelector(node, getParameterDeclarationTypeVisibilityDiagnosticMessage) } else if ast.IsTypeParameterDeclaration(node) { return wrapSimpleDiagnosticSelector(node, getTypeParameterConstraintVisibilityDiagnosticMessage) - } else if ast.IsExpressionWithTypeArguments(node) || (ast.IsTypeReferenceNode(node) && ast.IsHeritageClause(node.Parent)) { + } else if ast.IsExpressionWithTypeArguments(node) || + (ast.IsTypeReferenceNode(node) && + ast.IsHeritageClause(node.Parent) && + !ast.IsClassExpression(node.Parent.Parent)) { // unique node selection behavior, inline closure return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { var diagnosticMessage *diagnostics.Message diff --git a/tsc/internal/transformers/declarations/util.go b/tsc/internal/transformers/declarations/util.go index 58eb4fdd82973..1f009d21e486c 100644 --- a/tsc/internal/transformers/declarations/util.go +++ b/tsc/internal/transformers/declarations/util.go @@ -37,7 +37,9 @@ func canProduceDiagnostics(node *ast.Node) bool { ast.IsParameterDeclaration(node) || ast.IsTypeParameterDeclaration(node) || ast.IsExpressionWithTypeArguments(node) || - (ast.IsTypeReferenceNode(node) && ast.IsHeritageClause(node.Parent)) || + (ast.IsTypeReferenceNode(node) && + ast.IsHeritageClause(node.Parent) && + !ast.IsClassExpression(node.Parent.Parent)) || ast.IsImportEqualsDeclaration(node) || ast.IsTypeAliasDeclaration(node) || ast.IsJSTypeAliasDeclaration(node) || diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt index d2fe1a0d13bc4..76253d8a558fd 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.errors.txt @@ -1,7 +1,11 @@ +anonymousClassExpression.ts(1,1): error TS1344: A label is not allowed here. +anonymousClassExpression.ts(2,34): error TS4082: Default export of the module has or is using private name 'I'. implements.ts(1,1): error TS1344: A label is not allowed here. implements.ts(2,27): error TS4019: Implements clause of exported class 'C' has or is using private name 'I'. interface.ts(1,1): error TS1344: A label is not allowed here. interface.ts(2,28): error TS4022: 'extends' clause of exported interface 'J' has or is using private name 'I'. +namedClassExpression.ts(1,1): error TS1344: A label is not allowed here. +namedClassExpression.ts(2,36): error TS4082: Default export of the module has or is using private name 'I'. typeArgument.ts(1,1): error TS1344: A label is not allowed here. typeArgument.ts(3,33): error TS4022: 'extends' clause of exported interface 'J' has or is using private name 'I'. @@ -30,4 +34,20 @@ typeArgument.ts(3,33): error TS4022: 'extends' clause of exported interface 'J' export interface J extends Base {} ~ !!! error TS4022: 'extends' clause of exported interface 'J' has or is using private name 'I'. + +==== namedClassExpression.ts (2 errors) ==== + label: interface I {} + ~~~~~ +!!! error TS1344: A label is not allowed here. + export default (class C implements I {}); + ~ +!!! error TS4082: Default export of the module has or is using private name 'I'. + +==== anonymousClassExpression.ts (2 errors) ==== + label: interface I {} + ~~~~~ +!!! error TS1344: A label is not allowed here. + export default (class implements I {}); + ~ +!!! error TS4082: Default export of the module has or is using private name 'I'. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js index ef0549f5a388a..64b5720d354cd 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.js @@ -13,6 +13,14 @@ label: interface I {} interface Base {} export interface J extends Base {} +//// [namedClassExpression.ts] +label: interface I {} +export default (class C implements I {}); + +//// [anonymousClassExpression.ts] +label: interface I {} +export default (class implements I {}); + //// [interface.js] label: ; @@ -24,3 +32,11 @@ export class C { //// [typeArgument.js] label: ; export {}; +//// [namedClassExpression.js] +label: ; +export default (class C { +}); +//// [anonymousClassExpression.js] +label: ; +export default (class { +}); diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols index 35d2e3f0d1c46..dd5bdd2561567 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.symbols @@ -29,3 +29,18 @@ export interface J extends Base {} >Base : Symbol(Base, Decl(typeArgument.ts, 0, 21)) >I : Symbol(I, Decl(typeArgument.ts, 0, 6)) +=== namedClassExpression.ts === +label: interface I {} +>I : Symbol(I, Decl(namedClassExpression.ts, 0, 6)) + +export default (class C implements I {}); +>C : Symbol(C, Decl(namedClassExpression.ts, 1, 16)) +>I : Symbol(I, Decl(namedClassExpression.ts, 0, 6)) + +=== anonymousClassExpression.ts === +label: interface I {} +>I : Symbol(I, Decl(anonymousClassExpression.ts, 0, 6)) + +export default (class implements I {}); +>I : Symbol(I, Decl(anonymousClassExpression.ts, 0, 6)) + diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types index 8b6a2c9dfd9d1..d434befb81d68 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitInaccessibleHeritageType.types @@ -20,3 +20,20 @@ label: interface I {} interface Base {} export interface J extends Base {} +=== namedClassExpression.ts === +label: interface I {} +>label : any + +export default (class C implements I {}); +>(class C implements I {}) : typeof C +>class C implements I {} : typeof C +>C : typeof C + +=== anonymousClassExpression.ts === +label: interface I {} +>label : any + +export default (class implements I {}); +>(class implements I {}) : typeof (Anonymous class) +>class implements I {} : typeof (Anonymous class) + diff --git a/tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts b/tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts index b842d263b93a1..a33322aa8fb58 100644 --- a/tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts +++ b/tsc/testdata/tests/cases/compiler/declarationEmitInaccessibleHeritageType.ts @@ -12,3 +12,11 @@ export class C implements I {} label: interface I {} interface Base {} export interface J extends Base {} + +// @filename: namedClassExpression.ts +label: interface I {} +export default (class C implements I {}); + +// @filename: anonymousClassExpression.ts +label: interface I {} +export default (class implements I {});