From ecc86b3049adf42ca66feb2e54f3fb043f0ff32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 27 Jan 2025 00:30:34 +0100 Subject: [PATCH 1/4] Fixed instantiation expression type param leak in body-less arrows --- src/compiler/checker.ts | 14 +- ...tionExpressionNoTypeParameterLeak1.symbols | 81 ++++++++++ ...iationExpressionNoTypeParameterLeak1.types | 153 ++++++++++++++++++ ...antiationExpressionNoTypeParameterLeak1.ts | 24 +++ 4 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols create mode 100644 tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types create mode 100644 tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ded639b7a09cf..8a0f0f25bca80 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20259,16 +20259,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const firstIdentifier = getFirstIdentifier(entityName); if (!isThisIdentifier(firstIdentifier)) { // Don't attempt to analyze typeof this.xxx const firstIdentifierSymbol = getResolvedSymbol(firstIdentifier); - const tpDeclaration = tp.symbol.declarations![0]; // There is exactly one declaration, otherwise `containsReference` is not called - const tpScope = tpDeclaration.kind === SyntaxKind.TypeParameter ? tpDeclaration.parent : // Type parameter is a regular type parameter, e.g. foo - tp.isThisType ? tpDeclaration : // Type parameter is the this type, and its declaration is the class declaration. - undefined; // Type parameter's declaration was unrecognized, e.g. comes from JSDoc annotation. + const tpScope = getTypeParameterScope(); if (firstIdentifierSymbol.declarations && tpScope) { return some(firstIdentifierSymbol.declarations, idDecl => isNodeDescendantOf(idDecl, tpScope)) || some((node as TypeQueryNode).typeArguments, containsReference); } } return true; + case SyntaxKind.ExpressionWithTypeArguments: + return isNodeDescendantOf(node, getTypeParameterScope()); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: return !(node as FunctionLikeDeclaration).type && !!(node as FunctionLikeDeclaration).body || @@ -20278,6 +20277,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return !!forEachChild(node, containsReference); } + + function getTypeParameterScope() { + const tpDeclaration = tp.symbol.declarations![0]; // There is exactly one declaration, otherwise `containsReference` is not called + return tpDeclaration.kind === SyntaxKind.TypeParameter ? tpDeclaration.parent : // Type parameter is a regular type parameter, e.g. foo + tp.isThisType ? tpDeclaration : // Type parameter is the this type, and its declaration is the class declaration. + undefined; // Type parameter's declaration was unrecognized, e.g. comes from JSDoc annotation. + } } function getHomomorphicTypeVariable(type: MappedType) { diff --git a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols new file mode 100644 index 0000000000000..7519e8019cc5d --- /dev/null +++ b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols @@ -0,0 +1,81 @@ +//// [tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts] //// + +=== instantiationExpressionNoTypeParameterLeak1.ts === +// https://github.com/microsoft/TypeScript/issues/61041 + +export const test1 = (g: (x: X) => X) => g; +>test1 : Symbol(test1, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 12)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 26)) +>A : Symbol(A, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 30)) +>x : Symbol(x, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 33)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 22)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 26)) + +export const output1 = test1((y: number) => 1); +>output1 : Symbol(output1, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 3, 12)) +>test1 : Symbol(test1, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 2, 12)) +>y : Symbol(y, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 3, 38)) + +output1(1); +>output1 : Symbol(output1, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 3, 12)) + +export function test2(g: (x: X) => X) { +>test2 : Symbol(test2, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 4, 11)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 6, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 6, 25)) +>A : Symbol(A, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 6, 29)) +>x : Symbol(x, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 6, 32)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 6, 22)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 6, 22)) + + return g; +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 6, 25)) +} +export const output2 = test2((y: number) => 1); +>output2 : Symbol(output2, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 9, 12)) +>test2 : Symbol(test2, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 4, 11)) +>y : Symbol(y, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 9, 38)) + +output2(1); +>output2 : Symbol(output2, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 9, 12)) + +export const test3 = (g: () => (x: X) => X) => g(); +>test3 : Symbol(test3, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 12)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 26)) +>A : Symbol(A, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 30)) +>x : Symbol(x, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 39)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 22)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 26)) + +export const output3 = test3(() => (y: number) => 1); +>output3 : Symbol(output3, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 13, 12)) +>test3 : Symbol(test3, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 12, 12)) +>y : Symbol(y, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 13, 44)) + +output3(1); +>output3 : Symbol(output3, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 13, 12)) + +export function test4(g: () => (x: X) => X) { +>test4 : Symbol(test4, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 14, 11)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 16, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 16, 25)) +>A : Symbol(A, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 16, 29)) +>x : Symbol(x, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 16, 38)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 16, 22)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 16, 22)) + + return g(); +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 16, 25)) +} +export const output4 = test4(() => (y: number) => 1); +>output4 : Symbol(output4, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 19, 12)) +>test4 : Symbol(test4, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 14, 11)) +>y : Symbol(y, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 19, 44)) + +output4(1); +>output4 : Symbol(output4, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 19, 12)) + diff --git a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types new file mode 100644 index 0000000000000..ec64af8b3781d --- /dev/null +++ b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types @@ -0,0 +1,153 @@ +//// [tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts] //// + +=== instantiationExpressionNoTypeParameterLeak1.ts === +// https://github.com/microsoft/TypeScript/issues/61041 + +export const test1 = (g: (x: X) => X) => g; +>test1 : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>(g: (x: X) => X) => g : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ +>x : X +> : ^ +>g : (x: X) => X +> : ^ ^^ ^^^^^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ + +export const output1 = test1((y: number) => 1); +>output1 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test1((y: number) => 1) : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test1 : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>(y: number) => 1 : (y: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>y : number +> : ^^^^^^ +>1 : 1 +> : ^ + +output1(1); +>output1(1) : number +> : ^^^^^^ +>output1 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + +export function test2(g: (x: X) => X) { +>test2 : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ +>x : X +> : ^ + + return g; +>g : (x: X) => X +> : ^ ^^ ^^^^^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ +} +export const output2 = test2((y: number) => 1); +>output2 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test2((y: number) => 1) : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test2 : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>(y: number) => 1 : (y: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>y : number +> : ^^^^^^ +>1 : 1 +> : ^ + +output2(1); +>output2(1) : number +> : ^^^^^^ +>output2 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + +export const test3 = (g: () => (x: X) => X) => g(); +>test3 : (g: () => (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>(g: () => (x: X) => X) => g() : (g: () => (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>g : () => (x: X) => X +> : ^ ^^^^^^^ +>x : X +> : ^ +>g() : (x: X) => X +> : ^ ^^ ^^^^^ +>g : () => (x: X) => X +> : ^ ^^^^^^^ + +export const output3 = test3(() => (y: number) => 1); +>output3 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test3(() => (y: number) => 1) : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test3 : (g: () => (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>() => (y: number) => 1 : () => (y: number) => number +> : ^^^^^^^ ^^ ^^^^^^^^^^^ +>(y: number) => 1 : (y: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>y : number +> : ^^^^^^ +>1 : 1 +> : ^ + +output3(1); +>output3(1) : number +> : ^^^^^^ +>output3 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + +export function test4(g: () => (x: X) => X) { +>test4 : (g: () => (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>g : () => (x: X) => X +> : ^ ^^^^^^^ +>x : X +> : ^ + + return g(); +>g() : (x: X) => X +> : ^ ^^ ^^^^^ +>g : () => (x: X) => X +> : ^ ^^^^^^^ +} +export const output4 = test4(() => (y: number) => 1); +>output4 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test4(() => (y: number) => 1) : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test4 : (g: () => (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>() => (y: number) => 1 : () => (y: number) => number +> : ^^^^^^^ ^^ ^^^^^^^^^^^ +>(y: number) => 1 : (y: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>y : number +> : ^^^^^^ +>1 : 1 +> : ^ + +output4(1); +>output4(1) : number +> : ^^^^^^ +>output4 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + diff --git a/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts b/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts new file mode 100644 index 0000000000000..6984f6c2439e7 --- /dev/null +++ b/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts @@ -0,0 +1,24 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/61041 + +export const test1 = (g: (x: X) => X) => g; +export const output1 = test1((y: number) => 1); +output1(1); + +export function test2(g: (x: X) => X) { + return g; +} +export const output2 = test2((y: number) => 1); +output2(1); + +export const test3 = (g: () => (x: X) => X) => g(); +export const output3 = test3(() => (y: number) => 1); +output3(1); + +export function test4(g: () => (x: X) => X) { + return g(); +} +export const output4 = test4(() => (y: number) => 1); +output4(1); From b0c4532707bb4f53546c2ee629c6f04add9375c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 27 Jan 2025 10:41:55 +0100 Subject: [PATCH 2/4] add extra test case --- ...tionExpressionNoTypeParameterLeak1.symbols | 18 +++++++++++ ...iationExpressionNoTypeParameterLeak1.types | 32 +++++++++++++++++++ ...antiationExpressionNoTypeParameterLeak1.ts | 4 +++ 3 files changed, 54 insertions(+) diff --git a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols index 7519e8019cc5d..8e631731fdd06 100644 --- a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols +++ b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols @@ -79,3 +79,21 @@ export const output4 = test4(() => (y: number) => 1); output4(1); >output4 : Symbol(output4, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 19, 12)) +export declare function test5(g: (x: X) => X): typeof g; +>test5 : Symbol(test5, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 20, 11)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 22, 30)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 22, 33)) +>A : Symbol(A, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 22, 37)) +>x : Symbol(x, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 22, 40)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 22, 30)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 22, 30)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 22, 33)) + +export const output5 = test5((y: number) => 1); +>output5 : Symbol(output5, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 23, 12)) +>test5 : Symbol(test5, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 20, 11)) +>y : Symbol(y, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 23, 38)) + +output5(1); +>output5 : Symbol(output5, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 23, 12)) + diff --git a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types index ec64af8b3781d..37f5f3c6af693 100644 --- a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types +++ b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types @@ -151,3 +151,35 @@ output4(1); >1 : 1 > : ^ +export declare function test5(g: (x: X) => X): typeof g; +>test5 : (g: (x: X) => X) => typeof g +> : ^ ^^ ^^ ^^^^^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ +>x : X +> : ^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ + +export const output5 = test5((y: number) => 1); +>output5 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test5((y: number) => 1) : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test5 : (g: (x: X) => X) => typeof g +> : ^ ^^ ^^ ^^^^^ +>(y: number) => 1 : (y: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>y : number +> : ^^^^^^ +>1 : 1 +> : ^ + +output5(1); +>output5(1) : number +> : ^^^^^^ +>output5 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + diff --git a/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts b/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts index 6984f6c2439e7..6c54c6904b569 100644 --- a/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts +++ b/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts @@ -22,3 +22,7 @@ export function test4(g: () => (x: X) => X) { } export const output4 = test4(() => (y: number) => 1); output4(1); + +export declare function test5(g: (x: X) => X): typeof g; +export const output5 = test5((y: number) => 1); +output5(1); From e105b89efaf00231a96bd770bb81b15817492c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 27 Jan 2025 11:26:49 +0100 Subject: [PATCH 3/4] add yet another test --- ...tionExpressionNoTypeParameterLeak1.symbols | 19 ++++++++++ ...iationExpressionNoTypeParameterLeak1.types | 36 +++++++++++++++++++ ...antiationExpressionNoTypeParameterLeak1.ts | 4 +++ 3 files changed, 59 insertions(+) diff --git a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols index 8e631731fdd06..4f2fbbebdb1a6 100644 --- a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols +++ b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.symbols @@ -97,3 +97,22 @@ export const output5 = test5((y: number) => 1); output5(1); >output5 : Symbol(output5, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 23, 12)) +export const test6 = (g: (x: X) => X) => g; +>test6 : Symbol(test6, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 12)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 26)) +>A : Symbol(A, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 30)) +>x : Symbol(x, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 33)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 22)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 22)) +>g : Symbol(g, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 26)) +>X : Symbol(X, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 22)) + +export const output6 = test6((y: number) => 1); +>output6 : Symbol(output6, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 27, 12)) +>test6 : Symbol(test6, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 26, 12)) +>y : Symbol(y, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 27, 38)) + +output6(1); +>output6 : Symbol(output6, Decl(instantiationExpressionNoTypeParameterLeak1.ts, 27, 12)) + diff --git a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types index 37f5f3c6af693..9569cbc5e7b25 100644 --- a/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types +++ b/tests/baselines/reference/instantiationExpressionNoTypeParameterLeak1.types @@ -183,3 +183,39 @@ output5(1); >1 : 1 > : ^ +export const test6 = (g: (x: X) => X) => g; +>test6 : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>(g: (x: X) => X) => g : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ +>x : X +> : ^ +>g : (x: X) => X +> : ^ ^^ ^^^^^ +>g : (x: X) => X +> : ^ ^^ ^^ ^^^^^ + +export const output6 = test6((y: number) => 1); +>output6 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test6((y: number) => 1) : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>test6 : (g: (x: X) => X) => (x: X) => X +> : ^ ^^ ^^ ^^^^^^ ^^ ^^^^^ +>(y: number) => 1 : (y: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>y : number +> : ^^^^^^ +>1 : 1 +> : ^ + +output6(1); +>output6(1) : number +> : ^^^^^^ +>output6 : (x: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + diff --git a/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts b/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts index 6c54c6904b569..9f16dee9ebc8e 100644 --- a/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts +++ b/tests/cases/compiler/instantiationExpressionNoTypeParameterLeak1.ts @@ -26,3 +26,7 @@ output4(1); export declare function test5(g: (x: X) => X): typeof g; export const output5 = test5((y: number) => 1); output5(1); + +export const test6 = (g: (x: X) => X) => g; +export const output6 = test6((y: number) => 1); +output6(1); From 4069c6bdaf8bb8831d242d6e9bc21d5d7f0d11c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 27 Jan 2025 11:43:02 +0100 Subject: [PATCH 4/4] reuse some of the existing logic --- src/compiler/checker.ts | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8a0f0f25bca80..0616d202d96da 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20254,20 +20254,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.Identifier: return !tp.isThisType && isPartOfTypeNode(node) && maybeTypeParameterReference(node) && getTypeFromTypeNodeWorker(node as TypeNode) === tp; // use worker because we're looking for === equality - case SyntaxKind.TypeQuery: - const entityName = (node as TypeQueryNode).exprName; - const firstIdentifier = getFirstIdentifier(entityName); - if (!isThisIdentifier(firstIdentifier)) { // Don't attempt to analyze typeof this.xxx - const firstIdentifierSymbol = getResolvedSymbol(firstIdentifier); - const tpScope = getTypeParameterScope(); - if (firstIdentifierSymbol.declarations && tpScope) { - return some(firstIdentifierSymbol.declarations, idDecl => isNodeDescendantOf(idDecl, tpScope)) || - some((node as TypeQueryNode).typeArguments, containsReference); - } - } - return true; - case SyntaxKind.ExpressionWithTypeArguments: - return isNodeDescendantOf(node, getTypeParameterScope()); + case SyntaxKind.TypeQuery: { + const { exprName, typeArguments } = node as TypeQueryNode; + return entityNameWithTypeArgumentsContainsReference(exprName, typeArguments); + } + case SyntaxKind.ExpressionWithTypeArguments: { + const { expression, typeArguments } = node as ExpressionWithTypeArguments; + return isEntityName(expression) ? entityNameWithTypeArgumentsContainsReference(expression, typeArguments) : isNodeDescendantOf(node, getTypeParameterScope()); + } case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: return !(node as FunctionLikeDeclaration).type && !!(node as FunctionLikeDeclaration).body || @@ -20278,6 +20272,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!forEachChild(node, containsReference); } + function entityNameWithTypeArgumentsContainsReference(entityName: EntityName, typeArguments: NodeArray | undefined) { + const firstIdentifier = getFirstIdentifier(entityName); + if (isThisIdentifier(firstIdentifier)) { // Don't attempt to analyze typeof this.xxx + return true; + } + const firstIdentifierSymbol = getResolvedSymbol(firstIdentifier); + const tpScope = getTypeParameterScope(); + if (!firstIdentifierSymbol.declarations || !tpScope) { + return true; + } + return some(firstIdentifierSymbol.declarations, idDecl => isNodeDescendantOf(idDecl, tpScope)) || + some(typeArguments, containsReference); + } + function getTypeParameterScope() { const tpDeclaration = tp.symbol.declarations![0]; // There is exactly one declaration, otherwise `containsReference` is not called return tpDeclaration.kind === SyntaxKind.TypeParameter ? tpDeclaration.parent : // Type parameter is a regular type parameter, e.g. foo