Skip to content

Commit 413176b

Browse files
committed
Use symbols instead of types in infinite instantiation detection
1 parent f531ff8 commit 413176b

File tree

2 files changed

+70
-64
lines changed

2 files changed

+70
-64
lines changed

src/compiler/checker.ts

Lines changed: 64 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ module ts {
14721472
return appendParentTypeArgumentsAndSymbolName(symbol);
14731473
}
14741474

1475-
function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, typeStack?: Type[]) {
1475+
function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) {
14761476
let globalFlagsToPass = globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike;
14771477
return writeType(type, globalFlags);
14781478

@@ -1557,49 +1557,54 @@ module ts {
15571557
}
15581558

15591559
function writeAnonymousType(type: ObjectType, flags: TypeFormatFlags) {
1560-
// Always use 'typeof T' for type of class, enum, and module objects
1561-
if (type.symbol && type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
1562-
writeTypeofSymbol(type, flags);
1563-
}
1564-
// Use 'typeof T' for types of functions and methods that circularly reference themselves
1565-
else if (shouldWriteTypeOfFunctionSymbol()) {
1566-
writeTypeofSymbol(type, flags);
1567-
}
1568-
else if (typeStack && contains(typeStack, type)) {
1569-
// If type is an anonymous type literal in a type alias declaration, use type alias name
1570-
let typeAlias = getTypeAliasForTypeLiteral(type);
1571-
if (typeAlias) {
1572-
// The specified symbol flags need to be reinterpreted as type flags
1573-
buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
1560+
let symbol = type.symbol;
1561+
if (symbol) {
1562+
// Always use 'typeof T' for type of class, enum, and module objects
1563+
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
1564+
writeTypeofSymbol(type, flags);
1565+
}
1566+
else if (shouldWriteTypeOfFunctionSymbol()) {
1567+
writeTypeofSymbol(type, flags);
1568+
}
1569+
else if (contains(symbolStack, symbol)) {
1570+
// If type is an anonymous type literal in a type alias declaration, use type alias name
1571+
let typeAlias = getTypeAliasForTypeLiteral(type);
1572+
if (typeAlias) {
1573+
// The specified symbol flags need to be reinterpreted as type flags
1574+
buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
1575+
}
1576+
else {
1577+
// Recursive usage, use any
1578+
writeKeyword(writer, SyntaxKind.AnyKeyword);
1579+
}
15741580
}
15751581
else {
1576-
// Recursive usage, use any
1577-
writeKeyword(writer, SyntaxKind.AnyKeyword);
1582+
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
1583+
// of types allows us to catch circular references to instantiations of the same anonymous type
1584+
if (!symbolStack) {
1585+
symbolStack = [];
1586+
}
1587+
symbolStack.push(symbol);
1588+
writeLiteralType(type, flags);
1589+
symbolStack.pop();
15781590
}
15791591
}
15801592
else {
1581-
if (!typeStack) {
1582-
typeStack = [];
1583-
}
1584-
typeStack.push(type);
1593+
// Anonymous types with no symbol are never circular
15851594
writeLiteralType(type, flags);
1586-
typeStack.pop();
15871595
}
15881596

15891597
function shouldWriteTypeOfFunctionSymbol() {
1590-
if (type.symbol) {
1591-
let isStaticMethodSymbol = !!(type.symbol.flags & SymbolFlags.Method && // typeof static method
1592-
ts.forEach(type.symbol.declarations, declaration => declaration.flags & NodeFlags.Static));
1593-
let isNonLocalFunctionSymbol = !!(type.symbol.flags & SymbolFlags.Function) &&
1594-
(type.symbol.parent || // is exported function symbol
1595-
ts.forEach(type.symbol.declarations, declaration =>
1596-
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
1597-
1598-
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
1599-
// typeof is allowed only for static/non local functions
1600-
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
1601-
(typeStack && contains(typeStack, type)); // it is type of the symbol uses itself recursively
1602-
}
1598+
let isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
1599+
forEach(symbol.declarations, declaration => declaration.flags & NodeFlags.Static));
1600+
let isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
1601+
(symbol.parent || // is exported function symbol
1602+
forEach(symbol.declarations, declaration =>
1603+
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
1604+
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
1605+
// typeof is allowed only for static/non local functions
1606+
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
1607+
(contains(symbolStack, symbol)); // it is type of the symbol uses itself recursively
16031608
}
16041609
}
16051610
}
@@ -1634,7 +1639,7 @@ module ts {
16341639
if (flags & TypeFormatFlags.InElementType) {
16351640
writePunctuation(writer, SyntaxKind.OpenParenToken);
16361641
}
1637-
buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, typeStack);
1642+
buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, symbolStack);
16381643
if (flags & TypeFormatFlags.InElementType) {
16391644
writePunctuation(writer, SyntaxKind.CloseParenToken);
16401645
}
@@ -1646,7 +1651,7 @@ module ts {
16461651
}
16471652
writeKeyword(writer, SyntaxKind.NewKeyword);
16481653
writeSpace(writer);
1649-
buildSignatureDisplay(resolved.constructSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, typeStack);
1654+
buildSignatureDisplay(resolved.constructSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, symbolStack);
16501655
if (flags & TypeFormatFlags.InElementType) {
16511656
writePunctuation(writer, SyntaxKind.CloseParenToken);
16521657
}
@@ -1658,15 +1663,15 @@ module ts {
16581663
writer.writeLine();
16591664
writer.increaseIndent();
16601665
for (let signature of resolved.callSignatures) {
1661-
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, typeStack);
1666+
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, symbolStack);
16621667
writePunctuation(writer, SyntaxKind.SemicolonToken);
16631668
writer.writeLine();
16641669
}
16651670
for (let signature of resolved.constructSignatures) {
16661671
writeKeyword(writer, SyntaxKind.NewKeyword);
16671672
writeSpace(writer);
16681673

1669-
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, typeStack);
1674+
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, symbolStack);
16701675
writePunctuation(writer, SyntaxKind.SemicolonToken);
16711676
writer.writeLine();
16721677
}
@@ -1707,7 +1712,7 @@ module ts {
17071712
if (p.flags & SymbolFlags.Optional) {
17081713
writePunctuation(writer, SyntaxKind.QuestionToken);
17091714
}
1710-
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, typeStack);
1715+
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, symbolStack);
17111716
writePunctuation(writer, SyntaxKind.SemicolonToken);
17121717
writer.writeLine();
17131718
}
@@ -1736,18 +1741,18 @@ module ts {
17361741
}
17371742
}
17381743

