More robust circularity detection in node builder (#24225)

This commit is contained in:
Wesley Wigham 2018-05-17 14:30:07 -07:00 committed by GitHub
parent 08c364d258
commit 3800d7b246
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 16 deletions

View file

@ -3060,7 +3060,7 @@ namespace ts {
flags,
tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: noop },
encounteredError: false,
symbolStack: undefined,
visitedSymbols: undefined,
inferTypeParameters: undefined
};
}
@ -3242,7 +3242,10 @@ namespace ts {
function createAnonymousTypeNode(type: ObjectType): TypeNode {
const symbol = type.symbol;
let id: string;
if (symbol) {
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
id = (isConstructorObject ? "+" : "") + getSymbolId(symbol);
if (isJavaScriptConstructor(symbol.valueDeclaration)) {
// Instance and static types share the same symbol; only add 'typeof' for the static side.
const isInstanceType = type === getInferredClassType(symbol) ? SymbolFlags.Type : SymbolFlags.Value;
@ -3254,7 +3257,7 @@ namespace ts {
shouldWriteTypeOfFunctionSymbol()) {
return symbolToTypeNode(symbol, context, SymbolFlags.Value);
}
else if (contains(context.symbolStack, symbol)) {
else if (context.visitedSymbols && context.visitedSymbols.has(id)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
const typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
@ -3268,20 +3271,14 @@ namespace ts {
else {
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
// of types allows us to catch circular references to instantiations of the same anonymous type
if (!context.symbolStack) {
context.symbolStack = [];
if (!context.visitedSymbols) {
context.visitedSymbols = createMap<true>();
}
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
if (isConstructorObject) {
return createTypeNodeFromObjectType(type);
}
else {
context.symbolStack.push(symbol);
const result = createTypeNodeFromObjectType(type);
context.symbolStack.pop();
return result;
}
context.visitedSymbols.set(id, true);
const result = createTypeNodeFromObjectType(type);
context.visitedSymbols.delete(id);
return result;
}
}
else {
@ -3298,7 +3295,7 @@ namespace ts {
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return (!!(context.flags & NodeBuilderFlags.UseTypeOfFunction) || contains(context.symbolStack, symbol)) && // it is type of the symbol uses itself recursively
return (!!(context.flags & NodeBuilderFlags.UseTypeOfFunction) || (context.visitedSymbols && context.visitedSymbols.has(id))) && // it is type of the symbol uses itself recursively
(!(context.flags & NodeBuilderFlags.UseStructuralFallback) || isValueSymbolAccessible(symbol, context.enclosingDeclaration)); // And the build is going to succeed without visibility error or there is no structural fallback allowed
}
}
@ -3997,7 +3994,7 @@ namespace ts {
// State
encounteredError: boolean;
symbolStack: Symbol[] | undefined;
visitedSymbols: Map<true> | undefined;
inferTypeParameters: TypeParameter[] | undefined;
}

View file

@ -0,0 +1,37 @@
//// [classExpressionInClassStaticDeclarations.ts]
class C {
static D = class extends C {};
}
//// [classExpressionInClassStaticDeclarations.js]
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var C = /** @class */ (function () {
function C() {
}
C.D = /** @class */ (function (_super) {
__extends(class_1, _super);
function class_1() {
return _super !== null && _super.apply(this, arguments) || this;
}
return class_1;
}(C));
return C;
}());
//// [classExpressionInClassStaticDeclarations.d.ts]
declare class C {
static D: {
new (): {};
D: any;
};
}

View file

@ -0,0 +1,8 @@
=== tests/cases/compiler/classExpressionInClassStaticDeclarations.ts ===
class C {
>C : Symbol(C, Decl(classExpressionInClassStaticDeclarations.ts, 0, 0))
static D = class extends C {};
>D : Symbol(C.D, Decl(classExpressionInClassStaticDeclarations.ts, 0, 9))
>C : Symbol(C, Decl(classExpressionInClassStaticDeclarations.ts, 0, 0))
}

View file

@ -0,0 +1,9 @@
=== tests/cases/compiler/classExpressionInClassStaticDeclarations.ts ===
class C {
>C : C
static D = class extends C {};
>D : typeof (Anonymous class)
>class extends C {} : typeof (Anonymous class)
>C : C
}

View file

@ -0,0 +1,4 @@
// @declaration: true
class C {
static D = class extends C {};
}