Skip to content

Commit 7b860f5

Browse files
committed
Merge pull request microsoft#2991 from Microsoft/circularVar
Error when variable is circularly referenced in type annotation
2 parents 69de046 + eeb23ad commit 7b860f5

18 files changed

+291
-481
lines changed

src/compiler/checker.ts

Lines changed: 95 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,11 @@ module ts {
8888
let undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
8989
let nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null");
9090
let unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
91-
let resolvingType = createIntrinsicType(TypeFlags.Any, "__resolving__");
9291

9392
let emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
9493
let anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
9594
let noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
96-
95+
9796
let anySignature = createSignature(undefined, undefined, emptyArray, anyType, 0, false, false);
9897
let unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, 0, false, false);
9998

@@ -118,14 +117,17 @@ module ts {
118117
let getGlobalParameterDecoratorType: () => ObjectType;
119118
let getGlobalPropertyDecoratorType: () => ObjectType;
120119
let getGlobalMethodDecoratorType: () => ObjectType;
121-
120+
122121
let tupleTypes: Map<TupleType> = {};
123122
let unionTypes: Map<UnionType> = {};
124123
let stringLiteralTypes: Map<StringLiteralType> = {};
125124
let emitExtends = false;
126125
let emitDecorate = false;
127126
let emitParam = false;
128127

128+
let resolutionTargets: Object[] = [];
129+
let resolutionResults: boolean[] = [];
130+
129131
let mergedSymbols: Symbol[] = [];
130132
let symbolLinks: SymbolLinks[] = [];
131133
let nodeLinks: NodeLinks[] = [];
@@ -1980,7 +1982,7 @@ module ts {
19801982
}
19811983
}
19821984

1983-
function collectLinkedAliases(node: Identifier): Node[]{
1985+
function collectLinkedAliases(node: Identifier): Node[] {
19841986
var exportSymbol: Symbol;
19851987
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
19861988
exportSymbol = resolveName(node.parent, node.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, Diagnostics.Cannot_find_name_0, node);
@@ -2014,6 +2016,38 @@ module ts {
20142016
}
20152017
}
20162018

2019+
// Push an entry on the type resolution stack. If an entry with the given target is not already on the stack,
2020+
// a new entry with that target and an associated result value of true is pushed on the stack, and the value
2021+
// true is returned. Otherwise, a circularity has occurred and the result values of the existing entry and
2022+
// all entries pushed after it are changed to false, and the value false is returned. The target object provides
2023+
// a unique identity for a particular type resolution result: Symbol instances are used to track resolution of
2024+
// SymbolLinks.type, SymbolLinks instances are used to track resolution of SymbolLinks.declaredType, and
2025+
// Signature instances are used to track resolution of Signature.resolvedReturnType.
2026+
function pushTypeResolution(target: Object): boolean {
2027+
let i = 0;
2028+
let count = resolutionTargets.length;
2029+
while (i < count && resolutionTargets[i] !== target) {
2030+
i++;
2031+
}
2032+
if (i < count) {
2033+
do {
2034+
resolutionResults[i++] = false;
2035+
}
2036+
while (i < count);
2037+
return false;
2038+
}
2039+
resolutionTargets.push(target);
2040+
resolutionResults.push(true);
2041+
return true;
2042+
}
2043+
2044+
// Pop an entry from the type resolution stack and return its associated result value. The result value will
2045+
// be true if no circularities were detected, or false if a circularity was found.
2046+
function popTypeResolution(): boolean {
2047+
resolutionTargets.pop();
2048+
return resolutionResults.pop();
2049+
}
2050+
20172051
function getRootDeclaration(node: Node): Node {
20182052
while (node.kind === SyntaxKind.BindingElement) {
20192053
node = node.parent.parent;
@@ -2271,20 +2305,27 @@ module ts {
22712305
return links.type = checkExpression((<ExportAssignment>declaration).expression);
22722306
}
22732307
// Handle variable, parameter or property
2274-
links.type = resolvingType;
2275-
let type = getWidenedTypeForVariableLikeDeclaration(<VariableLikeDeclaration>declaration, /*reportErrors*/ true);
2276-
if (links.type === resolvingType) {
2277-
links.type = type;
2308+
if (!pushTypeResolution(symbol)) {
2309+
return unknownType;
22782310
}
2279-
}
2280-
else if (links.type === resolvingType) {
2281-
links.type = anyType;
2282-
if (compilerOptions.noImplicitAny) {
2283-
let diagnostic = (<VariableLikeDeclaration>symbol.valueDeclaration).type ?
2284-
Diagnostics._0_implicitly_has_type_any_because_it_is_referenced_directly_or_indirectly_in_its_own_type_annotation :
2285-
Diagnostics._0_implicitly_has_type_any_because_it_is_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer;
2286-
error(symbol.valueDeclaration, diagnostic, symbolToString(symbol));
2311+
let type = getWidenedTypeForVariableLikeDeclaration(<VariableLikeDeclaration>declaration, /*reportErrors*/ true);
2312+
if (!popTypeResolution()) {
2313+
if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
2314+
// Variable has type annotation that circularly references the variable itself
2315+
type = unknownType;
2316+
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
2317+
symbolToString(symbol));
2318+
}
2319+
else {
2320+
// Variable has initializer that circularly references the variable itself
2321+
type = anyType;
2322+
if (compilerOptions.noImplicitAny) {
2323+
error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_is_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
2324+
symbolToString(symbol));
2325+
}
2326+
}
22872327
}
2328+
links.type = type;
22882329
}
22892330
return links.type;
22902331
}
@@ -2308,19 +2349,13 @@ module ts {
23082349

23092350
function getTypeOfAccessors(symbol: Symbol): Type {
23102351
let links = getSymbolLinks(symbol);
2311-
checkAndStoreTypeOfAccessors(symbol, links);
2312-
return links.type;
2313-
}
2314-
2315-
function checkAndStoreTypeOfAccessors(symbol: Symbol, links?: SymbolLinks) {
2316-
links = links || getSymbolLinks(symbol);
23172352
if (!links.type) {
2318-
links.type = resolvingType;
2353+
if (!pushTypeResolution(symbol)) {
2354+
return unknownType;
2355+
}
23192356
let getter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
23202357
let setter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.SetAccessor);
2321-
23222358
let type: Type;
2323-
23242359
// First try to see if the user specified a return type on the get-accessor.
23252360
let getterReturnType = getAnnotatedAccessorType(getter);
23262361
if (getterReturnType) {
@@ -2342,23 +2377,20 @@ module ts {
23422377
if (compilerOptions.noImplicitAny) {
23432378
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_type_annotation, symbolToString(symbol));
23442379
}
2345-
23462380
type = anyType;
23472381
}
23482382
}
23492383
}
2350-
2351-
if (links.type === resolvingType) {
2352-
links.type = type;
2353-
}
2354-
}
2355-
else if (links.type === resolvingType) {
2356-
links.type = anyType;
2357-
if (compilerOptions.noImplicitAny) {
2358-
let getter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
2359-
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
2384+
if (!popTypeResolution()) {
2385+
type = anyType;
2386+
if (compilerOptions.noImplicitAny) {
2387+
let getter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
2388+
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
2389+
}
23602390
}
2391+
links.type = type;
23612392
}
2393+
return links.type;
23622394
}
23632395

