From 5e0fbc677ac8ed6ac11deb75e4da3245039b2b6e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 30 Oct 2019 13:36:43 -0700 Subject: [PATCH] Add circularity checking during deferred type argument creation (#34791) --- src/compiler/checker.ts | 21 +++++++++++++- src/compiler/diagnosticMessages.json | 8 ++++++ ...ferentialInterfaceAccessNoCrash.errors.txt | 23 +++++++++++++++ ...ularlyReferentialInterfaceAccessNoCrash.js | 14 ++++++++++ ...yReferentialInterfaceAccessNoCrash.symbols | 28 +++++++++++++++++++ ...rlyReferentialInterfaceAccessNoCrash.types | 18 ++++++++++++ ...ularlyReferentialInterfaceAccessNoCrash.ts | 10 +++++++ 7 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.errors.txt create mode 100644 tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.js create mode 100644 tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.symbols create mode 100644 tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.types create mode 100644 tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a8a19743ae1..f30131d10d4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -163,6 +163,7 @@ namespace ts { ImmediateBaseConstraint, EnumTagType, JSDocTypeReference, + ResolvedTypeArguments, } const enum CheckMode { @@ -6560,6 +6561,8 @@ namespace ts { return !!(target).immediateBaseConstraint; case TypeSystemPropertyName.JSDocTypeReference: return !!getSymbolLinks(target as Symbol).resolvedJSDocType; + case TypeSystemPropertyName.ResolvedTypeArguments: + return !!(target as TypeReference).resolvedTypeArguments; } return Debug.assertNever(propertyName); } @@ -10650,12 +10653,28 @@ namespace ts { function getTypeArguments(type: TypeReference): readonly Type[] { if (!type.resolvedTypeArguments) { + if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedTypeArguments)) { + return type.target.localTypeParameters?.map(() => errorType) || emptyArray; + } const node = type.node; const typeArguments = !node ? emptyArray : node.kind === SyntaxKind.TypeReference ? concatenate(type.target.outerTypeParameters, getEffectiveTypeArguments(node, type.target.localTypeParameters!)) : node.kind === SyntaxKind.ArrayType ? [getTypeFromTypeNode(node.elementType)] : map(node.elementTypes, getTypeFromTypeNode); - type.resolvedTypeArguments = type.mapper ? instantiateTypes(typeArguments, type.mapper) : typeArguments; + if (popTypeResolution()) { + type.resolvedTypeArguments = type.mapper ? instantiateTypes(typeArguments, type.mapper) : typeArguments; + } + else { + type.resolvedTypeArguments = type.target.localTypeParameters?.map(() => errorType) || emptyArray; + error( + type.node || currentNode, + type.target.symbol + ? Diagnostics.Type_arguments_for_0_circularly_reference_themselves + : Diagnostics.Tuple_type_arguments_circularly_reference_themselves + , + type.target.symbol && symbolToString(type.target.symbol) + ); + } } return type.resolvedTypeArguments; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 5cb18c1f333..d76219e90a7 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3136,6 +3136,14 @@ "category": "Error", "code": 4108 }, + "Type arguments for '{0}' circularly reference themselves.": { + "category": "Error", + "code": 4109 + }, + "Tuple type arguments circularly reference themselves.": { + "category": "Error", + "code": 4110 + }, "The current host does not support the '{0}' option.": { "category": "Error", diff --git a/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.errors.txt b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.errors.txt new file mode 100644 index 00000000000..4ae7ebf3d75 --- /dev/null +++ b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts(1,12): error TS4109: Type arguments for 'Mx' circularly reference themselves. +tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts(8,16): error TS4109: Type arguments for 'Array' circularly reference themselves. +tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts(10,18): error TS4110: Tuple type arguments circularly reference themselves. + + +==== tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts (3 errors) ==== + type Mxs = Mx<'list', Mxs['p1']>; + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS4109: Type arguments for 'Mx' circularly reference themselves. + + interface Mx { + p1: T; + p2: K; + } + + type ArrElem = ['list', ArrElem[number][0]][]; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS4109: Type arguments for 'Array' circularly reference themselves. + + type TupleElem = [['list', TupleElem[0][0]]]; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS4110: Tuple type arguments circularly reference themselves. + \ No newline at end of file diff --git a/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.js b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.js new file mode 100644 index 00000000000..af94b23a687 --- /dev/null +++ b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.js @@ -0,0 +1,14 @@ +//// [circularlyReferentialInterfaceAccessNoCrash.ts] +type Mxs = Mx<'list', Mxs['p1']>; + +interface Mx { + p1: T; + p2: K; +} + +type ArrElem = ['list', ArrElem[number][0]][]; + +type TupleElem = [['list', TupleElem[0][0]]]; + + +//// [circularlyReferentialInterfaceAccessNoCrash.js] diff --git a/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.symbols b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.symbols new file mode 100644 index 00000000000..3335d53a67b --- /dev/null +++ b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.symbols @@ -0,0 +1,28 @@ +=== tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts === +type Mxs = Mx<'list', Mxs['p1']>; +>Mxs : Symbol(Mxs, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 0, 0)) +>Mx : Symbol(Mx, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 0, 33)) +>Mxs : Symbol(Mxs, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 0, 0)) + +interface Mx { +>Mx : Symbol(Mx, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 0, 33)) +>T : Symbol(T, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 2, 13)) +>K : Symbol(K, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 2, 15)) + + p1: T; +>p1 : Symbol(Mx.p1, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 2, 20)) +>T : Symbol(T, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 2, 13)) + + p2: K; +>p2 : Symbol(Mx.p2, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 3, 8)) +>K : Symbol(K, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 2, 15)) +} + +type ArrElem = ['list', ArrElem[number][0]][]; +>ArrElem : Symbol(ArrElem, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 5, 1)) +>ArrElem : Symbol(ArrElem, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 5, 1)) + +type TupleElem = [['list', TupleElem[0][0]]]; +>TupleElem : Symbol(TupleElem, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 7, 46)) +>TupleElem : Symbol(TupleElem, Decl(circularlyReferentialInterfaceAccessNoCrash.ts, 7, 46)) + diff --git a/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.types b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.types new file mode 100644 index 00000000000..37dc9060f0b --- /dev/null +++ b/tests/baselines/reference/circularlyReferentialInterfaceAccessNoCrash.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts === +type Mxs = Mx<'list', Mxs['p1']>; +>Mxs : Mxs + +interface Mx { + p1: T; +>p1 : T + + p2: K; +>p2 : K +} + +type ArrElem = ['list', ArrElem[number][0]][]; +>ArrElem : ArrElem + +type TupleElem = [['list', TupleElem[0][0]]]; +>TupleElem : TupleElem + diff --git a/tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts b/tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts new file mode 100644 index 00000000000..ea1bf43db33 --- /dev/null +++ b/tests/cases/compiler/circularlyReferentialInterfaceAccessNoCrash.ts @@ -0,0 +1,10 @@ +type Mxs = Mx<'list', Mxs['p1']>; + +interface Mx { + p1: T; + p2: K; +} + +type ArrElem = ['list', ArrElem[number][0]][]; + +type TupleElem = [['list', TupleElem[0][0]]];