More robust circularity detection in node builder (#24225)
This commit is contained in:
parent
08c364d258
commit
3800d7b246
|
@ -3060,7 +3060,7 @@ namespace ts {
|
||||||
flags,
|
flags,
|
||||||
tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: noop },
|
tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: noop },
|
||||||
encounteredError: false,
|
encounteredError: false,
|
||||||
symbolStack: undefined,
|
visitedSymbols: undefined,
|
||||||
inferTypeParameters: undefined
|
inferTypeParameters: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3242,7 +3242,10 @@ namespace ts {
|
||||||
|
|
||||||
function createAnonymousTypeNode(type: ObjectType): TypeNode {
|
function createAnonymousTypeNode(type: ObjectType): TypeNode {
|
||||||
const symbol = type.symbol;
|
const symbol = type.symbol;
|
||||||
|
let id: string;
|
||||||
if (symbol) {
|
if (symbol) {
|
||||||
|
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
|
||||||
|
id = (isConstructorObject ? "+" : "") + getSymbolId(symbol);
|
||||||
if (isJavaScriptConstructor(symbol.valueDeclaration)) {
|
if (isJavaScriptConstructor(symbol.valueDeclaration)) {
|
||||||
// Instance and static types share the same symbol; only add 'typeof' for the static side.
|
// Instance and static types share the same symbol; only add 'typeof' for the static side.
|
||||||
const isInstanceType = type === getInferredClassType(symbol) ? SymbolFlags.Type : SymbolFlags.Value;
|
const isInstanceType = type === getInferredClassType(symbol) ? SymbolFlags.Type : SymbolFlags.Value;
|
||||||
|
@ -3254,7 +3257,7 @@ namespace ts {
|
||||||
shouldWriteTypeOfFunctionSymbol()) {
|
shouldWriteTypeOfFunctionSymbol()) {
|
||||||
return symbolToTypeNode(symbol, context, SymbolFlags.Value);
|
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
|
// If type is an anonymous type literal in a type alias declaration, use type alias name
|
||||||
const typeAlias = getTypeAliasForTypeLiteral(type);
|
const typeAlias = getTypeAliasForTypeLiteral(type);
|
||||||
if (typeAlias) {
|
if (typeAlias) {
|
||||||
|
@ -3268,20 +3271,14 @@ namespace ts {
|
||||||
else {
|
else {
|
||||||
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
|
// 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
|
// of types allows us to catch circular references to instantiations of the same anonymous type
|
||||||
if (!context.symbolStack) {
|
if (!context.visitedSymbols) {
|
||||||
context.symbolStack = [];
|
context.visitedSymbols = createMap<true>();
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
|
context.visitedSymbols.set(id, true);
|
||||||
if (isConstructorObject) {
|
const result = createTypeNodeFromObjectType(type);
|
||||||
return createTypeNodeFromObjectType(type);
|
context.visitedSymbols.delete(id);
|
||||||
}
|
return result;
|
||||||
else {
|
|
||||||
context.symbolStack.push(symbol);
|
|
||||||
const result = createTypeNodeFromObjectType(type);
|
|
||||||
context.symbolStack.pop();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -3298,7 +3295,7 @@ namespace ts {
|
||||||
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
|
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
|
||||||
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
|
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
|
||||||
// typeof is allowed only for static/non local functions
|
// 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
|
(!(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
|
// State
|
||||||
encounteredError: boolean;
|
encounteredError: boolean;
|
||||||
symbolStack: Symbol[] | undefined;
|
visitedSymbols: Map<true> | undefined;
|
||||||
inferTypeParameters: TypeParameter[] | undefined;
|
inferTypeParameters: TypeParameter[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
// @declaration: true
|
||||||
|
class C {
|
||||||
|
static D = class extends C {};
|
||||||
|
}
|
Loading…
Reference in a new issue