From 1f3029d4f15e8d6fa8617318a8655c71756ce2bf Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 23 Mar 2017 08:34:58 -0700 Subject: [PATCH 1/4] error handling --- src/compiler/checker.ts | 960 +++++++++++++++++++++------------------- src/compiler/types.ts | 23 +- 2 files changed, 517 insertions(+), 466 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b681d1298d2..c981d0673b8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -61,6 +61,7 @@ namespace ts { const noImplicitThis = compilerOptions.noImplicitThis === undefined ? compilerOptions.strict : compilerOptions.noImplicitThis; const emitResolver = createResolver(); + let nodeBuilderCache: NodeBuilder | undefined; const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined"); undefinedSymbol.declarations = []; @@ -106,9 +107,9 @@ namespace ts { getParameterType: getTypeAtPosition, getReturnTypeOfSignature, getNonNullableType, - typeToTypeNode, - indexInfoToIndexSignatureDeclaration, - signatureToSignatureDeclaration, + typeToTypeNode: getNodeBuilder().typeToTypeNode, + indexInfoToIndexSignatureDeclaration: getNodeBuilder().indexInfoToIndexSignatureDeclaration, + signatureToSignatureDeclaration: getNodeBuilder().signatureToSignatureDeclaration, getSymbolsInScope: (location, meaning) => { location = getParseTreeNode(location); return location ? getSymbolsInScope(location, meaning) : []; @@ -2191,476 +2192,529 @@ namespace ts { return result; } - function typeParameterToDeclaration(type: TypeParameter, enclosingDeclaration?: Node): TypeParameterDeclaration { - if (!(type && type.symbol && type.flags & TypeFlags.TypeParameter)) { - return undefined; + function getNodeBuilder(): NodeBuilder { + if (nodeBuilderCache) { + return nodeBuilderCache; } - const constraint = typeToTypeNode(getConstraintFromTypeParameter(type), enclosingDeclaration); - const defaultParameter = typeToTypeNode(getDefaultFromTypeParameter(type), enclosingDeclaration); - - // TODO: use method internal to typeToTypeNode. - const name = symbolToString(type.symbol); - return createTypeParameterDeclaration(name, constraint, defaultParameter); - } - - function symbolToParameterDeclaration(parameterSymbol: Symbol, enclosingDeclaration?: Node): ParameterDeclaration { - const parameterDeclaration = parameterSymbol.declarations[0] as ParameterDeclaration; - const parameterType = getTypeOfSymbol(parameterSymbol); - const parameterTypeNode = typeToTypeNode(parameterType, enclosingDeclaration); - // TODO: check initializer accessibility correctly. - const parameterNode = createParameter( - parameterDeclaration.decorators, - parameterDeclaration.modifiers, - parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken), - // Clone name to remove trivia. - getSynthesizedClone(parameterDeclaration.name), - parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken), - parameterTypeNode, - parameterDeclaration.initializer); - return parameterNode; - } - - function signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node): SignatureDeclaration { - const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, enclosingDeclaration)); - const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, enclosingDeclaration)); - const type = typeToTypeNodeExceptAny(getReturnTypeOfSignature(signature)); - - return createSignatureDeclaration(kind, typeParameters, parameters, type); - - function typeToTypeNodeExceptAny(type: Type): TypeNode | undefined { - const typeNode = typeToTypeNode(type, enclosingDeclaration); - return typeNode && typeNode.kind !== SyntaxKind.AnyKeyword ? typeNode : undefined; - } - } - - - // function typeToDisplayParts - - function typeToTypeNode(type: Type, enclosingDeclaration?: Node, returnNodeOnError?: boolean): TypeNode { let encounteredError = false; - let inObjectTypeLiteral = false; - let checkAlias = true; - let symbolStack: Symbol[] = undefined; - const result = typeToTypeNodeWorker(type); - // returnNodeOnError = true; // TODO: unset. - return encounteredError && !returnNodeOnError ? undefined: result; + nodeBuilderCache = { + typeToTypeNode, + indexInfoToIndexSignatureDeclaration, + signatureToSignatureDeclaration + } - function typeToTypeNodeWorker(type: Type): TypeNode { - if (!type) { - encounteredError = true; + return nodeBuilderCache; + + function typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode { + const helper = () => typeToTypeNodeHelper(type, enclosingDeclaration, flags); + return callHelperWithErrorHandling(helper); + } + + function indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration { + const helper = () => indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, enclosingDeclaration, flags); + return callHelperWithErrorHandling(helper); + } + + function signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration { + const helper = () => signatureToSignatureDeclarationHelper(signature, kind, enclosingDeclaration, flags); + return callHelperWithErrorHandling(helper); + } + + function callHelperWithErrorHandling(nodeBuilderHelper: () => T): T | undefined { + const encounteredErrorCache = encounteredError; + const resultingNode = nodeBuilderHelper(); + const result = encounteredError ? undefined : resultingNode; + encounteredError = encounteredErrorCache; + return result; + } + + function typeToTypeNodeHelper(type: Type, enclosingDeclaration: Node, flags: NodeBuilderFlags): TypeNode { + let inObjectTypeLiteral = false; + let checkAlias = true; + let symbolStack: Symbol[] = undefined; + + return typeToTypeNodeWorker(type, flags); + + function typeToTypeNodeWorker(type: Type, flags: NodeBuilderFlags): TypeNode { + if (!type) { + encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowUndefinedNode); + return undefined; + } + flags = flags & ~NodeBuilderFlags.allowUndefinedNode; + + 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, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + return createTypeReferenceNode(name, /*typeArguments*/ undefined); + } + if (type.flags & (TypeFlags.StringLiteral)) { + return createLiteralTypeNode((createLiteral((type).text))); + } + if (type.flags & (TypeFlags.NumberLiteral)) { + return createLiteralTypeNode((createNumericLiteral((type).text))); + } + if (type.flags & TypeFlags.BooleanLiteral) { + return (type).intrinsicName === "true" ? createTrue() : createFalse(); + } + if (type.flags & TypeFlags.EnumLiteral) { + const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + 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 (inObjectTypeLiteral) { + encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowThisInObjectLiteral); + } + return createThis(); + } + + const objectFlags = getObjectFlags(type); + + if (objectFlags & ObjectFlags.Reference) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + return typeReferenceToTypeReferenceNode(type); + } + if (objectFlags & ObjectFlags.ClassOrInterface) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + // TODO(aozgaa): handle type arguments. + return createTypeReferenceNode(name, /*typeArguments*/ undefined); + } + if (type.flags & TypeFlags.TypeParameter) { + const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ 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) { + const name = symbolToName(type.aliasSymbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + const typeArgumentNodes = mapToTypeNodeArray(type.aliasTypeArguments); + return createTypeReferenceNode(name, typeArgumentNodes); + } + checkAlias = false; + + if (type.flags & TypeFlags.Union) { + return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, mapToTypeNodeArray(formatUnionTypes((type).types))); + } + + if (type.flags & TypeFlags.Intersection) { + return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types)); + } + + if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + // The type is an object literal type. + return createAnonymousTypeNode(type); + } + + if (type.flags & TypeFlags.Index) { + const indexedType = (type).type; + const indexTypeNode = typeToTypeNodeWorker(indexedType, flags); + return createTypeOperatorNode(indexTypeNode); + } + if (type.flags & TypeFlags.IndexedAccess) { + const objectTypeNode = typeToTypeNodeWorker((type).objectType, flags); + const indexTypeNode = typeToTypeNodeWorker((type).indexType, flags); + return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); + } + + Debug.fail("Should be unreachable."); + + function mapToTypeNodeArray(types: Type[]): NodeArray { + return types && asNodeArray(types.map(typeToTypeNodeWorker) as TypeNode[]); + } + + function createMappedTypeNodeFromType(type: MappedType) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + const typeParameter = getTypeParameterFromMappedType(type); + const typeParameterNode = typeParameterToDeclaration(typeParameter, enclosingDeclaration, flags); + + const templateTypeNode = typeToTypeNodeWorker(getTemplateTypeFromMappedType(type), flags | NodeBuilderFlags.allowUndefinedNode); + const readonlyToken = (type).declaration && (type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; + const questionToken = (type).declaration && (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, /*mustBeIdentifier*/ false, flags); + return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); + } + else { + return createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + } + 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 (!symbolStack) { + symbolStack = []; + } + symbolStack.push(symbol); + let result = createTypeNodeFromObjectType(type); + 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(symbolStack, symbol); // it is type of the symbol uses itself recursively + } + } + } + + function createTypeNodeFromObjectType(type: ObjectType): TypeNode { + if (type.objectFlags & ObjectFlags.Mapped) { + if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { + return createMappedTypeNodeFromType(type); + } + } + + const resolved = resolveStructuredTypeMembers(type); + if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { + if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { + return createTypeLiteralNode(/*members*/ undefined); + } + + if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { + const signature = resolved.callSignatures[0]; + return signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, enclosingDeclaration, flags); + } + if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { + const signature = resolved.constructSignatures[0]; + return signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, enclosingDeclaration, flags); + } + } + + const saveInObjectTypeLiteral = inObjectTypeLiteral; + inObjectTypeLiteral = true; + const members = createTypeNodesFromResolvedType(resolved); + inObjectTypeLiteral = saveInObjectTypeLiteral; + return createTypeLiteralNode(members); + } + + function createTypeQueryNodeFromType(type: Type) { + const symbol = type.symbol; + if (symbol) { + const entityName = symbolToName(symbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + return createTypeQueryNode(entityName); + } + } + + function typeReferenceToTypeReferenceNode(type: TypeReference) { + const typeArguments: Type[] = type.typeArguments || emptyArray; + if (type.target === globalArrayType) { + const elementType = typeToTypeNodeWorker(typeArguments[0], flags); + return createArrayTypeNode(elementType); + } + else if (type.target.objectFlags & ObjectFlags.Tuple) { + return createTupleTypeNode(typeArguments.length > 0 ? mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type))) : undefined); + } + else { + const outerTypeParameters = type.target.outerTypeParameters; + let i = 0; + let qualifiedName: QualifiedName | undefined = undefined; + if (outerTypeParameters) { + const length = outerTypeParameters.length; + while (i < length) { + // Find group of type arguments for type parameters with the same declaring container. + const start = i; + const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]); + do { + i++; + } while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent); + // When type parameters are their own type arguments for the whole group (i.e. we have + // the default outer type arguments), we don't show the group. + if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { + const qualifiedNamePart = symbolToName(parent, enclosingDeclaration, /*mustBeIdentifier*/ true, flags); + if (!qualifiedName) { + qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); + } + else { + Debug.assert(!qualifiedName.right); + qualifiedName.right = qualifiedNamePart; + qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); + } + } + } + } + let entityName: EntityName = undefined; + const nameIdentifier = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ 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); + } + } + + function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { + const typeElements: TypeElement[] = []; + for (const signature of resolvedType.callSignatures) { + typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, enclosingDeclaration, flags)); + } + for (const signature of resolvedType.constructSignatures) { + typeElements.push(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; + if (!properties) { + return typeElements; + } + + for (const propertySymbol of properties) { + const propertyType = getTypeOfSymbol(propertySymbol); + const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement; + if (!oldDeclaration) { + return; + } + const propertyName = oldDeclaration.name; + const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;; + if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) { + const signatures = getSignaturesOfType(propertyType, SignatureKind.Call); + for (const signature of signatures) { + const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, enclosingDeclaration, flags); + methodDeclaration.name = propertyName; + methodDeclaration.questionToken = optionalToken; + typeElements.push(methodDeclaration); + } + } + else { + typeElements.push(createPropertySignature( + propertyName, + optionalToken, + typeToTypeNodeWorker(propertyType, flags), + /*initializer*/undefined)); + } + } + return typeElements.length ? typeElements : undefined; + } + } + } + + function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration: Node, flags: NodeBuilderFlags): IndexSignatureDeclaration { + const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); + + const name = getNameFromIndexInfo(indexInfo); + const indexingParameter = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + name, + /*questionToken*/ undefined, + indexerTypeNode, + /*initializer*/ undefined); + const typeNode = typeToTypeNodeHelper(indexInfo.type, enclosingDeclaration, flags); + return createIndexSignatureDeclaration( + [indexingParameter], + typeNode, + /*decorators*/ undefined, + indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined); + } + + function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, enclosingDeclaration: Node, flags: NodeBuilderFlags): SignatureDeclaration { + + const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, enclosingDeclaration, flags)); + const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, enclosingDeclaration, flags)); + const type = typeToTypeNodeExceptAny(getReturnTypeOfSignature(signature)); + + return createSignatureDeclaration(kind, typeParameters, parameters, type); + + function typeToTypeNodeExceptAny(type: Type): TypeNode | undefined { + // Note, this call will *not* mark encounteredError. + const typeNode = typeToTypeNode(type, enclosingDeclaration); + return typeNode && typeNode.kind !== SyntaxKind.AnyKeyword ? typeNode : undefined; + } + } + + function typeParameterToDeclaration(type: TypeParameter, enclosingDeclaration: Node, flags: NodeBuilderFlags): TypeParameterDeclaration { + if (!(type && type.symbol && type.flags & TypeFlags.TypeParameter)) { return undefined; } - if (type.flags & TypeFlags.Any) { - return createKeywordTypeNode(SyntaxKind.AnyKeyword); + const constraint = typeToTypeNodeHelper(getConstraintFromTypeParameter(type), enclosingDeclaration, flags | NodeBuilderFlags.allowUndefinedNode); + const defaultParameter = typeToTypeNodeHelper(getDefaultFromTypeParameter(type), enclosingDeclaration, flags | NodeBuilderFlags.allowUndefinedNode); +; + const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ true, flags); + return createTypeParameterDeclaration(name, constraint, defaultParameter); + } + + function symbolToParameterDeclaration(parameterSymbol: Symbol, enclosingDeclaration: Node, flags: NodeBuilderFlags): ParameterDeclaration { + const parameterDeclaration = parameterSymbol.declarations[0] as ParameterDeclaration; + const parameterType = getTypeOfSymbol(parameterSymbol); + const parameterTypeNode = typeToTypeNodeHelper(parameterType, enclosingDeclaration, flags); + // TODO(aozgaa): check initializer accessibility correctly. + const parameterNode = createParameter( + parameterDeclaration.decorators, + parameterDeclaration.modifiers, + parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken), + // Clone name to remove trivia. + getSynthesizedClone(parameterDeclaration.name), + parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken), + parameterTypeNode, + parameterDeclaration.initializer); + return parameterNode; + } + + function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, mustBeIdentifier: true, flags: NodeBuilderFlags): Identifier; + function symbolToName(symbol: Symbol, enclosingDeclaration: Node, mustBeIdentifier: false, flags: NodeBuilderFlags): EntityName; + function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, mustBeIdentifier: boolean, flags: NodeBuilderFlags): EntityName { + let parentSymbol: Symbol; + let meaning: SymbolFlags; + + // Get qualified name if the symbol is not a type parameter + // and there is an enclosing declaration. + let chain: Symbol[]; + const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; + if (!isTypeParameter && enclosingDeclaration) { + chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true); + // TODO: check whether type pointed to by symbol requires type arguments to be printed. + Debug.assert(chain && chain.length > 0); } - 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 = createNameFromSymbol(type.symbol); - return createTypeReferenceNode(name, /*typeArguments*/ undefined); - } - if (type.flags & (TypeFlags.StringLiteral)) { - return createLiteralTypeNode((createLiteral((type).text))); - } - if (type.flags & (TypeFlags.NumberLiteral)) { - return createLiteralTypeNode((createNumericLiteral((type).text))); - } - if(type.flags & TypeFlags.BooleanLiteral) { - return (type).intrinsicName === "true" ? createTrue() : createFalse(); - } - if (type.flags & TypeFlags.EnumLiteral) { - const name = createNameFromSymbol(type.symbol); - 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 (inObjectTypeLiteral) { - encounteredError = true; - } - return createThis(); + else { + chain = [symbol]; } - const objectFlags = getObjectFlags(type); - - if (objectFlags & ObjectFlags.Reference) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - return typeReferenceToTypeReferenceNode(type); - } - if (objectFlags & ObjectFlags.ClassOrInterface) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - const name = createNameFromSymbol(type.symbol); - // TODO: handle type arguments. - return createTypeReferenceNode(name, /*typeArguments*/ undefined); - } - if (type.flags & TypeFlags.TypeParameter) { - const name = createNameFromSymbol(type.symbol); - // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. - return createTypeReferenceNode(name, /*typeArguments*/ undefined); + parentSymbol = undefined; + if (mustBeIdentifier && chain.length !== 1) { + encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowQualifedNameInPlaceOfIdentifier); + // TODO: failing to get an identifier when we expect one generates an unprintable node. + // Should error handling be more severe? } + return createEntityNameFromSymbolChain(chain, chain.length - 1); - if (checkAlias && type.aliasSymbol) { - const name = createNameFromSymbol(type.aliasSymbol); - const typeArgumentNodes = mapToTypeNodeArray(type.aliasTypeArguments); - return createTypeReferenceNode(name, typeArgumentNodes); - } - checkAlias = false; - - if (type.flags & TypeFlags.Union) { - return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, mapToTypeNodeArray(formatUnionTypes((type).types))); - } - - if (type.flags & TypeFlags.Intersection) { - return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types)); - } - - if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - // The type is an object literal type. - return createAnonymousTypeNode(type); - } - - // TODO (aozgaa): implement string and number literals here once there is a testable case. - - if (type.flags & TypeFlags.Index) { - const indexedType = (type).type; - const indexTypeNode = typeToTypeNodeWorker(indexedType); - return createTypeOperatorNode(indexTypeNode); - } - if (type.flags & TypeFlags.IndexedAccess) { - const objectTypeNode = typeToTypeNodeWorker((type).objectType); - const indexTypeNode = typeToTypeNodeWorker((type).indexType); - return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); - } - - Debug.fail("Should be unreachable."); - - function mapToTypeNodeArray(types: Type[]): NodeArray { - return types && asNodeArray(types.map(typeToTypeNodeWorker) as TypeNode[]); - } - - function createMappedTypeNodeFromType(type: MappedType) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - const typeParameter = getTypeParameterFromMappedType(type); - const typeParameterNode = typeParameterToDeclaration(typeParameter, enclosingDeclaration); - - const templateTypeNode = typeToTypeNode(getTemplateTypeFromMappedType(type), enclosingDeclaration); - const readonlyToken = (type).declaration && (type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; - const questionToken = (type).declaration && (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 = createNameFromSymbol(typeAlias); - return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); - } - else { - return createKeywordTypeNode(SyntaxKind.AnyKeyword); - } + function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { + Debug.assert(chain && 0 <= index && index < chain.length); + // const parentIndex = index - 1; + const symbol = chain[index]; + let typeParameterString = ""; + if (index > 0) { + // TODO: is the parentSymbol wrong? + const parentSymbol = chain[index - 1]; + let typeParameters: TypeParameter[]; + if (getCheckFlags(symbol) & CheckFlags.Instantiated) { + typeParameters = getTypeParametersOfClassOrInterface(parentSymbol); } 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 (!symbolStack) { - symbolStack = []; + const targetSymbol = getTargetSymbol(parentSymbol); + if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) { + typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); } - symbolStack.push(symbol); - let result = createTypeNodeFromObjectType(type); - symbolStack.pop(); - return result; } - } - else { - // Anonymous types without a symbol are never circular. - return createTypeNodeFromObjectType(type); - } + if (typeParameters && typeParameters.length > 0) { + encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowTypeParameterInQualifiedName);; + const writer = getSingleLineStringWriter(); + const displayBuilder = getSymbolDisplayBuilder(); + displayBuilder.buildDisplayForTypeParametersAndDelimiters(typeParameters, writer, enclosingDeclaration, 0); + typeParameterString = writer.string(); + releaseStringWriter(writer); - 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 } } + const symbolName = getNameOfSymbol(symbol); + const symbolNameWithTypeParameters = typeParameterString.length > 0 ? `${symbolName}<${typeParameterString}>` : symbolName; + let identifier = createIdentifier(symbolNameWithTypeParameters); + + return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; } - function createTypeNodeFromObjectType(type: ObjectType): TypeNode { - if (type.objectFlags & ObjectFlags.Mapped) { - if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { - return createMappedTypeNodeFromType(type); - } - } + /** @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 { + let accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/false); - const resolved = resolveStructuredTypeMembers(type); - if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { - if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { - return createTypeLiteralNode(/*members*/ undefined); - } + if (!accessibleSymbolChain || + needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { - if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { - const signature = resolved.callSignatures[0]; - return signatureToSignatureDeclaration(signature, SyntaxKind.FunctionType, enclosingDeclaration); - } - if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { - const signature = resolved.constructSignatures[0]; - return signatureToSignatureDeclaration(signature, SyntaxKind.ConstructorType, enclosingDeclaration); - } - } - - const saveInObjectTypeLiteral = inObjectTypeLiteral; - inObjectTypeLiteral = true; - const members = createTypeNodesFromResolvedType(resolved); - inObjectTypeLiteral = saveInObjectTypeLiteral; - return createTypeLiteralNode(members); - } - - function createTypeQueryNodeFromType(type: Type) { - const symbol = type.symbol; - if (symbol) { - const entityName = createNameFromSymbol(symbol); - return createTypeQueryNode(entityName); - } - } - - function typeReferenceToTypeReferenceNode(type: TypeReference) { - const typeArguments: Type[] = type.typeArguments || emptyArray; - if (type.target === globalArrayType) { - const elementType = typeToTypeNodeWorker(typeArguments[0]); - return createArrayTypeNode(elementType); - } - else if (type.target.objectFlags & ObjectFlags.Tuple) { - return createTupleTypeNode(typeArguments.length > 0 ? mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type))) : undefined); - } - else { - const outerTypeParameters = type.target.outerTypeParameters; - let i = 0; - let qualifiedName: QualifiedName | undefined = undefined; - if (outerTypeParameters) { - const length = outerTypeParameters.length; - while (i < length) { - // Find group of type arguments for type parameters with the same declaring container. - const start = i; - const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]); - do { - i++; - } while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent); - // When type parameters are their own type arguments for the whole group (i.e. we have - // the default outer type arguments), we don't show the group. - if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - const qualifiedNamePart = createNameFromSymbol(parent, /*mustBeIdentifier*/ true); - if (!qualifiedName) { - qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); - } - else { - Debug.assert(!qualifiedName.right); - qualifiedName.right = qualifiedNamePart; - qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); - } - } + // Go up and add our parent. + const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol); + if (parent) { + const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false); + if (parentChain) { + accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [symbol]); } } - let entityName: EntityName = undefined; - const nameIdentifier = createNameFromSymbol(type.symbol, /*mustBeIdentifier*/ true); - 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); - } - } - - function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { - const typeElements: TypeElement[] = []; - for (const signature of resolvedType.callSignatures) { - typeElements.push(signatureToSignatureDeclaration(signature, SyntaxKind.CallSignature, enclosingDeclaration)); - } - for (const signature of resolvedType.constructSignatures) { - typeElements.push(signatureToSignatureDeclaration(signature, SyntaxKind.ConstructSignature, enclosingDeclaration)); - } - if (resolvedType.stringIndexInfo) { - typeElements.push(indexInfoToIndexSignatureDeclaration(resolvedType.stringIndexInfo, IndexKind.String, enclosingDeclaration)); - } - if (resolvedType.numberIndexInfo) { - typeElements.push(indexInfoToIndexSignatureDeclaration(resolvedType.numberIndexInfo, IndexKind.Number, enclosingDeclaration)); } - const properties = resolvedType.properties; - if (!properties) { - return typeElements; + if (accessibleSymbolChain) { + return accessibleSymbolChain; } - for (const propertySymbol of properties) { - const propertyType = getTypeOfSymbol(propertySymbol); - const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement; - if (!oldDeclaration) { - return; - } - const propertyName = oldDeclaration.name; - const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;; - if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) { - const signatures = getSignaturesOfType(propertyType, SignatureKind.Call); - for (const signature of signatures) { - const methodDeclaration = signatureToSignatureDeclaration(signature, SyntaxKind.MethodSignature, enclosingDeclaration); - methodDeclaration.name = propertyName; - methodDeclaration.questionToken = optionalToken; - typeElements.push(methodDeclaration); - } - } - else { - typeElements.push(createPropertySignature( - propertyName, - optionalToken, - typeToTypeNodeWorker(propertyType), - /*initializer*/undefined)); - } - } - return typeElements.length ? typeElements : undefined; - } + else if ( + // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. + endOfChain || + // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) + !(!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) && + // If a parent symbol is an anonymous type, don't write it. + !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) { - function createNameFromSymbol(symbol: Symbol, mustBeIdentifier: true): Identifier; - function createNameFromSymbol(symbol: Symbol, mustBeIdentifier?: false): EntityName; - function createNameFromSymbol(symbol: Symbol, mustBeIdentifier: boolean | undefined): EntityName { - let parentSymbol: Symbol; - let meaning: SymbolFlags; - - // Get qualified name if the symbol is not a type parameter - // and there is an enclosing declaration. - let chain: Symbol[]; - const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; - if (!isTypeParameter && enclosingDeclaration) { - chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true); - // TODO: check whether type pointed to by symbol requires type arguments to be printed. - Debug.assert(chain && chain.length > 0); - } - else { - chain = [symbol]; - } - - parentSymbol = undefined; - if(mustBeIdentifier && chain.length !== 1) { - encounteredError = true; - // TODO: failing to get an identifier when we expect one generates an unprintable node. - // Should error handling be more severe? - } - return createEntityNameFromSymbolChain(chain, chain.length - 1); - - function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { - Debug.assert(chain && 0 <= index && index < chain.length); - // const parentIndex = index - 1; - const symbol = chain[index]; - let typeParameterString = ""; - if(index > 0) { - // TODO: is the parentSymbol wrong? - const parentSymbol = chain[index - 1]; - let typeParameters: TypeParameter[]; - if(getCheckFlags(symbol) & CheckFlags.Instantiated) { - typeParameters = getTypeParametersOfClassOrInterface(parentSymbol); - } - else { - const targetSymbol = getTargetSymbol(parentSymbol); - if(targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) { - typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - } - } - if(typeParameters && typeParameters.length > 0) { - encounteredError = true; - const writer = getSingleLineStringWriter(); - const displayBuilder = getSymbolDisplayBuilder(); - displayBuilder.buildDisplayForTypeParametersAndDelimiters(typeParameters, writer, enclosingDeclaration, 0); - typeParameterString = writer.string(); - releaseStringWriter(writer); - - } - } - const symbolName = getNameOfSymbol(symbol); - const symbolNameWithTypeParameters = typeParameterString.length > 0 ? `${symbolName}<${typeParameterString}>` : symbolName; - let identifier = createIdentifier(symbolNameWithTypeParameters); - - return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; - } - - /** @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 { - let accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/false); - - if (!accessibleSymbolChain || - needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { - - // Go up and add our parent. - const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol); - if (parent) { - const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false); - if (parentChain) { - accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [symbol]); - } - } - } - - if (accessibleSymbolChain) { - return accessibleSymbolChain; - } - - else if ( - // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. - endOfChain || - // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) - !(!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) && - // If a parent symbol is an anonymous type, don't write it. - !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) { - - return [symbol]; - } + return [symbol]; } } @@ -2673,7 +2727,7 @@ namespace ts { if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { return declarationNameToString((declaration.parent).name); } - encounteredError = true; + encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowAnonymousIdentifier); switch (declaration.kind) { case SyntaxKind.ClassExpression: return "(Anonymous class)"; @@ -2687,26 +2741,6 @@ namespace ts { } } - function indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration: Node): IndexSignatureDeclaration { - const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); - - const name = getNameFromIndexInfo(indexInfo); - const indexingParameter = createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - name, - /*questionToken*/ undefined, - indexerTypeNode, - /*initializer*/ undefined); - const typeNode = typeToTypeNode(indexInfo.type, enclosingDeclaration); - return createIndexSignatureDeclaration( - [indexingParameter], - typeNode, - /*decorators*/ undefined, - indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined); - } - function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string { const writer = getSingleLineStringWriter(); getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5ccdd79f199..d6faf8cc647 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2473,11 +2473,11 @@ namespace ts { getNonNullableType(type: Type): Type; /** Note that the resulting nodes cannot be checked. */ - typeToTypeNode(type: Type, enclosingDeclaration: Node): TypeNode; + typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode; /** Note that the resulting nodes cannot be checked. */ - indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration: Node): IndexSignatureDeclaration; + signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration; /** Note that the resulting nodes cannot be checked. */ - signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration: Node): SignatureDeclaration; + indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolAtLocation(node: Node): Symbol; @@ -2530,6 +2530,23 @@ namespace ts { /* @internal */ getTypeCount(): number; } + /** Note that any resulting nodes cannot be checked. */ + /* @internal */ + export interface NodeBuilder { + typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode; + signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration; + indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration; + } + + export enum NodeBuilderFlags { + None = 0, + allowUndefinedNode = 1 << 0, + allowThisInObjectLiteral = 1 << 1, + allowQualifedNameInPlaceOfIdentifier = 1 << 2, + allowTypeParameterInQualifiedName = 1 << 3, + allowAnonymousIdentifier = 1 << 4 + } + export interface SymbolDisplayBuilder { buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void; From dfb4df92c8f7e5d80af3213f27e70187dd9287a9 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 23 Mar 2017 11:17:25 -0700 Subject: [PATCH 2/4] move undefined handling to caller --- src/compiler/checker.ts | 44 ++++++++++++++++++++++------------------- src/compiler/types.ts | 9 ++++----- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c981d0673b8..f996f738f12 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2235,14 +2235,14 @@ namespace ts { let checkAlias = true; let symbolStack: Symbol[] = undefined; - return typeToTypeNodeWorker(type, flags); + return typeToTypeNodeWorker(type); - function typeToTypeNodeWorker(type: Type, flags: NodeBuilderFlags): TypeNode { + function typeToTypeNodeWorker(type: Type): TypeNode { if (!type) { - encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowUndefinedNode); + encounteredError = true; + // TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)? return undefined; } - flags = flags & ~NodeBuilderFlags.allowUndefinedNode; if (type.flags & TypeFlags.Any) { return createKeywordTypeNode(SyntaxKind.AnyKeyword); @@ -2302,7 +2302,7 @@ namespace ts { if (objectFlags & ObjectFlags.Reference) { Debug.assert(!!(type.flags & TypeFlags.Object)); - return typeReferenceToTypeReferenceNode(type); + return typeReferenceToTypeNode (type); } if (objectFlags & ObjectFlags.ClassOrInterface) { Debug.assert(!!(type.flags & TypeFlags.Object)); @@ -2339,19 +2339,19 @@ namespace ts { if (type.flags & TypeFlags.Index) { const indexedType = (type).type; - const indexTypeNode = typeToTypeNodeWorker(indexedType, flags); + const indexTypeNode = typeToTypeNodeWorker(indexedType); return createTypeOperatorNode(indexTypeNode); } if (type.flags & TypeFlags.IndexedAccess) { - const objectTypeNode = typeToTypeNodeWorker((type).objectType, flags); - const indexTypeNode = typeToTypeNodeWorker((type).indexType, flags); + const objectTypeNode = typeToTypeNodeWorker((type).objectType); + const indexTypeNode = typeToTypeNodeWorker((type).indexType); return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); } Debug.fail("Should be unreachable."); function mapToTypeNodeArray(types: Type[]): NodeArray { - return types && asNodeArray(types.map(typeToTypeNodeWorker) as TypeNode[]); + return types && asNodeArray(types.map(typeToTypeNodeWorker).filter(node => !!node)); } function createMappedTypeNodeFromType(type: MappedType) { @@ -2359,7 +2359,8 @@ namespace ts { const typeParameter = getTypeParameterFromMappedType(type); const typeParameterNode = typeParameterToDeclaration(typeParameter, enclosingDeclaration, flags); - const templateTypeNode = typeToTypeNodeWorker(getTemplateTypeFromMappedType(type), flags | NodeBuilderFlags.allowUndefinedNode); + const templateType = getTemplateTypeFromMappedType(type) + const templateTypeNode = templateType && typeToTypeNodeWorker(templateType); const readonlyToken = (type).declaration && (type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; const questionToken = (type).declaration && (type).declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; @@ -2456,10 +2457,10 @@ namespace ts { } } - function typeReferenceToTypeReferenceNode(type: TypeReference) { + function typeReferenceToTypeNode(type: TypeReference) { const typeArguments: Type[] = type.typeArguments || emptyArray; if (type.target === globalArrayType) { - const elementType = typeToTypeNodeWorker(typeArguments[0], flags); + const elementType = typeToTypeNodeWorker(typeArguments[0]); return createArrayTypeNode(elementType); } else if (type.target.objectFlags & ObjectFlags.Tuple) { @@ -2547,10 +2548,12 @@ namespace ts { } } else { + // TODO(aozgaa): should we create a node with explicit or implict any? + const propertyTypeNode = propertyType ? typeToTypeNodeWorker(propertyType) : createKeywordTypeNode(SyntaxKind.AnyKeyword); typeElements.push(createPropertySignature( propertyName, optionalToken, - typeToTypeNodeWorker(propertyType, flags), + propertyTypeNode, /*initializer*/undefined)); } } @@ -2599,11 +2602,12 @@ namespace ts { return undefined; } - const constraint = typeToTypeNodeHelper(getConstraintFromTypeParameter(type), enclosingDeclaration, flags | NodeBuilderFlags.allowUndefinedNode); - const defaultParameter = typeToTypeNodeHelper(getDefaultFromTypeParameter(type), enclosingDeclaration, flags | NodeBuilderFlags.allowUndefinedNode); -; + const constraint = getConstraintFromTypeParameter(type); + const constraintNode = constraint && typeToTypeNodeHelper(constraint, enclosingDeclaration, flags); + const defaultParameter = getDefaultFromTypeParameter(type); + const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, enclosingDeclaration, flags); const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ true, flags); - return createTypeParameterDeclaration(name, constraint, defaultParameter); + return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } function symbolToParameterDeclaration(parameterSymbol: Symbol, enclosingDeclaration: Node, flags: NodeBuilderFlags): ParameterDeclaration { @@ -2635,7 +2639,7 @@ namespace ts { const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; if (!isTypeParameter && enclosingDeclaration) { chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true); - // TODO: check whether type pointed to by symbol requires type arguments to be printed. + // TODO(aozgaa): check whether type pointed to by symbol requires type arguments to be printed. Debug.assert(chain && chain.length > 0); } else { @@ -2645,7 +2649,7 @@ namespace ts { parentSymbol = undefined; if (mustBeIdentifier && chain.length !== 1) { encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowQualifedNameInPlaceOfIdentifier); - // TODO: failing to get an identifier when we expect one generates an unprintable node. + // TODO(aozgaa): failing to get an identifier when we expect one generates an unprintable node. // Should error handling be more severe? } return createEntityNameFromSymbolChain(chain, chain.length - 1); @@ -2656,7 +2660,7 @@ namespace ts { const symbol = chain[index]; let typeParameterString = ""; if (index > 0) { - // TODO: is the parentSymbol wrong? + // TODO(aozgaa): is the parentSymbol wrong? const parentSymbol = chain[index - 1]; let typeParameters: TypeParameter[]; if (getCheckFlags(symbol) & CheckFlags.Instantiated) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d6faf8cc647..03569788d25 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2540,11 +2540,10 @@ namespace ts { export enum NodeBuilderFlags { None = 0, - allowUndefinedNode = 1 << 0, - allowThisInObjectLiteral = 1 << 1, - allowQualifedNameInPlaceOfIdentifier = 1 << 2, - allowTypeParameterInQualifiedName = 1 << 3, - allowAnonymousIdentifier = 1 << 4 + allowThisInObjectLiteral = 1 << 0, + allowQualifedNameInPlaceOfIdentifier = 1 << 1, + allowTypeParameterInQualifiedName = 1 << 2, + allowAnonymousIdentifier = 1 << 3 } export interface SymbolDisplayBuilder { From 8915cb9a0f5f12c03ddb054ab20f4a5eb00bbc7a Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 23 Mar 2017 13:36:38 -0700 Subject: [PATCH 3/4] Split signature factory methods --- src/compiler/factory.ts | 63 ++++++++++++++++++++++++------- src/compiler/visitor.ts | 35 ++++++++++++----- src/services/codefixes/helpers.ts | 8 ++-- 3 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0ae42459709..0d2dee8cfdc 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -378,28 +378,65 @@ namespace ts { } // TODO: Split according to AST nodes. - export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): T; - export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined): T; - export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, name?: string | PropertyName, questionToken?: QuestionToken): T { - const signatureDeclaration = createSynthesizedNode(kind) as T; + export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined) { + const signatureDeclaration = createSynthesizedNode(kind) as SignatureDeclaration; signatureDeclaration.typeParameters = asNodeArray(typeParameters); signatureDeclaration.parameters = asNodeArray(parameters); signatureDeclaration.type = type; - signatureDeclaration.name = asName(name); - signatureDeclaration.questionToken = questionToken; return signatureDeclaration; } - // TODO: figure out right type annotation for this function. - export function updateSignatureDeclaration(node: T, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): T; - export function updateSignatureDeclaration(node: T, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined, name: PropertyName, questionToken: QuestionToken | undefined): T; - export function updateSignatureDeclaration(node: T, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined, name?: PropertyName, questionToken?: QuestionToken): T { + export function updateSignatureDeclaration(node: SignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined) { + return node.typeParameters !== typeParameters + || node.parameters !== parameters + || node.type !== type + ? updateNode(createSignatureDeclaration(node.kind, typeParameters, parameters, type), node) + : node; + } + + export function createFunctionTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): FunctionTypeNode { + return createSignatureDeclaration(SyntaxKind.FunctionType, typeParameters, parameters, type) as FunctionTypeNode; + } + + export function updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): FunctionTypeNode { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createConstructorTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): ConstructorTypeNode { + return createSignatureDeclaration(SyntaxKind.ConstructorType, typeParameters, parameters, type) as ConstructorTypeNode; + } + export function updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): ConstructorTypeNode { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createCallSignatureDeclaration(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): CallSignatureDeclaration { + return createSignatureDeclaration(SyntaxKind.CallSignature, typeParameters, parameters, type) as CallSignatureDeclaration; + } + export function updateCallSignatureDeclaration(node: CallSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): CallSignatureDeclaration { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createConstructSignatureDeclaration(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): ConstructSignatureDeclaration { + return createSignatureDeclaration(SyntaxKind.ConstructSignature, typeParameters, parameters, type) as ConstructSignatureDeclaration; + } + export function updateConstructSignatureDeclaration(node: ConstructSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): ConstructSignatureDeclaration { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createMethodSignature(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined): MethodSignature{ + const methodSignature = createSignatureDeclaration(SyntaxKind.MethodSignature, typeParameters, parameters, type) as MethodSignature; + methodSignature.name = asName(name); + methodSignature.questionToken = questionToken; + return methodSignature; + } + + export function updateMethodSignature(node: MethodSignature, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined, name?: PropertyName, questionToken?: QuestionToken): MethodSignature { return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.name !== name || node.questionToken !== questionToken - ? updateNode(createSignatureDeclaration(node.kind, typeParameters, parameters, type, name, questionToken), node) + ? updateNode(createMethodSignature(typeParameters, parameters, type, name, questionToken), node) : node; } @@ -502,7 +539,7 @@ namespace ts { : node; } - export function createMethod(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + export function createMethodDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.MethodDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -525,7 +562,7 @@ namespace ts { || node.parameters !== parameters || node.type !== type || node.body !== body - ? updateNode(createMethod(decorators, modifiers, asteriskToken, name, questionToken, typeParameters, parameters, type, body), node) + ? updateNode(createMethodDeclaration(decorators, modifiers, asteriskToken, name, questionToken, typeParameters, parameters, type, body), node) : node; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 57de104b0ca..e6df9c211e7 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -260,21 +260,36 @@ namespace ts { // Signatures and Signature Elements case SyntaxKind.FunctionType: + return updateFunctionTypeNode(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + case SyntaxKind.ConstructorType: + return updateConstructorTypeNode(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + case SyntaxKind.CallSignature: + return updateCallSignatureDeclaration(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + case SyntaxKind.ConstructSignature: - return updateSignatureDeclaration(node, - nodesVisitor((node).typeParameters, visitor, isTypeParameter), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - visitNode((node).type, visitor, isTypeNode)); + return updateConstructSignatureDeclaration(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); case SyntaxKind.MethodSignature: - return updateSignatureDeclaration(node, - nodesVisitor((node).typeParameters, visitor, isTypeParameter), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - visitNode((node).type, visitor, isTypeNode), - visitNode((node).name, visitor, isPropertyName), - visitNode((node).questionToken, tokenVisitor, isToken)); + return updateMethodSignature(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode), + visitNode((node).name, visitor, isPropertyName), + visitNode((node).questionToken, tokenVisitor, isToken)); case SyntaxKind.IndexSignature: return updateIndexSignatureDeclaration(node, diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index da729215087..0000bd97c56 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -100,7 +100,7 @@ namespace ts.codefix { if (declarations.length === 1) { Debug.assert(signatures.length === 1); const signature = signatures[0]; - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration) as MethodDeclaration; + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration); signatureDeclaration.modifiers = modifiers; signatureDeclaration.name = name; signatureDeclaration.questionToken = optional ? createToken(SyntaxKind.QuestionToken) : undefined; @@ -111,7 +111,7 @@ namespace ts.codefix { let signatureDeclarations = []; for (let i = 0; i < signatures.length; i++) { const signature = signatures[i]; - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration) as MethodDeclaration; + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration); signatureDeclaration.modifiers = modifiers; signatureDeclaration.name = name; signatureDeclaration.questionToken = optional ? createToken(SyntaxKind.QuestionToken) : undefined; @@ -120,7 +120,7 @@ namespace ts.codefix { if (declarations.length > signatures.length) { let signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration); - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration) as MethodDeclaration; + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration); signatureDeclaration.modifiers = modifiers; signatureDeclaration.name = name; signatureDeclaration.questionToken = optional ? createToken(SyntaxKind.QuestionToken) : undefined; @@ -197,7 +197,7 @@ namespace ts.codefix { } export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, optional: boolean, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) { - return createMethod( + return createMethodDeclaration( /*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, From ee7e9fc502ec45f93812ab0f4844a9765d7149a7 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 23 Mar 2017 14:00:49 -0700 Subject: [PATCH 4/4] handle todo's --- src/compiler/checker.ts | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f996f738f12..e98ac3be4dc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2257,7 +2257,7 @@ namespace ts { return createKeywordTypeNode(SyntaxKind.BooleanKeyword); } if (type.flags & TypeFlags.Enum) { - const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & (TypeFlags.StringLiteral)) { @@ -2270,7 +2270,7 @@ namespace ts { return (type).intrinsicName === "true" ? createTrue() : createFalse(); } if (type.flags & TypeFlags.EnumLiteral) { - const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & TypeFlags.Void) { @@ -2306,18 +2306,18 @@ namespace ts { } if (objectFlags & ObjectFlags.ClassOrInterface) { Debug.assert(!!(type.flags & TypeFlags.Object)); - const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + 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, /*mustBeIdentifier*/ false, flags); + 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) { - const name = symbolToName(type.aliasSymbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + const name = symbolToName(type.aliasSymbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags); const typeArgumentNodes = mapToTypeNodeArray(type.aliasTypeArguments); return createTypeReferenceNode(name, typeArgumentNodes); } @@ -2381,7 +2381,7 @@ namespace ts { const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags - const entityName = symbolToName(typeAlias, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + const entityName = symbolToName(typeAlias, enclosingDeclaration, /*expectsIdentifier*/ false, flags); return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); } else { @@ -2452,7 +2452,7 @@ namespace ts { function createTypeQueryNodeFromType(type: Type) { const symbol = type.symbol; if (symbol) { - const entityName = symbolToName(symbol, enclosingDeclaration, /*mustBeIdentifier*/ false, flags); + const entityName = symbolToName(symbol, enclosingDeclaration, /*expectsIdentifier*/ false, flags); return createTypeQueryNode(entityName); } } @@ -2482,7 +2482,7 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - const qualifiedNamePart = symbolToName(parent, enclosingDeclaration, /*mustBeIdentifier*/ true, flags); + const qualifiedNamePart = symbolToName(parent, enclosingDeclaration, /*expectsIdentifier*/ true, flags); if (!qualifiedName) { qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); } @@ -2495,7 +2495,7 @@ namespace ts { } } let entityName: EntityName = undefined; - const nameIdentifier = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ true, flags); + const nameIdentifier = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ true, flags); if (qualifiedName) { Debug.assert(!qualifiedName.right); qualifiedName.right = nameIdentifier; @@ -2606,7 +2606,7 @@ namespace ts { const constraintNode = constraint && typeToTypeNodeHelper(constraint, enclosingDeclaration, flags); const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, enclosingDeclaration, flags); - const name = symbolToName(type.symbol, enclosingDeclaration, /*mustBeIdentifier*/ true, flags); + const name = symbolToName(type.symbol, enclosingDeclaration, /*expectsIdentifier*/ true, flags); return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } @@ -2627,19 +2627,17 @@ namespace ts { return parameterNode; } - function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, mustBeIdentifier: true, flags: NodeBuilderFlags): Identifier; - function symbolToName(symbol: Symbol, enclosingDeclaration: Node, mustBeIdentifier: false, flags: NodeBuilderFlags): EntityName; - function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, mustBeIdentifier: boolean, flags: NodeBuilderFlags): EntityName { + function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, expectsIdentifier: true, flags: NodeBuilderFlags): Identifier; + function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, expectsIdentifier: false, flags: NodeBuilderFlags): EntityName; + function symbolToName(symbol: Symbol, enclosingDeclaration: Node | undefined, expectsIdentifier: boolean, flags: NodeBuilderFlags): EntityName { let parentSymbol: Symbol; let meaning: SymbolFlags; - // 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[]; const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; if (!isTypeParameter && enclosingDeclaration) { chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true); - // TODO(aozgaa): check whether type pointed to by symbol requires type arguments to be printed. Debug.assert(chain && chain.length > 0); } else { @@ -2647,10 +2645,8 @@ namespace ts { } parentSymbol = undefined; - if (mustBeIdentifier && chain.length !== 1) { + if (expectsIdentifier && chain.length !== 1) { encounteredError = encounteredError || !(flags & NodeBuilderFlags.allowQualifedNameInPlaceOfIdentifier); - // TODO(aozgaa): failing to get an identifier when we expect one generates an unprintable node. - // Should error handling be more severe? } return createEntityNameFromSymbolChain(chain, chain.length - 1); @@ -2660,7 +2656,6 @@ namespace ts { const symbol = chain[index]; let typeParameterString = ""; if (index > 0) { - // TODO(aozgaa): is the parentSymbol wrong? const parentSymbol = chain[index - 1]; let typeParameters: TypeParameter[]; if (getCheckFlags(symbol) & CheckFlags.Instantiated) {