1739-
function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1744+
function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
17401745
appendSymbolNameOnly(tp.symbol, writer);
17411746
let constraint = getConstraintOfTypeParameter(tp);
17421747
if (constraint) {
17431748
writeSpace(writer);
17441749
writeKeyword(writer, SyntaxKind.ExtendsKeyword);
17451750
writeSpace(writer);
1746-
buildTypeDisplay(constraint, writer, enclosingDeclaration, flags, typeStack);
1751+
buildTypeDisplay(constraint, writer, enclosingDeclaration, flags, symbolStack);
17471752
}
17481753
}
17491754

1750-
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1755+
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
17511756
if (hasDotDotDotToken(p.valueDeclaration)) {
17521757
writePunctuation(writer, SyntaxKind.DotDotDotToken);
17531758
}
@@ -1758,24 +1763,24 @@ module ts {
17581763
writePunctuation(writer, SyntaxKind.ColonToken);
17591764
writeSpace(writer);
17601765

1761-
buildTypeDisplay(getTypeOfSymbol(p), writer, enclosingDeclaration, flags, typeStack);
1766+
buildTypeDisplay(getTypeOfSymbol(p), writer, enclosingDeclaration, flags, symbolStack);
17621767
}
17631768

1764-
function buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1769+
function buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
17651770
if (typeParameters && typeParameters.length) {
17661771
writePunctuation(writer, SyntaxKind.LessThanToken);
17671772
for (let i = 0; i < typeParameters.length; i++) {
17681773
if (i > 0) {
17691774
writePunctuation(writer, SyntaxKind.CommaToken);
17701775
writeSpace(writer);
17711776
}
1772-
buildTypeParameterDisplay(typeParameters[i], writer, enclosingDeclaration, flags, typeStack);
1777+
buildTypeParameterDisplay(typeParameters[i], writer, enclosingDeclaration, flags, symbolStack);
17731778
}
17741779
writePunctuation(writer, SyntaxKind.GreaterThanToken);
17751780
}
17761781
}
17771782