23642396
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
@@ -2451,7 +2483,7 @@ module ts {
24512483
return result;
24522484
}
24532485

2454-
function getBaseTypes(type: InterfaceType): ObjectType[]{
2486+
function getBaseTypes(type: InterfaceType): ObjectType[] {
24552487
let typeWithBaseTypes = <InterfaceTypeWithBaseTypes>type;
24562488
if (!typeWithBaseTypes.baseTypes) {
24572489
if (type.symbol.flags & SymbolFlags.Class) {
@@ -2536,17 +2568,18 @@ module ts {
25362568
function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type {
25372569
let links = getSymbolLinks(symbol);
25382570
if (!links.declaredType) {
2539-
links.declaredType = resolvingType;
2571+
// Note that we use the links object as the target here because the symbol object is used as the unique
2572+
// identity for resolution of the 'type' property in SymbolLinks.
2573+
if (!pushTypeResolution(links)) {
2574+
return unknownType;
2575+
}
25402576
let declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
25412577
let type = getTypeFromTypeNode(declaration.type);
2542-
if (links.declaredType === resolvingType) {
2543-
links.declaredType = type;
2578+
if (!popTypeResolution()) {
2579+
type = unknownType;
2580+
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
25442581
}
2545-
}
2546-
else if (links.declaredType === resolvingType) {
2547-
links.declaredType = unknownType;
2548-
let declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
2549-
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
2582+
links.declaredType = type;
25502583
}
25512584
return links.declaredType;
25522585
}
@@ -3150,7 +3183,9 @@ module ts {
31503183

31513184
function getReturnTypeOfSignature(signature: Signature): Type {
31523185
if (!signature.resolvedReturnType) {
3153-
signature.resolvedReturnType = resolvingType;
3186+
if (!pushTypeResolution(signature)) {
3187+
return unknownType;
3188+
}
31543189
let type: Type;
31553190
if (signature.target) {
31563191
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
@@ -3161,21 +3196,19 @@ module ts {
31613196
else {
31623197
type = getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration);
31633198
}
3164-
if (signature.resolvedReturnType === resolvingType) {
3165-
signature.resolvedReturnType = type;
3166-
}
3167-
}
3168-
else if (signature.resolvedReturnType === resolvingType) {
3169-
signature.resolvedReturnType = anyType;
3170-
if (compilerOptions.noImplicitAny) {
3171-
let declaration = <Declaration>signature.declaration;
3172-
if (declaration.name) {
3173-
error(declaration.name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(declaration.name));
3174-
}
3175-
else {
3176-
error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions);
3199+
if (!popTypeResolution()) {
3200+
type = anyType;
3201+
if (compilerOptions.noImplicitAny) {
3202+
let declaration = <Declaration>signature.declaration;
3203+
if (declaration.name) {
3204+
error(declaration.name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(declaration.name));
3205+
}
3206+
else {
3207+
error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions);
3208+
}
31773209
}
31783210
}
3211+
signature.resolvedReturnType = type;
31793212
}
31803213
return signature.resolvedReturnType;
31813214
}
@@ -7342,10 +7375,9 @@ module ts {
73427375
if (isContextSensitive(node)) {
73437376
assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper);
73447377
}
7345-
if (!node.type) {
7346-
signature.resolvedReturnType = resolvingType;
7378+
if (!node.type && !signature.resolvedReturnType) {
73477379
let returnType = getReturnTypeFromBody(node, contextualMapper);
7348-
if (signature.resolvedReturnType === resolvingType) {
7380+
if (!signature.resolvedReturnType) {
73497381
signature.resolvedReturnType = returnType;
73507382
}
73517383
}
@@ -8359,8 +8391,7 @@ module ts {
83598391
}
83608392
}
83618393
}
8362-
8363-
checkAndStoreTypeOfAccessors(getSymbolOfNode(node));
8394+
getTypeOfAccessors(getSymbolOfNode(node));
83648395
}
83658396

83668397
checkFunctionLikeDeclaration(node);

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ module ts {
363363
An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2499, category: DiagnosticCategory.Error, key: "An interface can only extend an identifier/qualified-name with optional type arguments." },
364364
A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2500, category: DiagnosticCategory.Error, key: "A class can only implement an identifier/qualified-name with optional type arguments." },
365365
A_rest_element_cannot_contain_a_binding_pattern: { code: 2501, category: DiagnosticCategory.Error, key: "A rest element cannot contain a binding pattern." },
366+
_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation: { code: 2502, category: DiagnosticCategory.Error, key: "'{0}' is referenced directly or indirectly in its own type annotation." },
366367
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
367368
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
368369
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
@@ -517,7 +518,6 @@ module ts {
517518
Object_literal_s_property_0_implicitly_has_an_1_type: { code: 7018, category: DiagnosticCategory.Error, key: "Object literal's property '{0}' implicitly has an '{1}' type." },
518519
Rest_parameter_0_implicitly_has_an_any_type: { code: 7019, category: DiagnosticCategory.Error, key: "Rest parameter '{0}' implicitly has an 'any[]' type." },
519520
Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: { code: 7020, category: DiagnosticCategory.Error, key: "Call signature, which lacks return-type annotation, implicitly has an 'any' return type." },
520-
_0_implicitly_has_type_any_because_it_is_referenced_directly_or_indirectly_in_its_own_type_annotation: { code: 7021, category: DiagnosticCategory.Error, key: "'{0}' implicitly has type 'any' because it is referenced directly or indirectly in its own type annotation." },
521521
_0_implicitly_has_type_any_because_it_is_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer: { code: 7022, category: DiagnosticCategory.Error, key: "'{0}' implicitly has type 'any' because it is does not have a type annotation and is referenced directly or indirectly in its own initializer." },
522522
_0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions: { code: 7023, category: DiagnosticCategory.Error, key: "'{0}' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions." },
523523
Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions: { code: 7024, category: DiagnosticCategory.Error, key: "Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions." },

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,10 @@
14401440
"category": "Error",
14411441
"code": 2501
14421442
},
1443+
"'{0}' is referenced directly or indirectly in its own type annotation.": {
1444+
"category": "Error",
1445+
"code": 2502
1446+
},
14431447

14441448
"Import declaration '{0}' is using private name '{1}'.": {
14451449
"category": "Error",
@@ -2060,10 +2064,6 @@
20602064
"category": "Error",
20612065
"code": 7020
20622066
},
2063-
"'{0}' implicitly has type 'any' because it is referenced directly or indirectly in its own type annotation.": {
2064-
"category": "Error",
2065-
"code": 7021
2066-
},
20672067
"'{0}' implicitly has type 'any' because it is does not have a type annotation and is referenced directly or indirectly in its own initializer.": {
20682068
"category": "Error",
20692069
"code": 7022

0 commit comments

Comments
 (0)