From 0fa4f112a33a8f7fdc12b61e9e5a8418b9bf95eb Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 16 Mar 2017 16:41:26 -0700 Subject: [PATCH] signatures, parameter, literals, anyonymous types --- src/compiler/checker.ts | 283 +++++++++++------------------- src/compiler/factory.ts | 31 ++-- src/compiler/types.ts | 2 + src/compiler/visitor.ts | 56 +++--- src/services/codefixes/helpers.ts | 66 ++++--- 5 files changed, 181 insertions(+), 257 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d650865ef73..7545430717e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -109,6 +109,7 @@ namespace ts { createTypeNode, createTypeParameterDeclarationFromType, createIndexSignatureFromIndexInfo, + createParameterDeclarationFromSymbol, getSymbolsInScope: (location, meaning) => { location = getParseTreeNode(location); return location ? getSymbolsInScope(location, meaning) : []; @@ -2203,6 +2204,45 @@ namespace ts { return createTypeParameterDeclaration(name, constraint, defaultParameter); } + // TODO: enclosing declaration appears to be unused in getTypeOfSymbolAtLocation + function createParameterDeclarationFromSymbol(parameterSymbol: Symbol): ParameterDeclaration { + const parameterDeclaration = parameterSymbol.declarations[0] as ParameterDeclaration; + const parameterType = getTypeOfSymbol(parameterSymbol); + const parameterTypeNode = checker.createTypeNode(parameterType); + // TODO: clone binding names correctly. + // TODO: copy initialzer in a way that checks whether all symbols used in expression are accessible here, and qualify them appropriately. + const parameterNode = createParameter( + parameterDeclaration.decorators && parameterDeclaration.decorators.map(getSynthesizedDeepClone) + , parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedDeepClone) + , parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken) + , getSynthesizedDeepClone(parameterDeclaration.name) + , parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken) + , parameterTypeNode + , /*initializer*/ undefined); + return parameterNode; + } + + /* @internal */ + type SignatureParts = { + typeParameters: TypeParameterDeclaration[] | undefined; + parameters: ParameterDeclaration[]; + type: TypeNode; + } + + // TODO: expose this, remove copy from helper, possibly don't expose createParameter/TypeParameter? + function createSignatureParts(signature: Signature): SignatureParts { + return { + typeParameters: signature.typeParameters && signature.typeParameters.map(createTypeParameterDeclarationFromType), + parameters: signature.parameters.map(createParameterDeclarationFromSymbol), + type: createTypeNodeExceptAny(getReturnTypeOfSignature(signature)) + } + + function createTypeNodeExceptAny(type: Type): TypeNode | undefined { + const typeNode = createTypeNode(type); + return typeNode && typeNode.kind !== SyntaxKind.AnyKeyword ? typeNode : undefined; + } + } + function createTypeNode(type: Type): TypeNode { let undefinedArgumentIsError = true; let encounteredError = false; @@ -2322,22 +2362,6 @@ namespace ts { return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types)); } - if (objectFlags & ObjectFlags.Mapped) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - - // TODO: does typeParameter have the same constraint or do we need to overwrite it somehow? - const typeParameter = getTypeParameterFromMappedType(type); - // const constraintType = getConstraintTypeFromMappedType(type); - const typeParameterNode = createTypeParameterDeclarationFromType(typeParameter); - - const templateTypeNode = createTypeNode(getTemplateTypeFromMappedType(type)); - const readonlyToken = (type).declaration && (type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; - const questionToken = (type).declaration && (type).declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; - - // TODO: test. - return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); - } - if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { Debug.assert(!!(type.flags & TypeFlags.Object)); // The type is an object literal type. @@ -2371,57 +2395,25 @@ namespace ts { return asNodeArray(types && types.map(createTypeNodeWorker) as TypeNode[]); } - /******** START COPY *********/ - - // function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) { - // const globalFlagsToPass = globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike; - // let inObjectTypeLiteral = false; - // return writeType(type, globalFlags); - - // function writeType(type: Type, flags: TypeFormatFlags) { - // // const nextFlags = flags & ~TypeFormatFlags.InTypeAlias; - // // // Write undefined/null type as any - // // if (type.flags & TypeFlags.Intrinsic) { - // // // Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving - // // writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && isTypeAny(type) - // // ? "any" - // // : (type).intrinsicName); - // // } - // // else if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { - // // if (inObjectTypeLiteral) { - // // writer.reportInaccessibleThisError(); - // // } - // // writer.writeKeyword("this"); - // // } - // else if (getObjectFlags(type) & ObjectFlags.Reference) { - // writeTypeReference(type, nextFlags); - // } - // else if (type.flags & TypeFlags.EnumLiteral) { - // buildSymbolDisplay(getParentOfSymbol(type.symbol), writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); - // writePunctuation(writer, SyntaxKind.DotToken); - // appendSymbolNameOnly(type.symbol, writer); - // } - // else if (getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.Enum | TypeFlags.TypeParameter)) { - // // The specified symbol flags need to be reinterpreted as type flags - // buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); - // } - // else if (!(flags & TypeFormatFlags.InTypeAlias) && type.aliasSymbol && - // isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { - // const typeArguments = type.aliasTypeArguments; - // writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, length(typeArguments), nextFlags); - // } - // else if (type.flags & TypeFlags.UnionOrIntersection) { - // writeUnionOrIntersectionType(type, nextFlags); - // } - // else if (getObjectFlags(type) & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { - // writeAnonymousType(type, nextFlags); - // } + // TODO: implement when this is testable. // else if (type.flags & TypeFlags.StringOrNumberLiteral) { // writer.writeStringLiteral(literalTypeToString(type)); - // } - // } - /******** END COPY *********/ + function createMappedTypeNodeFromType(type: MappedType) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + + // TODO: does typeParameter have the same constraint or do we need to overwrite it somehow? + const typeParameter = getTypeParameterFromMappedType(type); + // const constraintType = getConstraintTypeFromMappedType(type); + const typeParameterNode = createTypeParameterDeclarationFromType(typeParameter); + + const templateTypeNode = createTypeNode(getTemplateTypeFromMappedType(type)); + const readonlyToken = (type).declaration && (type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; + const questionToken = (type).declaration && (type).declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; + + // TODO: test. + return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); + } function createAnonymousTypeNode(type: ObjectType): TypeNode { const symbol = type.symbol; @@ -2439,7 +2431,7 @@ namespace ts { const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags - const entityName = getEntityNameFromSymbol(typeAlias, enclosingDeclaration); + const entityName = createNameFromSymbol(typeAlias, enclosingDeclaration); return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); } else { @@ -2453,14 +2445,14 @@ namespace ts { symbolStack = []; } symbolStack.push(symbol); - let result = createTypeLiteralNodeFromType(type); + let result = createTypeNodeFromObjectType(type); symbolStack.pop(); return result; } } else { // Anonymous types with no symbol are never circular - return createTypeLiteralNodeFromType(type); + return createTypeNodeFromObjectType(type); } function shouldWriteTypeOfFunctionSymbol() { @@ -2477,103 +2469,48 @@ namespace ts { } } - function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) { + function createTypeNodeFromObjectType(type: ObjectType): TypeNode { if (type.objectFlags & ObjectFlags.Mapped) { if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { - writeMappedType(type); - return; + return createMappedTypeNodeFromType(type); } } const resolved = resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { - writePunctuation(writer, SyntaxKind.OpenBraceToken); - writePunctuation(writer, SyntaxKind.CloseBraceToken); - return; + return createTypeLiteralNode(/*members*/ undefined); } if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { - const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags); - if (parenthesizeSignature) { - writePunctuation(writer, SyntaxKind.OpenParenToken); - } - buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack); - if (parenthesizeSignature) { - writePunctuation(writer, SyntaxKind.CloseParenToken); - } - return; + const signature = resolved.callSignatures[0]; + const signatureParts = createSignatureParts(signature); + return createSignatureDeclaration(SyntaxKind.FunctionType, signatureParts.typeParameters, signatureParts.parameters, signatureParts.type); } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { - if (flags & TypeFormatFlags.InElementType) { - writePunctuation(writer, SyntaxKind.OpenParenToken); - } - writeKeyword(writer, SyntaxKind.NewKeyword); - writeSpace(writer); - buildSignatureDisplay(resolved.constructSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack); - if (flags & TypeFormatFlags.InElementType) { - writePunctuation(writer, SyntaxKind.CloseParenToken); - } - return; + const signature = resolved.constructSignatures[0]; + const signatureParts = createSignatureParts(signature); + return createSignatureDeclaration(SyntaxKind.ConstructorType, signatureParts.typeParameters, signatureParts.parameters, signatureParts.type); } } const saveInObjectTypeLiteral = inObjectTypeLiteral; inObjectTypeLiteral = true; - writePunctuation(writer, SyntaxKind.OpenBraceToken); - writer.writeLine(); - writer.increaseIndent(); - writeObjectLiteralType(resolved); - writer.decreaseIndent(); - writePunctuation(writer, SyntaxKind.CloseBraceToken); + const members = createTypeNodesFromResolvedType(resolved); inObjectTypeLiteral = saveInObjectTypeLiteral; - } - - function writeObjectLiteralType(resolved: ResolvedType) { - for (const signature of resolved.callSignatures) { - buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack); - writePunctuation(writer, SyntaxKind.SemicolonToken); - writer.writeLine(); - } - for (const signature of resolved.constructSignatures) { - buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, SignatureKind.Construct, symbolStack); - writePunctuation(writer, SyntaxKind.SemicolonToken); - writer.writeLine(); - } - buildIndexSignatureDisplay(resolved.stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack); - buildIndexSignatureDisplay(resolved.numberIndexInfo, writer, IndexKind.Number, enclosingDeclaration, globalFlags, symbolStack); - for (const p of resolved.properties) { - const t = getTypeOfSymbol(p); - if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) { - const signatures = getSignaturesOfType(t, SignatureKind.Call); - for (const signature of signatures) { - writePropertyWithModifiers(p); - buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack); - writePunctuation(writer, SyntaxKind.SemicolonToken); - writer.writeLine(); - } - } - else { - writePropertyWithModifiers(p); - writePunctuation(writer, SyntaxKind.ColonToken); - writeSpace(writer); - writeType(t, TypeFormatFlags.None); - writePunctuation(writer, SyntaxKind.SemicolonToken); - writer.writeLine(); - } - } + return createTypeLiteralNode(members); } function createTypeQueryNodeFromType(type: Type) { const symbol = type.symbol; if (symbol) { // TODO: get entity name instead. - const entityName = createIdentifier(symbolToString(symbol)); + const entityName = createNameFromSymbol(symbol); return createTypeQueryNode(entityName); } } - function getEntityNameFromSymbol(symbol: Symbol, enclosingDeclaration: Node): EntityName { + function createNameFromSymbol(symbol: Symbol): EntityName { symbol; enclosingDeclaration; // TODO: actually implement this return createIdentifier(symbolToString(symbol, enclosingDeclaration)); @@ -2636,67 +2573,53 @@ namespace ts { } } - function createTypeLiteralNodeFromType(type: ObjectType) { - const resolvedType = resolveStructuredTypeMembers(type); - const newMembers = createTypeNodesFromResolvedType(resolvedType); - return createTypeLiteralNode(newMembers); - } - function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { const typeElements: TypeElement[] = []; - for(const signature of resolvedType.callSignatures) { - signature; - throw new Error("call signatures not implemented"); + for (const signature of resolvedType.callSignatures) { + const signatureParts = createSignatureParts(signature); + typeElements.push(createSignatureDeclaration(SyntaxKind.CallSignature, signatureParts.typeParameters, signatureParts.parameters, signatureParts.type)); } for (const signature of resolvedType.constructSignatures) { - signature; - throw new Error("Construct signatures not implemented"); + const signatureParts = createSignatureParts(signature); + typeElements.push(createSignatureDeclaration(SyntaxKind.ConstructSignature, signatureParts.typeParameters, signatureParts.parameters, signatureParts.type)); } if (resolvedType.stringIndexInfo) { typeElements.push(createIndexSignatureFromIndexInfo(resolvedType.stringIndexInfo, IndexKind.String)); } if (resolvedType.numberIndexInfo) { - typeElements.push(createIndexSignatureFromIndexInfo(resolvedType.stringIndexInfo, IndexKind.Number)); + typeElements.push(createIndexSignatureFromIndexInfo(resolvedType.numberIndexInfo, IndexKind.Number)); } - const members = resolvedType.members; + const properties = resolvedType.properties; + if (!properties) { + return typeElements; + } - members.forEach(memberSymbol => { - const oldDeclaration = memberSymbol.declarations && memberSymbol.declarations[0] as TypeElement; + for (const propertySymbol of properties) { + const propertyType = getTypeOfSymbol(propertySymbol); + const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement; if (!oldDeclaration) { return; } - - const kind = oldDeclaration.kind; - const memberName = getSynthesizedDeepClone(oldDeclaration.name); - const memberType = getTypeOfSymbol(memberSymbol); - - switch (kind) { - case SyntaxKind.PropertySignature: - const optional = !!oldDeclaration.questionToken; - typeElements.push(createPropertySignature( - memberName - , optional ? createToken(SyntaxKind.QuestionToken) : undefined - , createTypeNode(memberType) - , /*initializer*/undefined)); - break; - case SyntaxKind.MethodSignature: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - const signatureType = getSignaturesOfSymbol(memberSymbol); - signatureType - createSignatureDeclaration - throw new Error("signature problems."); - // name ?: PropertyName; - // typeParameters ?: NodeArray; - // parameters: NodeArray; - // type ?: TypeNode; - case SyntaxKind.IndexSignature: - throw new Error("type literal constituent not implemented."); - default: - throw new Error("Unknown resolved member kind."); + const propertyName = getSynthesizedDeepClone(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 signatureParts = createSignatureParts(signature); + const methodDeclaration = createSignatureDeclaration(SyntaxKind.MethodSignature, signatureParts.typeParameters, signatureParts.parameters, signatureParts.type, propertyName, optionalToken); + methodDeclaration.questionToken = optionalToken; + typeElements.push(methodDeclaration); + } } - }); + else { + typeElements.push(createPropertySignature( + propertyName + , optionalToken + , createTypeNode(propertyType) + , /*initializer*/undefined)); + } + } return typeElements.length ? typeElements : undefined; } } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ebb4bbe5a9a..d91076dcde2 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -394,29 +394,37 @@ namespace ts { : node; } - export function createSignatureDeclaration(kind: SyntaxKind, name: string | PropertyName | undefined, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): T { + // TODO: ask if we should have multiple implementations. Some T's can't have question token. + 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; - signatureDeclaration.name = asName(name); signatureDeclaration.typeParameters = asNodeArray(typeParameters); signatureDeclaration.parameters = asNodeArray(parameters); signatureDeclaration.type = type; + signatureDeclaration.name = asName(name); + signatureDeclaration.questionToken = questionToken; return signatureDeclaration; } - export function updateSignatureDeclaration(node: T, name: string | PropertyName | undefined, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): T { - return node.name !== name - || node.typeParameters !== typeParameters + // 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 { + return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type - ? updateNode(createSignatureDeclaration(node.kind, name, typeParameters, parameters, type), node) + || node.name !== name + || node.questionToken !== questionToken + ? updateNode(createSignatureDeclaration(node.kind, typeParameters, parameters, type, name, questionToken), node) : node; } // Signature elements - export function createPropertySignature(name: PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): PropertySignature { + export function createPropertySignature(name: PropertyName | string, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): PropertySignature { const propertySignature = createSynthesizedNode(SyntaxKind.PropertySignature) as PropertySignature; - propertySignature.name = name; + propertySignature.name = asName(name); propertySignature.questionToken = questionToken; propertySignature.type = type; propertySignature.initializer = initializer; @@ -511,12 +519,13 @@ namespace ts { : node; } - export function createMethod(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | PropertyName, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + 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) { const node = createSynthesizedNode(SyntaxKind.MethodDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); node.asteriskToken = asteriskToken; node.name = asName(name); + node.questionToken = questionToken; node.typeParameters = asNodeArray(typeParameters); node.parameters = asNodeArray(parameters); node.type = type; @@ -524,7 +533,7 @@ namespace ts { return node; } - export function updateMethod(node: MethodDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + export function updateMethod(node: MethodDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, questionToken: QuestionToken | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken @@ -533,7 +542,7 @@ namespace ts { || node.parameters !== parameters || node.type !== type || node.body !== body - ? updateNode(createMethod(decorators, modifiers, asteriskToken, name, typeParameters, parameters, type, body), node) + ? updateNode(createMethod(decorators, modifiers, asteriskToken, name, questionToken, typeParameters, parameters, type, body), node) : node; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7f6c56aa1ff..acca1b7398c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2474,6 +2474,8 @@ namespace ts { createTypeParameterDeclarationFromType(type: Type): TypeParameterDeclaration; /** Note that the resulting type node cannot be checked. */ createIndexSignatureFromIndexInfo(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration + /** Note that the resulting type node cannot be checked. */ + createParameterDeclarationFromSymbol(parameterSymbol: Symbol): ParameterDeclaration; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolAtLocation(node: Node): Symbol; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index afe236859a4..5d63126ad0a 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -245,6 +245,8 @@ namespace ts { case SyntaxKind.EmptyStatement: case SyntaxKind.OmittedExpression: case SyntaxKind.DebuggerStatement: + case SyntaxKind.EndOfDeclarationMarker: + case SyntaxKind.MissingDeclaration: // No need to visit nodes with no children. return node; @@ -260,18 +262,25 @@ namespace ts { // Signatures and Signature Elements case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: - case SyntaxKind.MethodSignature: return updateSignatureDeclaration(node - , visitNode((node).name, visitor, isPropertyName) , nodesVisitor((node).typeParameters, visitor, isTypeParameter) - , nodesVisitor((node).parameters, visitor, isParameter) + , 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, visitor, isToken)); + case SyntaxKind.IndexSignature: return updateIndexSignatureDeclaration(node - , nodesVisitor((node).parameters, visitor) + , visitParameterList((node).parameters, visitor, context, nodesVisitor) , visitNode((node).type, visitor, isTypeNode) , nodesVisitor((node).decorators, visitor, isDecorator) , nodesVisitor((node).modifiers, visitor, isModifier)); @@ -290,47 +299,32 @@ namespace ts { return updateDecorator(node, visitNode((node).expression, visitor, isExpression)); - // Keyword Types and This - - case SyntaxKind.AnyKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.ObjectKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.ThisKeyword: - throw new Error("should be caught above"); - // Types case SyntaxKind.TypePredicate: throw new Error("reached unsupported type in visitor."); case SyntaxKind.TypeReference: return updateTypeReferenceNode(node - , visitNode((node).typeName, visitor, isEntityName) - , nodesVisitor((node).typeArguments, visitor, isTypeNode)); + , visitNode((node).typeName, visitor, isEntityName) + , nodesVisitor((node).typeArguments, visitor, isTypeNode)); - case SyntaxKind.FunctionType: - throw new Error("reached unsupported type in visitor."); - case SyntaxKind.ConstructorType: - throw new Error("reached unsupported type in visitor."); case SyntaxKind.TypeQuery: return updateTypeQueryNode((node), visitNode((node).exprName, visitor, isEntityName)); + case SyntaxKind.TypeLiteral: return updateTypeLiteralNode((node), nodesVisitor((node).members, visitor)); + case SyntaxKind.ArrayType: return updateArrayTypeNode(node, visitNode((node).elementType, visitor, isTypeNode)); + case SyntaxKind.TupleType: return updateTypleTypeNode((node), nodesVisitor((node).elementTypes, visitor, isTypeNode)); + case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: return updateUnionOrIntersectionTypeNode(node - , nodesVisitor((node).types, visitor, isTypeNode)); + , nodesVisitor((node).types, visitor, isTypeNode)); + case SyntaxKind.ParenthesizedType: throw new Error("reached unsupported type in visitor."); case SyntaxKind.ThisType: @@ -366,7 +360,7 @@ namespace ts { case SyntaxKind.IndexSignature: return updateIndexSignatureDeclaration(node - , nodesVisitor((node).parameters, visitor, isParameter) + , visitParameterList((node).parameters, visitor, context, nodesVisitor) , visitNode((node).type, visitor, isTypeNode) , nodesVisitor((node).decorators, visitor, isDecorator) , nodesVisitor((node).modifiers, visitor, isModifier)); @@ -383,8 +377,9 @@ namespace ts { return updateMethod(node, nodesVisitor((node).decorators, visitor, isDecorator), nodesVisitor((node).modifiers, visitor, isModifier), - (node).asteriskToken, + visitNode((node).asteriskToken, visitor, isToken), visitNode((node).name, visitor, isPropertyName), + visitNode((node).questionToken, visitor, isToken), nodesVisitor((node).typeParameters, visitor, isTypeParameter), visitParameterList((node).parameters, visitor, context, nodesVisitor), visitNode((node).type, visitor, isTypeNode), @@ -857,8 +852,7 @@ namespace ts { visitNode((node).expression, visitor, isExpression)); default: - throw new Error("not handled"); - // return node; + return node; } } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 37ea8bab1a9..e98235c3c8b 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -85,56 +85,66 @@ namespace ts.codefix { if (!(signatures && signatures.length > 0)) { return undefined; } + + const optional = !!(symbol.flags & SymbolFlags.Optional); if (declarations.length === 1) { Debug.assert(signatures.length === 1); // TODO: suppress any return type // TODO: get parameters working. // TODO: add support for type parameters. const signature = signatures[0]; - const newTypeParameters = signature.typeParameters && signature.typeParameters.map(checker.createTypeParameterDeclarationFromType); - const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker)); - - const returnType = createTypeNodeExceptAny(checker.getReturnTypeOfSignature(signature), checker); - return createStubbedMethod(modifiers, name, newTypeParameters, newParameterNodes, returnType); + const signatureParts = getSignatureParts(signature); + return createStubbedMethod(modifiers, name, optional, signatureParts.typeParameters, signatureParts.parameters, signatureParts.type); } let signatureDeclarations = []; for (let i = 0; i < signatures.length; i++) { // TODO: make signatures instead of methods const signature = signatures[i]; - const newTypeParameters = signature.typeParameters && signature.typeParameters.map(checker.createTypeParameterDeclarationFromType); - const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker)); - const returnType = createTypeNodeExceptAny(checker.getReturnTypeOfSignature(signature), checker); + const signatureParts = getSignatureParts(signature); signatureDeclarations.push(createMethod( /*decorators*/ undefined , modifiers , /*asteriskToken*/ undefined , name - , newTypeParameters - , newParameterNodes - , returnType + , optional ? createToken(SyntaxKind.QuestionToken) : undefined + , signatureParts.typeParameters + , signatureParts.parameters + , signatureParts.type , /*body*/undefined)); } if (declarations.length > signatures.length) { let signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration); - const newTypeParameters = signature.typeParameters && signature.typeParameters.map(checker.createTypeParameterDeclarationFromType); - const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker)); - const returnType = createTypeNodeExceptAny(checker.getReturnTypeOfSignature(signature), checker); - signatureDeclarations.push(createStubbedMethod(modifiers, name, newTypeParameters, newParameterNodes, returnType)); + const signatureParts = getSignatureParts(signature); + signatureDeclarations.push(createStubbedMethod(modifiers, name, optional, signatureParts.typeParameters, signatureParts.parameters, signatureParts.type)); } else { Debug.assert(declarations.length === signatures.length); - const methodImplementingSignatures = createMethodImplementingSignatures(signatures, name, modifiers); + const methodImplementingSignatures = createMethodImplementingSignatures(signatures, name, optional, modifiers); signatureDeclarations.push(methodImplementingSignatures); } return signatureDeclarations; default: return undefined; } + + type SignatureParts = { + typeParameters: TypeParameterDeclaration[]; + parameters: ParameterDeclaration[]; + type: TypeNode; + } + + function getSignatureParts(signature: Signature): SignatureParts { + return { + typeParameters: signature.typeParameters && signature.typeParameters.map(checker.createTypeParameterDeclarationFromType), + parameters: signature.getParameters().map(symbol => checker.createParameterDeclarationFromSymbol(symbol)), + type: createTypeNodeExceptAny(checker.getReturnTypeOfSignature(signature), checker) + } + } } - function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, modifiers: Modifier[] | undefined): MethodDeclaration { + function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, optional: boolean, modifiers: Modifier[] | undefined): MethodDeclaration { Debug.assert(signatures && signatures.length > 0); let maxArgsIndex = 0; @@ -153,7 +163,7 @@ namespace ts.codefix { maxArgsSignature = sig; } } - const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0); + const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0); const maxArgsParameterSymbolNames = signatures[maxArgsIndex].getParameters().map(symbol => symbol.getName()); const parameters: ParameterDeclaration[] = []; @@ -186,17 +196,19 @@ namespace ts.codefix { return createStubbedMethod( modifiers , name + , optional , /*typeParameters*/undefined , parameters , /*returnType*/ undefined); } - export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) { + export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, optional: boolean, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) { return createMethod( /*decorators*/undefined , modifiers , /*asteriskToken*/undefined , name + , optional ? createToken(SyntaxKind.QuestionToken) : undefined , typeParameters , parameters , returnType @@ -223,22 +235,6 @@ namespace ts.codefix { return undefined; } - function createParameterDeclarationFromSymbol(parameterSymbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker) { - const parameterDeclaration = parameterSymbol.getDeclarations()[0] as ParameterDeclaration; - const parameterType = checker.getTypeOfSymbolAtLocation(parameterSymbol, enclosingDeclaration); - const parameterTypeNode = checker.createTypeNode(parameterType); - // TODO: deep cloning of decorators/any node. - const parameterNode = createParameter( - parameterDeclaration.decorators && parameterDeclaration.decorators.map(getSynthesizedDeepClone) - , parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedDeepClone) - , parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken) - , getSynthesizedDeepClone(parameterDeclaration.name) - , parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken) - , parameterTypeNode - , /*initializer*/ undefined); - return parameterNode; - } - function createTypeNodeExceptAny(type: Type, checker: TypeChecker) { const typeNode = checker.createTypeNode(type); return typeNode && typeNode.kind !== SyntaxKind.AnyKeyword ? typeNode : undefined;