1778-
function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1783+
function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
17791784
if (typeParameters && typeParameters.length) {
17801785
writePunctuation(writer, SyntaxKind.LessThanToken);
17811786
for (let i = 0; i < typeParameters.length; i++) {
@@ -1789,19 +1794,19 @@ module ts {
17891794
}
17901795
}
17911796

1792-
function buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1797+
function buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
17931798
writePunctuation(writer, SyntaxKind.OpenParenToken);
17941799
for (let i = 0; i < parameters.length; i++) {
17951800
if (i > 0) {
17961801
writePunctuation(writer, SyntaxKind.CommaToken);
17971802
writeSpace(writer);
17981803
}
1799-
buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, typeStack);
1804+
buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, symbolStack);
18001805
}
18011806
writePunctuation(writer, SyntaxKind.CloseParenToken);
18021807
}
18031808

1804-
function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1809+
function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
18051810
if (flags & TypeFormatFlags.WriteArrowStyleSignature) {
18061811
writeSpace(writer);
18071812
writePunctuation(writer, SyntaxKind.EqualsGreaterThanToken);
@@ -1810,21 +1815,21 @@ module ts {
18101815
writePunctuation(writer, SyntaxKind.ColonToken);
18111816
}
18121817
writeSpace(writer);
1813-
buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags, typeStack);
1818+
buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags, symbolStack);
18141819
}
18151820

1816-
function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1821+
function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
18171822
if (signature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature)) {
18181823
// Instantiated signature, write type arguments instead
18191824
// This is achieved by passing in the mapper separately
18201825
buildDisplayForTypeArgumentsAndDelimiters(signature.target.typeParameters, signature.mapper, writer, enclosingDeclaration);
18211826
}
18221827
else {
1823-
buildDisplayForTypeParametersAndDelimiters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack);
1828+
buildDisplayForTypeParametersAndDelimiters(signature.typeParameters, writer, enclosingDeclaration, flags, symbolStack);
18241829
}
18251830

1826-
buildDisplayForParametersAndDelimiters(signature.parameters, writer, enclosingDeclaration, flags, typeStack);
1827-
buildReturnTypeDisplay(signature, writer, enclosingDeclaration, flags, typeStack);
1831+
buildDisplayForParametersAndDelimiters(signature.parameters, writer, enclosingDeclaration, flags, symbolStack);
1832+
buildReturnTypeDisplay(signature, writer, enclosingDeclaration, flags, symbolStack);
18281833
}
18291834

18301835
return _displayBuilder || (_displayBuilder = {
@@ -3889,7 +3894,7 @@ module ts {
38893894
mapper.mappings = {};
38903895
}
38913896
// Instantiate the given type using the given mapper and cache the result
3892-
let result = <ResolvedType>createObjectType(TypeFlags.Anonymous, type.symbol);
3897+
let result = <ResolvedType>createObjectType(TypeFlags.Anonymous | TypeFlags.Instantiated, type.symbol);
38933898
result.properties = instantiateList(getPropertiesOfObjectType(type), mapper, instantiateSymbol);
38943899
result.members = createSymbolTable(result.properties);
38953900
result.callSignatures = instantiateList(getSignaturesOfType(type, SignatureKind.Call), mapper, instantiateSignature);
@@ -4313,12 +4318,12 @@ module ts {
43134318
// Effectively, we will generate a false positive when two types are structurally equal to at least 10 levels, but unequal at
43144319
// some level beyond that.
43154320
function isDeeplyNestedGeneric(type: ObjectType, stack: ObjectType[]): boolean {
4316-
if (type.flags & TypeFlags.Reference && depth >= 10) {
4317-
let target = (<TypeReference>type).target;
4321+
if (type.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && depth >= 10) {
4322+
let symbol = type.symbol;
43184323
let count = 0;
43194324
for (let i = 0; i < depth; i++) {
43204325
let t = stack[i];
4321-
if (t.flags & TypeFlags.Reference && (<TypeReference>t).target === target) {
4326+
if (t.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && t.symbol === symbol) {
43224327
count++;
43234328
if (count >= 10) return true;
43244329
}

0 commit comments

Comments
 (0)