From bf2acf1d2cdf51fb462561f054d6dd0f01bd8aef Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Sat, 11 Mar 2017 18:58:46 -0800 Subject: [PATCH] basic end-to-end building type nodes --- src/compiler/checker.ts | 23 +++-- src/compiler/factory.ts | 99 +++++++++++++------ src/compiler/types.ts | 24 +++-- src/compiler/visitor.ts | 68 +++++++++++-- src/services/codefixes/fixAddMissingMember.ts | 5 +- src/services/utilities.ts | 2 +- 6 files changed, 159 insertions(+), 62 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ed15f8b035..f29204f7ca1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2190,7 +2190,7 @@ namespace ts { } function createTypeNode(type: Type) { - let encounteredError = false; + // let encounteredError = false; let checkAlias = true; return createTypeNodeWorker(type); @@ -2224,9 +2224,17 @@ namespace ts { if (type.flags & TypeFlags.Number) { return createKeywordTypeNode(SyntaxKind.NumberKeyword); } - if (type.flags & (TypeFlags.Boolean | TypeFlags.StringOrNumberLiteral)) { + if(type.flags & TypeFlags.Boolean) { + // TODO: this is probably x: boolean. How do we deal with x: true ? + return createKeywordTypeNode(SyntaxKind.BooleanKeyword); + } + if (type.flags & (TypeFlags.StringLiteral)) { // TODO: check if this actually works with boolean. - return createLiteralTypeNode((type).text); + return createLiteralTypeNode((createLiteral((type).text))); + } + if (type.flags & (TypeFlags.NumberLiteral)) { + // TODO: check if this actually works with boolean. + return createLiteralTypeNode((createNumericLiteral((type).text))); } if (type.flags & TypeFlags.Void) { return createKeywordTypeNode(SyntaxKind.VoidKeyword); @@ -2279,11 +2287,10 @@ namespace ts { if (objectFlags & ObjectFlags.ClassOrInterface) { Debug.assert(!!(type.flags & TypeFlags.Object)); - // If type is a class or interface type that wasn't hit by the isSymbolAccessible check above, - // type must be an anonymous class or interface. - - encounteredError = true; - return undefined; + const name = getNameOfSymbol(type.symbol); + // TODO: handle type arguments. + // TODO: handle anonymous classes. + return createTypeReferenceNode(name); } if (objectFlags & ObjectFlags.Reference) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 3633cc657ac..19e8ef25da0 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -234,23 +234,75 @@ namespace ts { return createSynthesizedNode(kind); } - - export function createLiteralTypeNode(value: string | number | boolean) { - const literal = createLiteral(value); + export function createLiteralTypeNode(literal: Expression) { const literalTypeNode = createSynthesizedNode(SyntaxKind.LiteralType) as LiteralTypeNode; literalTypeNode.literal = literal; return literalTypeNode; } + export function updateLiteralTypeNode(node: LiteralTypeNode, literal: Expression) { + return node.literal !== literal + ? updateNode(createLiteralTypeNode(literal), node) + : node; + } + // TODO: handle qualified names, ie EntityName's. export function createTypeReferenceNode(typeName: string | Identifier, typeArguments?: NodeArray) { const typeReference = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode; typeReference.typeName = asName(typeName); - typeReference.typeName.parent typeReference.typeArguments = typeArguments; return typeReference; } + export function updateTypeReferenceNode(node: TypeReferenceNode, typeName: Identifier, typeArguments?: NodeArray) { + return node.typeName !== typeName + || node.typeArguments !== typeArguments + ? updateNode(createTypeReferenceNode(typeName, typeArguments), node) + : node; + } + + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType, types: TypeNode[]): UnionTypeNode; + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.IntersectionType, types: TypeNode[]): IntersectionTypeNode; + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]): UnionOrIntersectionTypeNode; + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]): UnionOrIntersectionTypeNode { + const unionTypeNode = createSynthesizedNode(kind) as UnionTypeNode | IntersectionTypeNode; + unionTypeNode.types = asNodeArray(types); + return unionTypeNode; + } + + export function updateUnionOrIntersectionTypeNode(node: UnionOrIntersectionTypeNode, kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: NodeArray) { + return node.types !== types + || node.kind !== kind + ? updateNode(createUnionOrIntersectionTypeNode(kind, types), node) + : node; + } + + export function createTypeLiteralNode(members: TypeElement[]) { + const typeLiteralNode = createSynthesizedNode(SyntaxKind.LiteralType) as TypeLiteralNode; + typeLiteralNode.members = asNodeArray(members); + return typeLiteralNode; + } + + export function updateTypeLiteralNode(node: TypeLiteralNode, members: NodeArray) { + return node.members !== members + ? updateNode(createTypeLiteralNode(members), node) + : node; + } + + export function createTupleTypeNode(elementTypes: TypeNode[]) { + const tupleTypeNode = createSynthesizedNode(SyntaxKind.TupleType) as TupleTypeNode; + tupleTypeNode.elementTypes = asNodeArray(elementTypes); + return tupleTypeNode; + } + + export function updateTypleTypeNode(node: TupleTypeNode, elementTypes: TypeNode[]) { + return node.elementTypes !== elementTypes + ? updateNode(createTupleTypeNode(elementTypes), node) + : node; + } + + // Type Declarations + export function createTypeParameterNode(name: string | Identifier, constraint?: TypeNode, defaultParameter?: TypeNode) { const typeParameter = createSynthesizedNode(SyntaxKind.TypeParameter) as TypeParameterDeclaration; typeParameter.name = asName(name); @@ -260,31 +312,6 @@ namespace ts { return typeParameter; } - export function createUnionTypeNode(types: NodeArray) { - const unionTypeNode = createSynthesizedNode(SyntaxKind.UnionType) as UnionTypeNode; - unionTypeNode.types = asNodeArray(types); - return unionTypeNode; - } - - export function createIntersectionTypeNode(types: NodeArray) { - const intersectionTypeNode = createSynthesizedNode(SyntaxKind.IntersectionType) as IntersectionTypeNode; - intersectionTypeNode.types = asNodeArray(types); - return intersectionTypeNode; - } - - export function createTypeLiteralNode(typeElements: TypeElement[]) { - const typeLiteralNode = createSynthesizedNode(SyntaxKind.LiteralType) as TypeLiteralNode; - typeLiteralNode.members = asNodeArray(typeElements); - return typeLiteralNode; - } - - export function createTupleTypeNode(types: NodeArray) { - const tupleTypeNode = createSynthesizedNode(SyntaxKind.TupleType) as TupleTypeNode; - tupleTypeNode.elementTypes = asNodeArray(types); - return tupleTypeNode; - } - - // Signature elements /** Note, can also be used to construct index signatures. */ @@ -298,10 +325,11 @@ namespace ts { } // TODO: check usage of name... - export function createIndexSignature(parameter: ParameterDeclaration, type: TypeNode, decorators?: Decorator[], modifiers?: Modifier[]): IndexSignatureDeclaration { + // TODO: create entry in visitor.ts + export function createIndexSignatureDeclaration(parameters: ParameterDeclaration[], type: TypeNode, decorators?: Decorator[], modifiers?: Modifier[]): IndexSignatureDeclaration { const indexSignature = createSignature( SyntaxKind.IndexSignature - , asNodeArray([parameter]) + , asNodeArray(parameters) , /*name*/ undefined , /*typeParameters*/undefined , type) as IndexSignatureDeclaration; @@ -310,6 +338,15 @@ namespace ts { return indexSignature; } + export function updateIndexSignatureDeclaration(node: IndexSignatureDeclaration, parameters: ParameterDeclaration[], type: TypeNode, decorators?: Decorator[], modifiers?: Modifier[]) { + return node.parameters !== parameters + || node.type !== type + || node.decorators !== decorators + || node.modifiers !== modifiers + ? updateNode(createIndexSignatureDeclaration(parameters, type, decorators, modifiers), node) + : node; + } + export function createParameter(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression) { const node = createSynthesizedNode(SyntaxKind.Parameter); node.decorators = asNodeArray(decorators); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4fd4dd1780c..463638484c6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -673,7 +673,7 @@ namespace ts { initializer?: Expression; // Optional initializer } - export interface BindingElement extends Declaration { + export interface BindingElement extends Declaration { kind: SyntaxKind.BindingElement; parent?: BindingPattern; propertyName?: PropertyName; // Binding property name (in object binding pattern) @@ -849,19 +849,17 @@ namespace ts { _typeNodeBrand: any; } - export type KeywordKind = SyntaxKind.AnyKeyword - | SyntaxKind.NumberKeyword - | SyntaxKind.ObjectKeyword - | SyntaxKind.BooleanKeyword - | SyntaxKind.StringKeyword - | SyntaxKind.SymbolKeyword - | SyntaxKind.VoidKeyword - | SyntaxKind.UndefinedKeyword - | SyntaxKind.NullKeyword - | SyntaxKind.NeverKeyword; - export interface KeywordTypeNode extends TypeNode { - kind: KeywordKind; + kind: SyntaxKind.AnyKeyword + | SyntaxKind.NumberKeyword + | SyntaxKind.ObjectKeyword + | SyntaxKind.BooleanKeyword + | SyntaxKind.StringKeyword + | SyntaxKind.SymbolKeyword + | SyntaxKind.VoidKeyword + | SyntaxKind.UndefinedKeyword + | SyntaxKind.NullKeyword + | SyntaxKind.NeverKeyword; } export interface ThisTypeNode extends TypeNode { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 133c62e1926..35119dc7fc0 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -212,16 +212,12 @@ namespace ts { } const kind = node.kind; + // No need to visit nodes with no children. if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken)) { return node; } - // We do not yet support types. - if ((kind >= SyntaxKind.TypePredicate && kind <= SyntaxKind.LiteralType)) { - return node; - } - switch (node.kind) { case SyntaxKind.SemicolonClassElement: case SyntaxKind.EmptyStatement: @@ -241,6 +237,13 @@ namespace ts { visitNode((node).expression, visitor, isExpression)); // Signature elements + case SyntaxKind.IndexSignature: + return updateIndexSignatureDeclaration(node + , nodesVisitor((node).parameters, visitor) + , visitNode((node).type, visitor) + , nodesVisitor((node).decorators, visitor, isDecorator) + , nodesVisitor((node).modifiers, visitor, isModifier)); + case SyntaxKind.Parameter: return updateParameter(node, nodesVisitor((node).decorators, visitor, isDecorator), @@ -254,7 +257,60 @@ namespace ts { return updateDecorator(node, visitNode((node).expression, visitor, isExpression)); - // Type member + // Keyword Types + + 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: + return node; + + // Types + + case SyntaxKind.TypePredicate: + throw new Error("reached unsupported type."); + case SyntaxKind.TypeReference: + return updateTypeReferenceNode(node + , visitNode((node).typeName as Identifier, visitor) + , nodesVisitor((node).typeArguments, visitor) + ); + case SyntaxKind.FunctionType: + throw new Error("reached unsupported type."); + case SyntaxKind.ConstructorType: + throw new Error("reached unsupported type."); + case SyntaxKind.TypeQuery: + throw new Error("reached unsupported type."); + case SyntaxKind.TypeLiteral: + throw new Error("reached unsupported type."); + case SyntaxKind.ArrayType: + throw new Error("reached unsupported type."); + case SyntaxKind.TupleType: + throw new Error("reached unsupported type."); + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + throw new Error("reached unsupported type."); + case SyntaxKind.ParenthesizedType: + throw new Error("reached unsupported type."); + case SyntaxKind.ThisType: + throw new Error("reached unsupported type."); + case SyntaxKind.TypeOperator: + throw new Error("reached unsupported type."); + case SyntaxKind.IndexedAccessType: + throw new Error("reached unsupported type."); + case SyntaxKind.MappedType: + throw new Error("reached unsupported type."); + case SyntaxKind.LiteralType: + throw new Error("reached unsupported type."); + + // Type members + case SyntaxKind.PropertyDeclaration: return updateProperty(node, nodesVisitor((node).decorators, visitor, isDecorator), diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 05ff29f0692..79b95e4799d 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -62,13 +62,12 @@ namespace ts.codefix { , "name" , /*questionToken*/ undefined , stringTypeNode); - const indexSignature = createIndexSignature(indexingParameter, typeNode); + const indexSignature = createIndexSignatureDeclaration([indexingParameter], typeNode); - // const startPos = classDeclaration.members.pos; const indexSignatureChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); indexSignatureChangeTracker.insertNodeAfter(sourceFile, openBrace, indexSignature, { insertTrailingNewLine: true }); - return [{ + return [{ description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]), changes: propertyChangeTracker.getChanges() }, diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0fed656af5a..92b63656573 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1386,6 +1386,6 @@ namespace ts { } export function getOpenBraceOfClassLike(declaration: ClassLikeDeclaration, sourceFile: SourceFile) { - return getTokenAtPosition(sourceFile, declaration.members.pos); + return getTokenAtPosition(sourceFile, declaration.members.pos - 1); } }