From 088fdf6efecc82f6b445a126687863f5f2a5b2c0 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 17 Feb 2023 21:31:45 +0200 Subject: [PATCH] fix(51202): Circular instantiation expression crashing IDEs (#51214) --- src/compiler/checker.ts | 14 ++ .../reference/arrayTypeOfTypeOf.types | 4 +- .../circularInstantiationExpression.js | 11 ++ .../circularInstantiationExpression.symbols | 12 ++ .../circularInstantiationExpression.types | 11 ++ .../expressionWithJSDocTypeArguments.types | 16 +-- .../reference/instantiationExpressions.types | 22 ++-- ...foCircularInstantiationExpression.baseline | 124 ++++++++++++++++++ .../reference/selfReferentialFunctionType.js | 14 ++ .../selfReferentialFunctionType.symbols | 23 ++++ .../selfReferentialFunctionType.types | 15 +++ .../circularInstantiationExpression.ts | 3 + .../compiler/selfReferentialFunctionType.ts | 6 + ...uickInfoCircularInstantiationExpression.ts | 6 + 14 files changed, 260 insertions(+), 21 deletions(-) create mode 100644 tests/baselines/reference/circularInstantiationExpression.js create mode 100644 tests/baselines/reference/circularInstantiationExpression.symbols create mode 100644 tests/baselines/reference/circularInstantiationExpression.types create mode 100644 tests/baselines/reference/quickInfoCircularInstantiationExpression.baseline create mode 100644 tests/baselines/reference/selfReferentialFunctionType.js create mode 100644 tests/baselines/reference/selfReferentialFunctionType.symbols create mode 100644 tests/baselines/reference/selfReferentialFunctionType.types create mode 100644 tests/cases/compiler/circularInstantiationExpression.ts create mode 100644 tests/cases/compiler/selfReferentialFunctionType.ts create mode 100644 tests/cases/fourslash/quickInfoCircularInstantiationExpression.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e123c5ccaea..70bad376792 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6677,6 +6677,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } else { + const isInstantiationExpressionType = !!(getObjectFlags(type) & ObjectFlags.InstantiationExpressionType); + if (isInstantiationExpressionType) { + const instantiationExpressionType = type as InstantiationExpressionType; + if (isTypeQueryNode(instantiationExpressionType.node)) { + const typeNode = serializeExistingTypeNode(context, instantiationExpressionType.node); + if (typeNode) { + return typeNode; + } + } + if (context.visitedTypes?.has(typeId)) { + return createElidedInformationPlaceholder(context); + } + return visitAndTransformType(type, createTypeNodeFromObjectType); + } // Anonymous types without a symbol are never circular. return createTypeNodeFromObjectType(type); } diff --git a/tests/baselines/reference/arrayTypeOfTypeOf.types b/tests/baselines/reference/arrayTypeOfTypeOf.types index 60eec5fbba2..e937c6746a1 100644 --- a/tests/baselines/reference/arrayTypeOfTypeOf.types +++ b/tests/baselines/reference/arrayTypeOfTypeOf.types @@ -14,11 +14,11 @@ var xs2: typeof Array; >Array : ArrayConstructor var xs3: typeof Array; ->xs3 : { (arrayLength: number): number[]; (...items: number[]): number[]; new (arrayLength: number): number[]; new (...items: number[]): number[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; } +>xs3 : typeof Array >Array : ArrayConstructor var xs4: typeof Array; ->xs4 : { (arrayLength: number): number[]; (...items: number[]): number[]; new (arrayLength: number): number[]; new (...items: number[]): number[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; } +>xs4 : typeof Array >Array : ArrayConstructor >x : number diff --git a/tests/baselines/reference/circularInstantiationExpression.js b/tests/baselines/reference/circularInstantiationExpression.js new file mode 100644 index 00000000000..3b1a07a4c09 --- /dev/null +++ b/tests/baselines/reference/circularInstantiationExpression.js @@ -0,0 +1,11 @@ +//// [circularInstantiationExpression.ts] +declare function foo(t: T): typeof foo; +foo(""); + + +//// [circularInstantiationExpression.js] +foo(""); + + +//// [circularInstantiationExpression.d.ts] +declare function foo(t: T): typeof foo; diff --git a/tests/baselines/reference/circularInstantiationExpression.symbols b/tests/baselines/reference/circularInstantiationExpression.symbols new file mode 100644 index 00000000000..7936cf82aaa --- /dev/null +++ b/tests/baselines/reference/circularInstantiationExpression.symbols @@ -0,0 +1,12 @@ +=== tests/cases/compiler/circularInstantiationExpression.ts === +declare function foo(t: T): typeof foo; +>foo : Symbol(foo, Decl(circularInstantiationExpression.ts, 0, 0)) +>T : Symbol(T, Decl(circularInstantiationExpression.ts, 0, 21)) +>t : Symbol(t, Decl(circularInstantiationExpression.ts, 0, 24)) +>T : Symbol(T, Decl(circularInstantiationExpression.ts, 0, 21)) +>foo : Symbol(foo, Decl(circularInstantiationExpression.ts, 0, 0)) +>T : Symbol(T, Decl(circularInstantiationExpression.ts, 0, 21)) + +foo(""); +>foo : Symbol(foo, Decl(circularInstantiationExpression.ts, 0, 0)) + diff --git a/tests/baselines/reference/circularInstantiationExpression.types b/tests/baselines/reference/circularInstantiationExpression.types new file mode 100644 index 00000000000..696329f593a --- /dev/null +++ b/tests/baselines/reference/circularInstantiationExpression.types @@ -0,0 +1,11 @@ +=== tests/cases/compiler/circularInstantiationExpression.ts === +declare function foo(t: T): typeof foo; +>foo : (t: T) => typeof foo +>t : T +>foo : (t: T) => typeof foo + +foo(""); +>foo("") : (t: string) => any +>foo : (t: T) => (t: T) => any +>"" : "" + diff --git a/tests/baselines/reference/expressionWithJSDocTypeArguments.types b/tests/baselines/reference/expressionWithJSDocTypeArguments.types index b3b1cdf96c6..9733f5a1b11 100644 --- a/tests/baselines/reference/expressionWithJSDocTypeArguments.types +++ b/tests/baselines/reference/expressionWithJSDocTypeArguments.types @@ -33,19 +33,19 @@ const ComeOnFoo = foo; >foo : (x: T) => T type TWhatFoo = typeof foo; ->TWhatFoo : (x: any) => any +>TWhatFoo : typeof foo >foo : (x: T) => T type THuhFoo = typeof foo; ->THuhFoo : (x: string | null) => string | null +>THuhFoo : typeof foo >foo : (x: T) => T type TNopeFoo = typeof foo; ->TNopeFoo : (x: string | null) => string | null +>TNopeFoo : typeof foo >foo : (x: T) => T type TComeOnFoo = typeof foo; ->TComeOnFoo : (x: string | null) => string | null +>TComeOnFoo : typeof foo<(string | null) | null> >foo : (x: T) => T const WhatBar = Bar; @@ -69,18 +69,18 @@ const ComeOnBar = Bar; >Bar : typeof Bar type TWhatBar = typeof Bar; ->TWhatBar : { new (x: any): Bar; prototype: Bar; } +>TWhatBar : typeof Bar >Bar : typeof Bar type THuhBar = typeof Bar; ->THuhBar : { new (x: string | null): Bar; prototype: Bar; } +>THuhBar : typeof Bar >Bar : typeof Bar type TNopeBar = typeof Bar; ->TNopeBar : { new (x: string | null): Bar; prototype: Bar; } +>TNopeBar : typeof Bar >Bar : typeof Bar type TComeOnBar = typeof Bar; ->TComeOnBar : { new (x: string | null): Bar; prototype: Bar; } +>TComeOnBar : typeof Bar<(string | null) | null> >Bar : typeof Bar diff --git a/tests/baselines/reference/instantiationExpressions.types b/tests/baselines/reference/instantiationExpressions.types index 75fe72fc441..7b85e85e82c 100644 --- a/tests/baselines/reference/instantiationExpressions.types +++ b/tests/baselines/reference/instantiationExpressions.types @@ -41,15 +41,15 @@ type T10 = typeof fx<>; // Error >fx : { (x: T): T; (x: T, n: number): T; (t: [T, U]): [T, U]; } type T11 = typeof fx; // { (x: string): string; (x: string, n: number): string; } ->T11 : { (x: string): string; (x: string, n: number): string; } +>T11 : typeof fx >fx : { (x: T): T; (x: T, n: number): T; (t: [T, U]): [T, U]; } type T12 = typeof fx; // (t: [string, number]) => [string, number] ->T12 : (t: [string, number]) => [string, number] +>T12 : typeof fx >fx : { (x: T): T; (x: T, n: number): T; (t: [T, U]): [T, U]; } type T13 = typeof fx; // Error ->T13 : {} +>T13 : typeof fx >fx : { (x: T): T; (x: T, n: number): T; (t: [T, U]): [T, U]; } function f2() { @@ -76,11 +76,11 @@ type T20 = typeof Array<>; // Error >Array : ArrayConstructor type T21 = typeof Array; // new (...) => string[] ->T21 : { (arrayLength: number): string[]; (...items: string[]): string[]; new (arrayLength: number): string[]; new (...items: string[]): string[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; } +>T21 : typeof Array >Array : ArrayConstructor type T22 = typeof Array; // Error ->T22 : { isArray(arg: any): arg is any[]; readonly prototype: any[]; } +>T22 : typeof Array >Array : ArrayConstructor declare class C { @@ -439,7 +439,7 @@ function makeBox(value: T) { } type BoxFunc = typeof makeBox; // (value: T) => { value: T } ->BoxFunc : (value: T) => { value: T; } +>BoxFunc : typeof makeBox >makeBox : (value: T) => { value: T; } type StringBoxFunc = BoxFunc; // (value: string) => { value: string } @@ -469,7 +469,7 @@ declare const g1: { } type T30 = typeof g1; // { (a: V) => { a: V }; new (b: V) => { b: V }; } ->T30 : { (a: V): { a: V; }; new (b: V): { b: V; }; } +>T30 : typeof g1 >g1 : { (a: T): { a: T; }; new (b: U): { b: U; }; } type T31 = ReturnType>; // { a: A } @@ -489,11 +489,11 @@ declare const g2: { } type T40 = typeof g2; // Error ->T40 : { (a: U): U; new (b: T): T; } +>T40 : typeof g2 >g2 : { (a: T): T; new (b: T): T; } type T41 = typeof g2; // Error ->T41 : { (a: T): T; new (b: U): U; } +>T41 : typeof g2 >g2 : { (a: T): T; new (b: T): T; } declare const g3: { @@ -507,10 +507,10 @@ declare const g3: { } type T50 = typeof g3; // (a: U) => U ->T50 : (a: U) => U +>T50 : typeof g3 >g3 : { (a: T): T; new (b: T): T; } type T51 = typeof g3; // (b: U) => U ->T51 : new (b: U) => U +>T51 : typeof g3 >g3 : { (a: T): T; new (b: T): T; } diff --git a/tests/baselines/reference/quickInfoCircularInstantiationExpression.baseline b/tests/baselines/reference/quickInfoCircularInstantiationExpression.baseline new file mode 100644 index 00000000000..d4d4c097b99 --- /dev/null +++ b/tests/baselines/reference/quickInfoCircularInstantiationExpression.baseline @@ -0,0 +1,124 @@ +=== /tests/cases/fourslash/quickInfoCircularInstantiationExpression.ts === +// declare function foo(t: T): typeof foo; +// foo(""); +// ^^^ +// | ---------------------------------------------------------------------- +// | function foo(t: string): (t: string) => ... +// | ---------------------------------------------------------------------- + +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoCircularInstantiationExpression.ts", + "position": 46, + "name": "" + }, + "item": { + "kind": "function", + "kindModifiers": "declare", + "textSpan": { + "start": 46, + "length": 3 + }, + "displayParts": [ + { + "text": "function", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "foo", + "kind": "functionName" + }, + { + "text": "<", + "kind": "punctuation" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ">", + "kind": "punctuation" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "t", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "t", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "=>", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "...", + "kind": "text" + } + ], + "documentation": [] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/selfReferentialFunctionType.js b/tests/baselines/reference/selfReferentialFunctionType.js new file mode 100644 index 00000000000..5b8a4e1d985 --- /dev/null +++ b/tests/baselines/reference/selfReferentialFunctionType.js @@ -0,0 +1,14 @@ +//// [selfReferentialFunctionType.ts] +declare function f(args: typeof f): T; +declare function g(args: T): T; +declare function h(): typeof h; + + +//// [selfReferentialFunctionType.js] +"use strict"; + + +//// [selfReferentialFunctionType.d.ts] +declare function f(args: typeof f): T; +declare function g(args: T): T; +declare function h(): typeof h; diff --git a/tests/baselines/reference/selfReferentialFunctionType.symbols b/tests/baselines/reference/selfReferentialFunctionType.symbols new file mode 100644 index 00000000000..be32a861493 --- /dev/null +++ b/tests/baselines/reference/selfReferentialFunctionType.symbols @@ -0,0 +1,23 @@ +=== tests/cases/compiler/selfReferentialFunctionType.ts === +declare function f(args: typeof f): T; +>f : Symbol(f, Decl(selfReferentialFunctionType.ts, 0, 0)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 0, 19)) +>args : Symbol(args, Decl(selfReferentialFunctionType.ts, 0, 22)) +>f : Symbol(f, Decl(selfReferentialFunctionType.ts, 0, 0)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 0, 19)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 0, 19)) + +declare function g(args: T): T; +>g : Symbol(g, Decl(selfReferentialFunctionType.ts, 0, 44)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 1, 19)) +>g : Symbol(g, Decl(selfReferentialFunctionType.ts, 0, 44)) +>args : Symbol(args, Decl(selfReferentialFunctionType.ts, 1, 33)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 1, 19)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 1, 19)) + +declare function h(): typeof h; +>h : Symbol(h, Decl(selfReferentialFunctionType.ts, 1, 45)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 2, 19)) +>h : Symbol(h, Decl(selfReferentialFunctionType.ts, 1, 45)) +>T : Symbol(T, Decl(selfReferentialFunctionType.ts, 2, 19)) + diff --git a/tests/baselines/reference/selfReferentialFunctionType.types b/tests/baselines/reference/selfReferentialFunctionType.types new file mode 100644 index 00000000000..80c85f57a95 --- /dev/null +++ b/tests/baselines/reference/selfReferentialFunctionType.types @@ -0,0 +1,15 @@ +=== tests/cases/compiler/selfReferentialFunctionType.ts === +declare function f(args: typeof f): T; +>f : (args: typeof f) => T +>args : typeof f +>f : (args: typeof f) => T + +declare function g(args: T): T; +>g : (args: T) => T +>g : (args: T) => T +>args : T + +declare function h(): typeof h; +>h : () => typeof h +>h : () => typeof h + diff --git a/tests/cases/compiler/circularInstantiationExpression.ts b/tests/cases/compiler/circularInstantiationExpression.ts new file mode 100644 index 00000000000..fc42ae08ff4 --- /dev/null +++ b/tests/cases/compiler/circularInstantiationExpression.ts @@ -0,0 +1,3 @@ +// @declaration: true +declare function foo(t: T): typeof foo; +foo(""); diff --git a/tests/cases/compiler/selfReferentialFunctionType.ts b/tests/cases/compiler/selfReferentialFunctionType.ts new file mode 100644 index 00000000000..ad412795755 --- /dev/null +++ b/tests/cases/compiler/selfReferentialFunctionType.ts @@ -0,0 +1,6 @@ +// @strict: true +// @declaration: true + +declare function f(args: typeof f): T; +declare function g(args: T): T; +declare function h(): typeof h; diff --git a/tests/cases/fourslash/quickInfoCircularInstantiationExpression.ts b/tests/cases/fourslash/quickInfoCircularInstantiationExpression.ts new file mode 100644 index 00000000000..2010285d03e --- /dev/null +++ b/tests/cases/fourslash/quickInfoCircularInstantiationExpression.ts @@ -0,0 +1,6 @@ +/// + +////declare function foo(t: T): typeof foo; +/////**/foo(""); + +verify.baselineQuickInfo();