use NodeBuilderContext

This commit is contained in:
Arthur Ozga 2017-03-25 14:14:36 -07:00
parent 85986ddaee
commit a94f874b06
3 changed files with 373 additions and 332 deletions

View file

@ -2204,369 +2204,403 @@ namespace ts {
function createNodeBuilder(): NodeBuilder { function createNodeBuilder(): NodeBuilder {
let encounteredError = false; interface NodeBuilderContext {
readonly enclosingDeclaration: Node | undefined;
readonly flags: NodeBuilderFlags | undefined;
encounteredError: boolean;
inObjectTypeLiteral: boolean;
checkAlias: boolean;
symbolStack: Symbol[] | undefined;
}
function createNodeBuilderContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): NodeBuilderContext {
return {
enclosingDeclaration,
flags,
encounteredError: false,
inObjectTypeLiteral: false,
checkAlias: true,
symbolStack: undefined
};
}
let context: NodeBuilderContext;
return { return {
typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
Debug.assert(encounteredError === false, "Nested call into nodeBuilder are forbidden."); context = createNodeBuilderContext(enclosingDeclaration, flags);
encounteredError = false; const resultingNode = typeToTypeNodeHelper(type);
const resultingNode = typeToTypeNodeHelper(type, enclosingDeclaration, flags); const result = context.encounteredError ? undefined : resultingNode;
const result = encounteredError ? undefined : resultingNode;
encounteredError = false;
return result; return result;
}, },
indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
Debug.assert(encounteredError === false, "Nested call into nodeBuilder are forbidden."); context = createNodeBuilderContext(enclosingDeclaration, flags);
encounteredError = false; const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind);
const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, enclosingDeclaration, flags); const result = context.encounteredError ? undefined : resultingNode;
const result = encounteredError ? undefined : resultingNode;
encounteredError = false;
return result; return result;
}, },
signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
Debug.assert(encounteredError === false, "Nested call into nodeBuilder are forbidden."); context = createNodeBuilderContext(enclosingDeclaration, flags);
encounteredError = false; const resultingNode = signatureToSignatureDeclarationHelper(signature, kind);
const resultingNode = signatureToSignatureDeclarationHelper(signature, kind, enclosingDeclaration, flags); const result = context.encounteredError ? undefined : resultingNode;
const result = encounteredError ? undefined : resultingNode;
encounteredError = false;
return result; return result;
} }
}; };
function typeToTypeNodeHelper(type: Type, enclosingDeclaration: Node, flags: NodeBuilderFlags): TypeNode { function typeToTypeNodeHelper(type: Type): TypeNode {
let inObjectTypeLiteral = false; if (!type) {
let checkAlias = true; context.encounteredError = true;
let symbolStack: Symbol[] = undefined; // TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)?
return undefined;
}
return typeToTypeNodeWorker(type); if (type.flags & TypeFlags.Any) {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
if (type.flags & TypeFlags.String) {
return createKeywordTypeNode(SyntaxKind.StringKeyword);
}
if (type.flags & TypeFlags.Number) {
return createKeywordTypeNode(SyntaxKind.NumberKeyword);
}
if (type.flags & TypeFlags.Boolean) {
return createKeywordTypeNode(SyntaxKind.BooleanKeyword);
}
if (type.flags & TypeFlags.Enum) {
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & (TypeFlags.StringLiteral)) {
return createLiteralTypeNode((createLiteral((<LiteralType>type).text)));
}
if (type.flags & (TypeFlags.NumberLiteral)) {
return createLiteralTypeNode((createNumericLiteral((<LiteralType>type).text)));
}
if (type.flags & TypeFlags.BooleanLiteral) {
return (<IntrinsicType>type).intrinsicName === "true" ? createTrue() : createFalse();
}
if (type.flags & TypeFlags.EnumLiteral) {
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.Void) {
return createKeywordTypeNode(SyntaxKind.VoidKeyword);
}
if (type.flags & TypeFlags.Undefined) {
return createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
}
if (type.flags & TypeFlags.Null) {
return createKeywordTypeNode(SyntaxKind.NullKeyword);
}
if (type.flags & TypeFlags.Never) {
return createKeywordTypeNode(SyntaxKind.NeverKeyword);
}
if (type.flags & TypeFlags.ESSymbol) {
throw new Error("ESSymbol not implemented");
}
if (type.flags & TypeFlags.NonPrimitive) {
throw new Error("Non primitive not implemented");
}
if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (context.inObjectTypeLiteral) {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowThisInObjectLiteral)) {
context.encounteredError = true;
}
}
return createThis();
}
function typeToTypeNodeWorker(type: Type): TypeNode { const objectFlags = getObjectFlags(type);
if (!type) {
encounteredError = true; if (objectFlags & ObjectFlags.Reference) {
// TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)? Debug.assert(!!(type.flags & TypeFlags.Object));
return typeReferenceToTypeNode(<TypeReference>type);
}
if (objectFlags & ObjectFlags.ClassOrInterface) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
// TODO(aozgaa): handle type arguments.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.TypeParameter) {
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (context.checkAlias && type.aliasSymbol) {
const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false);
const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments);
return createTypeReferenceNode(name, typeArgumentNodes);
}
context.checkAlias = false;
if (type.flags & TypeFlags.Union) {
const formattedUnionTypes = formatUnionTypes((<UnionType>type).types);
const unionTypeNodes = formattedUnionTypes && mapToTypeNodeArray(formattedUnionTypes);
if (unionTypeNodes && unionTypeNodes.length > 0) {
return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, unionTypeNodes);
}
else {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) {
context.encounteredError = true;
}
return undefined; return undefined;
} }
}
if (type.flags & TypeFlags.Any) { if (type.flags & TypeFlags.Intersection) {
return createKeywordTypeNode(SyntaxKind.AnyKeyword); return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types));
} }
if (type.flags & TypeFlags.String) {
return createKeywordTypeNode(SyntaxKind.StringKeyword); if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
} Debug.assert(!!(type.flags & TypeFlags.Object));
if (type.flags & TypeFlags.Number) { // The type is an object literal type.
return createKeywordTypeNode(SyntaxKind.NumberKeyword); return createAnonymousTypeNode(<ObjectType>type);
} }
if (type.flags & TypeFlags.Boolean) {
return createKeywordTypeNode(SyntaxKind.BooleanKeyword); if (type.flags & TypeFlags.Index) {
} const indexedType = (<IndexType>type).type;
if (type.flags & TypeFlags.Enum) { const indexTypeNode = typeToTypeNodeHelper(indexedType);
const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags); return createTypeOperatorNode(indexTypeNode);
return createTypeReferenceNode(name, /*typeArguments*/ undefined); }
} if (type.flags & TypeFlags.IndexedAccess) {
if (type.flags & (TypeFlags.StringLiteral)) { const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType);
return createLiteralTypeNode((createLiteral((<LiteralType>type).text))); const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType);
} return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
if (type.flags & (TypeFlags.NumberLiteral)) { }
return createLiteralTypeNode((createNumericLiteral((<LiteralType>type).text)));
} Debug.fail("Should be unreachable.");
if (type.flags & TypeFlags.BooleanLiteral) {
return (<IntrinsicType>type).intrinsicName === "true" ? createTrue() : createFalse(); function mapToTypeNodeArray(types: Type[]): TypeNode[] {
} const result = [];
if (type.flags & TypeFlags.EnumLiteral) { for (const type of types) {
const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags); const typeNode = typeToTypeNodeHelper(type);
return createTypeReferenceNode(name, /*typeArguments*/ undefined); if (typeNode) {
} result.push(typeNode);
if (type.flags & TypeFlags.Void) {
return createKeywordTypeNode(SyntaxKind.VoidKeyword);
}
if (type.flags & TypeFlags.Undefined) {
return createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
}
if (type.flags & TypeFlags.Null) {
return createKeywordTypeNode(SyntaxKind.NullKeyword);
}
if (type.flags & TypeFlags.Never) {
return createKeywordTypeNode(SyntaxKind.NeverKeyword);
}
if (type.flags & TypeFlags.ESSymbol) {
throw new Error("ESSymbol not implemented");
}
if (type.flags & TypeFlags.NonPrimitive) {
throw new Error("Non primitive not implemented");
}
if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (inObjectTypeLiteral) {
encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowThisInObjectLiteral);
} }
return createThis();
} }
return result;
}
const objectFlags = getObjectFlags(type); function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const typeParameter = getTypeParameterFromMappedType(<MappedType>type);
const typeParameterNode = typeParameterToDeclaration(typeParameter);
if (objectFlags & ObjectFlags.Reference) { const templateType = getTemplateTypeFromMappedType(<MappedType>type);
Debug.assert(!!(type.flags & TypeFlags.Object)); const templateTypeNode = templateType && typeToTypeNodeHelper(templateType);
return typeReferenceToTypeNode (<TypeReference>type); const readonlyToken = (<MappedType>type).declaration && (<MappedType>type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
} const questionToken = (<MappedType>type).declaration && (<MappedType>type).declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
if (objectFlags & ObjectFlags.ClassOrInterface) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags);
// TODO(aozgaa): handle type arguments.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.TypeParameter) {
const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags);
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (checkAlias && type.aliasSymbol) { return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
const name = symbolToName(type.aliasSymbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags); }
const typeArgumentNodes = mapToTypeNodeArray(type.aliasTypeArguments);
return createTypeReferenceNode(name, typeArgumentNodes);
}
checkAlias = false;
if (type.flags & TypeFlags.Union) { function createAnonymousTypeNode(type: ObjectType): TypeNode {
return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, mapToTypeNodeArray(formatUnionTypes((<UnionType>type).types))); const symbol = type.symbol;
} if (symbol) {
// Always use 'typeof T' for type of class, enum, and module objects
if (type.flags & TypeFlags.Intersection) { if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) ||
return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types)); symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) ||
} shouldWriteTypeOfFunctionSymbol()) {
return createTypeQueryNodeFromType(type);
if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { }
Debug.assert(!!(type.flags & TypeFlags.Object)); else if (contains(context.symbolStack, symbol)) {
// The type is an object literal type. // If type is an anonymous type literal in a type alias declaration, use type alias name
return createAnonymousTypeNode(<ObjectType>type); const typeAlias = getTypeAliasForTypeLiteral(type);
} if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
if (type.flags & TypeFlags.Index) { const entityName = symbolToName(typeAlias, /*expectsIdentifier*/ false);
const indexedType = (<IndexType>type).type; return createTypeReferenceNode(entityName, /*typeArguments*/ undefined);
const indexTypeNode = typeToTypeNodeWorker(indexedType);
return createTypeOperatorNode(indexTypeNode);
}
if (type.flags & TypeFlags.IndexedAccess) {
const objectTypeNode = typeToTypeNodeWorker((<IndexedAccessType>type).objectType);
const indexTypeNode = typeToTypeNodeWorker((<IndexedAccessType>type).indexType);
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
}
Debug.fail("Should be unreachable.");
function mapToTypeNodeArray(types: Type[]): NodeArray<TypeNode> {
return types && asNodeArray(types.map(typeToTypeNodeWorker).filter(node => !!node));
}
function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const typeParameter = getTypeParameterFromMappedType(<MappedType>type);
const typeParameterNode = typeParameterToDeclaration(typeParameter, enclosingDeclaration, flags);
const templateType = getTemplateTypeFromMappedType(<MappedType>type);
const templateTypeNode = templateType && typeToTypeNodeWorker(templateType);
const readonlyToken = (<MappedType>type).declaration && (<MappedType>type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
const questionToken = (<MappedType>type).declaration && (<MappedType>type).declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
}
function createAnonymousTypeNode(type: ObjectType): TypeNode {
const symbol = type.symbol;
if (symbol) {
// Always use 'typeof T' for type of class, enum, and module objects
if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) ||
symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) ||
shouldWriteTypeOfFunctionSymbol()) {
return createTypeQueryNodeFromType(type);
}
else if (contains(symbolStack, symbol)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
const typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
const entityName = symbolToName(typeAlias, enclosingDeclaration, /*expectsIdentifier*/ false, flags);
return createTypeReferenceNode(entityName, /*typeArguments*/ undefined);
}
else {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
} }
else { else {
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead return createKeywordTypeNode(SyntaxKind.AnyKeyword);
// of types allows us to catch circular references to instantiations of the same anonymous type
if (!symbolStack) {
symbolStack = [];
}
symbolStack.push(symbol);
const result = createTypeNodeFromObjectType(type);
symbolStack.pop();
return result;
} }
} }
else { else {
// Anonymous types without a symbol are never circular. // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
return createTypeNodeFromObjectType(type); // of types allows us to catch circular references to instantiations of the same anonymous type
} if (!context.symbolStack) {
context.symbolStack = [];
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return contains(symbolStack, symbol); // it is type of the symbol uses itself recursively
} }
context.symbolStack.push(symbol);
const result = createTypeNodeFromObjectType(type);
context.symbolStack.pop();
return result;
}
}
else {
// Anonymous types without a symbol are never circular.
return createTypeNodeFromObjectType(type);
}
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return contains(context.symbolStack, symbol); // it is type of the symbol uses itself recursively
}
}
}
function createTypeNodeFromObjectType(type: ObjectType): TypeNode {
if (type.objectFlags & ObjectFlags.Mapped) {
if (getConstraintTypeFromMappedType(<MappedType>type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
return createMappedTypeNodeFromType(<MappedType>type);
} }
} }
function createTypeNodeFromObjectType(type: ObjectType): TypeNode { const resolved = resolveStructuredTypeMembers(type);
if (type.objectFlags & ObjectFlags.Mapped) { if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
if (getConstraintTypeFromMappedType(<MappedType>type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
return createMappedTypeNodeFromType(<MappedType>type); return createTypeLiteralNode(/*members*/ undefined);
}
} }
const resolved = resolveStructuredTypeMembers(type); if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { const signature = resolved.callSignatures[0];
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { return <FunctionTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType);
return createTypeLiteralNode(/*members*/ undefined);
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
const signature = resolved.callSignatures[0];
return <FunctionTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, enclosingDeclaration, flags);
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
const signature = resolved.constructSignatures[0];
return <ConstructorTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, enclosingDeclaration, flags);
}
} }
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
const saveInObjectTypeLiteral = inObjectTypeLiteral; const signature = resolved.constructSignatures[0];
inObjectTypeLiteral = true; return <ConstructorTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType);
const members = createTypeNodesFromResolvedType(resolved);
inObjectTypeLiteral = saveInObjectTypeLiteral;
return createTypeLiteralNode(members);
}
function createTypeQueryNodeFromType(type: Type) {
const symbol = type.symbol;
if (symbol) {
const entityName = symbolToName(symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags);
return createTypeQueryNode(entityName);
} }
} }
function typeReferenceToTypeNode(type: TypeReference) { const saveInObjectTypeLiteral = context.inObjectTypeLiteral;
const typeArguments: Type[] = type.typeArguments || emptyArray; context.inObjectTypeLiteral = true;
if (type.target === globalArrayType) { const members = createTypeNodesFromResolvedType(resolved);
const elementType = typeToTypeNodeWorker(typeArguments[0]); context.inObjectTypeLiteral = saveInObjectTypeLiteral;
return createArrayTypeNode(elementType); return createTypeLiteralNode(members);
}
function createTypeQueryNodeFromType(type: Type) {
const symbol = type.symbol;
if (symbol) {
const entityName = symbolToName(symbol, /*expectsIdentifier*/ false);
return createTypeQueryNode(entityName);
}
}
function typeReferenceToTypeNode(type: TypeReference) {
const typeArguments: Type[] = type.typeArguments || emptyArray;
if (type.target === globalArrayType) {
const elementType = typeToTypeNodeHelper(typeArguments[0]);
return createArrayTypeNode(elementType);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
if (typeArguments.length > 0) {
const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)));
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
return createTupleTypeNode(tupleConstituentNodes);
}
} }
else if (type.target.objectFlags & ObjectFlags.Tuple) { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyTuple)) {
return createTupleTypeNode(typeArguments.length > 0 ? mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type))) : undefined); context.encounteredError = true;
} }
else { return undefined;
const outerTypeParameters = type.target.outerTypeParameters; }
let i = 0; else {
let qualifiedName: QualifiedName | undefined = undefined; const outerTypeParameters = type.target.outerTypeParameters;
if (outerTypeParameters) { let i = 0;
const length = outerTypeParameters.length; let qualifiedName: QualifiedName | undefined = undefined;
while (i < length) { if (outerTypeParameters) {
// Find group of type arguments for type parameters with the same declaring container. const length = outerTypeParameters.length;
const start = i; while (i < length) {
const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]); // Find group of type arguments for type parameters with the same declaring container.
do { const start = i;
i++; const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]);
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent); do {
// When type parameters are their own type arguments for the whole group (i.e. we have i++;
// the default outer type arguments), we don't show the group. } while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { // When type parameters are their own type arguments for the whole group (i.e. we have
const qualifiedNamePart = symbolToName(parent, enclosingDeclaration, /*expectsIdentifier*/ true, flags); // the default outer type arguments), we don't show the group.
if (!qualifiedName) { if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true);
} if (!qualifiedName) {
else { qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined);
Debug.assert(!qualifiedName.right); }
qualifiedName.right = qualifiedNamePart; else {
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); Debug.assert(!qualifiedName.right);
} qualifiedName.right = qualifiedNamePart;
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined);
} }
} }
} }
let entityName: EntityName = undefined;
const nameIdentifier = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ true, flags);
if (qualifiedName) {
Debug.assert(!qualifiedName.right);
qualifiedName.right = nameIdentifier;
entityName = qualifiedName;
}
else {
entityName = nameIdentifier;
}
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
const typeArgumentNodes = mapToTypeNodeArray(typeArguments.length > 0 ? typeArguments.slice(i, typeParameterCount - i) : undefined);
return createTypeReferenceNode(entityName, typeArgumentNodes);
} }
let entityName: EntityName = undefined;
const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true);
if (qualifiedName) {
Debug.assert(!qualifiedName.right);
qualifiedName.right = nameIdentifier;
entityName = qualifiedName;
}
else {
entityName = nameIdentifier;
}
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
const typeArgumentNodes = typeArguments.length > 0 ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i)) : undefined;
return createTypeReferenceNode(entityName, typeArgumentNodes);
}
}
function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] {
const typeElements: TypeElement[] = [];
for (const signature of resolvedType.callSignatures) {
typeElements.push(<CallSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature));
}
for (const signature of resolvedType.constructSignatures) {
typeElements.push(<ConstructSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature));
}
if (resolvedType.stringIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String));
}
if (resolvedType.numberIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number));
} }
function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { const properties = resolvedType.properties;
const typeElements: TypeElement[] = []; if (!properties) {
for (const signature of resolvedType.callSignatures) { return typeElements;
typeElements.push(<CallSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, enclosingDeclaration, flags)); }
}
for (const signature of resolvedType.constructSignatures) {
typeElements.push(<ConstructSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, enclosingDeclaration, flags));
}
if (resolvedType.stringIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, enclosingDeclaration, flags));
}
if (resolvedType.numberIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, enclosingDeclaration, flags));
}
const properties = resolvedType.properties; for (const propertySymbol of properties) {
if (!properties) { const propertyType = getTypeOfSymbol(propertySymbol);
return typeElements; const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement;
if (!oldDeclaration) {
return;
} }
const propertyName = oldDeclaration.name;
for (const propertySymbol of properties) { const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;
const propertyType = getTypeOfSymbol(propertySymbol); if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) {
const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement; const signatures = getSignaturesOfType(propertyType, SignatureKind.Call);
if (!oldDeclaration) { for (const signature of signatures) {
return; const methodDeclaration = <MethodSignature>signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature);
methodDeclaration.name = propertyName;
methodDeclaration.questionToken = optionalToken;
typeElements.push(methodDeclaration);
} }
const propertyName = oldDeclaration.name; }
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; else {
if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) {
const signatures = getSignaturesOfType(propertyType, SignatureKind.Call);
for (const signature of signatures) {
const methodDeclaration = <MethodSignature>signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, enclosingDeclaration, flags);
methodDeclaration.name = propertyName;
methodDeclaration.questionToken = optionalToken;
typeElements.push(methodDeclaration);
}
}
else {
// TODO(aozgaa): should we create a node with explicit or implict any? // TODO(aozgaa): should we create a node with explicit or implict any?
const propertyTypeNode = propertyType ? typeToTypeNodeWorker(propertyType) : createKeywordTypeNode(SyntaxKind.AnyKeyword); const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType) : createKeywordTypeNode(SyntaxKind.AnyKeyword);
typeElements.push(createPropertySignature( typeElements.push(createPropertySignature(
propertyName, propertyName,
optionalToken, optionalToken,
propertyTypeNode, propertyTypeNode,
/*initializer*/undefined)); /*initializer*/undefined));
}
} }
return typeElements.length ? typeElements : undefined;
} }
return typeElements.length ? typeElements : undefined;
} }
} }
function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration: Node, flags: NodeBuilderFlags): IndexSignatureDeclaration { function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration {
const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
const name = getNameFromIndexInfo(indexInfo); const name = getNameFromIndexInfo(indexInfo);
@ -2578,7 +2612,7 @@ namespace ts {
/*questionToken*/ undefined, /*questionToken*/ undefined,
indexerTypeNode, indexerTypeNode,
/*initializer*/ undefined); /*initializer*/ undefined);
const typeNode = typeToTypeNodeHelper(indexInfo.type, enclosingDeclaration, flags); const typeNode = typeToTypeNodeHelper(indexInfo.type);
return createIndexSignatureDeclaration( return createIndexSignatureDeclaration(
[indexingParameter], [indexingParameter],
typeNode, typeNode,
@ -2586,37 +2620,37 @@ namespace ts {
indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined); indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined);
} }
function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, enclosingDeclaration: Node, flags: NodeBuilderFlags): SignatureDeclaration { function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind): SignatureDeclaration {
const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, enclosingDeclaration, flags)); const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter));
const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, enclosingDeclaration, flags)); const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter));
const returnTypeNode = typeToTypeNodeExceptAny(getReturnTypeOfSignature(signature)); const returnTypeNode = typeToTypeNodeExceptAny(getReturnTypeOfSignature(signature));
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode); return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode);
function typeToTypeNodeExceptAny(type: Type): TypeNode | undefined { function typeToTypeNodeExceptAny(type: Type): TypeNode | undefined {
const typeNode = type && typeToTypeNodeHelper(type, enclosingDeclaration, flags); const typeNode = type && typeToTypeNodeHelper(type);
return typeNode && typeNode.kind !== SyntaxKind.AnyKeyword ? typeNode : undefined; return typeNode && typeNode.kind !== SyntaxKind.AnyKeyword ? typeNode : undefined;
} }
} }
function typeParameterToDeclaration(type: TypeParameter, enclosingDeclaration: Node, flags: NodeBuilderFlags): TypeParameterDeclaration { function typeParameterToDeclaration(type: TypeParameter): TypeParameterDeclaration {
if (!(type && type.symbol && type.flags & TypeFlags.TypeParameter)) { if (!(type && type.symbol && type.flags & TypeFlags.TypeParameter)) {
return undefined; return undefined;
} }
const constraint = getConstraintFromTypeParameter(type); const constraint = getConstraintFromTypeParameter(type);
const constraintNode = constraint && typeToTypeNodeHelper(constraint, enclosingDeclaration, flags); const constraintNode = constraint && typeToTypeNodeHelper(constraint);
const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameter = getDefaultFromTypeParameter(type);
const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, enclosingDeclaration, flags); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter);
const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ true, flags); const name = symbolToName(type.symbol, /*expectsIdentifier*/ true);
return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode);
} }
function symbolToParameterDeclaration(parameterSymbol: Symbol, enclosingDeclaration: Node, flags: NodeBuilderFlags): ParameterDeclaration { function symbolToParameterDeclaration(parameterSymbol: Symbol): ParameterDeclaration {
const parameterDeclaration = parameterSymbol.declarations[0] as ParameterDeclaration; const parameterDeclaration = parameterSymbol.declarations[0] as ParameterDeclaration;
const parameterType = getTypeOfSymbol(parameterSymbol); const parameterType = getTypeOfSymbol(parameterSymbol);
const parameterTypeNode = typeToTypeNodeHelper(parameterType, enclosingDeclaration, flags); const parameterTypeNode = typeToTypeNodeHelper(parameterType);
// TODO(aozgaa): check initializer accessibility correctly. // TODO(aozgaa): check initializer accessibility correctly.
const parameterNode = createParameter( const parameterNode = createParameter(
parameterDeclaration.decorators, parameterDeclaration.decorators,
@ -2630,15 +2664,15 @@ namespace ts {
return parameterNode; return parameterNode;
} }
function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, expectsIdentifier: true, flags: NodeBuilderFlags): Identifier; function symbolToName(symbol: Symbol, expectsIdentifier: true): Identifier;
function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, expectsIdentifier: false, flags: NodeBuilderFlags): EntityName; function symbolToName(symbol: Symbol, expectsIdentifier: false): EntityName;
function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, expectsIdentifier: boolean, flags: NodeBuilderFlags): EntityName { function symbolToName(symbol: Symbol, expectsIdentifier: boolean): EntityName {
let parentSymbol: Symbol; let parentSymbol: Symbol;
// Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration.
let chain: Symbol[]; let chain: Symbol[];
const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
if (!isTypeParameter && enclosingDeclaration) { if (!isTypeParameter && context.enclosingDeclaration) {
chain = getSymbolChain(symbol, SymbolFlags.None, /*endOfChain*/ true); chain = getSymbolChain(symbol, SymbolFlags.None, /*endOfChain*/ true);
Debug.assert(chain && chain.length > 0); Debug.assert(chain && chain.length > 0);
} }
@ -2648,8 +2682,10 @@ namespace ts {
parentSymbol = undefined; parentSymbol = undefined;
if (expectsIdentifier && chain.length !== 1) { if (expectsIdentifier && chain.length !== 1
encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowQualifedNameInPlaceOfIdentifier); && !context.encounteredError
&& !(context.flags & NodeBuilderFlags.allowQualifedNameInPlaceOfIdentifier)) {
context.encounteredError = true;
} }
return createEntityNameFromSymbolChain(chain, chain.length - 1); return createEntityNameFromSymbolChain(chain, chain.length - 1);
@ -2672,11 +2708,12 @@ namespace ts {
} }
} }
if (typeParameters && typeParameters.length > 0) { if (typeParameters && typeParameters.length > 0) {
encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowTypeParameterInQualifiedName); if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowTypeParameterInQualifiedName)) {
context.encounteredError = true;
}
const writer = getSingleLineStringWriter(); const writer = getSingleLineStringWriter();
const displayBuilder = getSymbolDisplayBuilder(); const displayBuilder = getSymbolDisplayBuilder();
displayBuilder.buildDisplayForTypeParametersAndDelimiters(typeParameters, writer, enclosingDeclaration, 0); displayBuilder.buildDisplayForTypeParametersAndDelimiters(typeParameters, writer, context.enclosingDeclaration, 0);
typeParameterString = writer.string(); typeParameterString = writer.string();
releaseStringWriter(writer); releaseStringWriter(writer);
@ -2691,10 +2728,10 @@ namespace ts {
/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ /** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */
function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined { function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined {
let accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/false); let accessibleSymbolChain = getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/false);
if (!accessibleSymbolChain || if (!accessibleSymbolChain ||
needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent. // Go up and add our parent.
const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol); const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
@ -2731,7 +2768,9 @@ namespace ts {
if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) {
return declarationNameToString((<VariableDeclaration>declaration.parent).name); return declarationNameToString((<VariableDeclaration>declaration.parent).name);
} }
encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowAnonymousIdentifier); if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowAnonymousIdentifier)) {
context.encounteredError = true;
}
switch (declaration.kind) { switch (declaration.kind) {
case SyntaxKind.ClassExpression: case SyntaxKind.ClassExpression:
return "(Anonymous class)"; return "(Anonymous class)";

View file

@ -238,10 +238,10 @@ namespace ts {
: node; : node;
} }
export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: NodeArray<TypeNode> | undefined) { export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: TypeNode[] | undefined) {
const typeReference = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode; const typeReference = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode;
typeReference.typeName = asName(typeName); typeReference.typeName = asName(typeName);
typeReference.typeArguments = typeArguments; typeReference.typeArguments = asNodeArray(typeArguments);
return typeReference; return typeReference;
} }

View file

@ -2548,7 +2548,9 @@ namespace ts {
allowThisInObjectLiteral = 1 << 0, allowThisInObjectLiteral = 1 << 0,
allowQualifedNameInPlaceOfIdentifier = 1 << 1, allowQualifedNameInPlaceOfIdentifier = 1 << 1,
allowTypeParameterInQualifiedName = 1 << 2, allowTypeParameterInQualifiedName = 1 << 2,
allowAnonymousIdentifier = 1 << 3 allowAnonymousIdentifier = 1 << 3,
allowEmptyUnionOrIntersection = 1 << 4,
allowEmptyTuple = 1 << 5
} }
export interface SymbolDisplayBuilder { export interface SymbolDisplayBuilder {