From 89bfbd0cf3f9bce9bdc602cfb3664817bbd21b44 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 Apr 2015 22:48:18 -0700 Subject: [PATCH 1/3] Make StringLiterals and HeritageClauses into TypeNodes. It makes the checker much simpler and nicer. --- src/compiler/checker.ts | 76 ++++++++++++++++++++------------------- src/compiler/types.ts | 4 +-- src/compiler/utilities.ts | 2 +- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d0c51eb05c9..d156c128b08 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -597,7 +597,7 @@ module ts { if (moduleSymbol.flags & SymbolFlags.Variable) { let typeAnnotation = (moduleSymbol.valueDeclaration).type; if (typeAnnotation) { - return getPropertyOfType(getTypeFromTypeNodeOrHeritageClauseElement(typeAnnotation), name); + return getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name); } } } @@ -646,7 +646,7 @@ module ts { if (symbol.flags & SymbolFlags.Variable) { var typeAnnotation = (symbol.valueDeclaration).type; if (typeAnnotation) { - return resolveSymbol(getPropertyOfType(getTypeFromTypeNodeOrHeritageClauseElement(typeAnnotation), name)); + return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name)); } } } @@ -2127,7 +2127,7 @@ module ts { } // Use type from type annotation if one is present if (declaration.type) { - return getTypeFromTypeNodeOrHeritageClauseElement(declaration.type); + return getTypeFromTypeNode(declaration.type); } if (declaration.kind === SyntaxKind.Parameter) { let func = declaration.parent; @@ -2289,18 +2289,18 @@ module ts { return links.type; } - function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode | LiteralExpression { + function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode { return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type; } function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type { if (accessor) { if (accessor.kind === SyntaxKind.GetAccessor) { - return accessor.type && getTypeFromTypeNodeOrHeritageClauseElement(accessor.type); + return accessor.type && getTypeFromTypeNode(accessor.type); } else { let setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor); - return setterTypeAnnotation && getTypeFromTypeNodeOrHeritageClauseElement(setterTypeAnnotation); + return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation); } } return undefined; @@ -2541,7 +2541,7 @@ module ts { if (!links.declaredType) { links.declaredType = resolvingType; let declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); - let type = getTypeFromTypeNodeOrHeritageClauseElement(declaration.type); + let type = getTypeFromTypeNode(declaration.type); if (links.declaredType === resolvingType) { links.declaredType = type; } @@ -3083,7 +3083,7 @@ module ts { returnType = classType; } else if (declaration.type) { - returnType = getTypeFromTypeNodeOrHeritageClauseElement(declaration.type); + returnType = getTypeFromTypeNode(declaration.type); } else { // TypeScript 1.0 spec (April 2014): @@ -3241,7 +3241,7 @@ module ts { function getIndexTypeOfSymbol(symbol: Symbol, kind: IndexKind): Type { let declaration = getIndexDeclarationOfSymbol(symbol, kind); return declaration - ? declaration.type ? getTypeFromTypeNodeOrHeritageClauseElement(declaration.type) : anyType + ? declaration.type ? getTypeFromTypeNode(declaration.type) : anyType : undefined; } @@ -3252,7 +3252,7 @@ module ts { type.constraint = targetConstraint ? instantiateType(targetConstraint, type.mapper) : noConstraintType; } else { - type.constraint = getTypeFromTypeNodeOrHeritageClauseElement((getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint); + type.constraint = getTypeFromTypeNode((getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint); } } return type.constraint === noConstraintType ? undefined : type.constraint; @@ -3383,7 +3383,7 @@ module ts { if (type.flags & (TypeFlags.Class | TypeFlags.Interface) && type.flags & TypeFlags.Reference) { let typeParameters = (type).typeParameters; if (node.typeArguments && node.typeArguments.length === typeParameters.length) { - type = createTypeReference(type, map(node.typeArguments, getTypeFromTypeNodeOrHeritageClauseElement)); + type = createTypeReference(type, map(node.typeArguments, getTypeFromTypeNode)); } else { error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length); @@ -3481,7 +3481,7 @@ module ts { function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type { let links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = createArrayType(getTypeFromTypeNodeOrHeritageClauseElement(node.elementType)); + links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType)); } return links.resolvedType; } @@ -3499,7 +3499,7 @@ module ts { function getTypeFromTupleTypeNode(node: TupleTypeNode): Type { let links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNodeOrHeritageClauseElement)); + links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode)); } return links.resolvedType; } @@ -3608,7 +3608,7 @@ module ts { function getTypeFromUnionTypeNode(node: UnionTypeNode): Type { let links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNodeOrHeritageClauseElement), /*noSubtypeReduction*/ true); + links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction*/ true); } return links.resolvedType; } @@ -3622,7 +3622,7 @@ module ts { return links.resolvedType; } - function getStringLiteralType(node: LiteralExpression): StringLiteralType { + function getStringLiteralType(node: StringLiteralExpression): StringLiteralType { if (hasProperty(stringLiteralTypes, node.text)) { return stringLiteralTypes[node.text]; } @@ -3632,7 +3632,7 @@ module ts { return type; } - function getTypeFromStringLiteral(node: LiteralExpression): Type { + function getTypeFromStringLiteral(node: StringLiteralExpression): Type { let links = getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = getStringLiteralType(node); @@ -3640,7 +3640,7 @@ module ts { return links.resolvedType; } - function getTypeFromTypeNodeOrHeritageClauseElement(node: TypeNode | LiteralExpression | HeritageClauseElement): Type { + function getTypeFromTypeNode(node: TypeNode): Type { switch (node.kind) { case SyntaxKind.AnyKeyword: return anyType; @@ -3655,7 +3655,7 @@ module ts { case SyntaxKind.VoidKeyword: return voidType; case SyntaxKind.StringLiteral: - return getTypeFromStringLiteral(node); + return getTypeFromStringLiteral(node); case SyntaxKind.TypeReference: return getTypeFromTypeReference(node); case SyntaxKind.HeritageClauseElement: @@ -3669,7 +3669,7 @@ module ts { case SyntaxKind.UnionType: return getTypeFromUnionTypeNode(node); case SyntaxKind.ParenthesizedType: - return getTypeFromTypeNodeOrHeritageClauseElement((node).type); + return getTypeFromTypeNode((node).type); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.TypeLiteral: @@ -5680,7 +5680,7 @@ module ts { let declaration = node.parent; if (node === declaration.initializer) { if (declaration.type) { - return getTypeFromTypeNodeOrHeritageClauseElement(declaration.type); + return getTypeFromTypeNode(declaration.type); } if (declaration.kind === SyntaxKind.Parameter) { let type = getContextuallyTypedParameterType(declaration); @@ -5883,7 +5883,7 @@ module ts { case SyntaxKind.NewExpression: return getContextualTypeForArgument(parent, node); case SyntaxKind.TypeAssertionExpression: - return getTypeFromTypeNodeOrHeritageClauseElement((parent).type); + return getTypeFromTypeNode((parent).type); case SyntaxKind.BinaryExpression: return getContextualTypeForBinaryOperand(node); case SyntaxKind.PropertyAssignment: @@ -6705,7 +6705,7 @@ module ts { let typeArgumentsAreAssignable = true; for (let i = 0; i < typeParameters.length; i++) { let typeArgNode = typeArguments[i]; - let typeArgument = getTypeFromTypeNodeOrHeritageClauseElement(typeArgNode); + let typeArgument = getTypeFromTypeNode(typeArgNode); // Do not push on this array! It has a preallocated length typeArgumentResultTypes[i] = typeArgument; if (typeArgumentsAreAssignable /* so far */) { @@ -6727,9 +6727,12 @@ module ts { let paramType = getTypeAtPosition(signature, i); // A tagged template expression provides a special first argument, and string literals get string literal types // unless we're reporting errors - let argType = i === 0 && node.kind === SyntaxKind.TaggedTemplateExpression ? globalTemplateStringsArrayType : - arg.kind === SyntaxKind.StringLiteral && !reportErrors ? getStringLiteralType(arg) : - checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + let argType = i === 0 && node.kind === SyntaxKind.TaggedTemplateExpression + ? globalTemplateStringsArrayType + : arg.kind === SyntaxKind.StringLiteral && !reportErrors + ? getStringLiteralType(arg) + : checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + // Use argument expression as error location when reporting errors if (!checkTypeRelatedTo(argType, paramType, relation, reportErrors ? arg : undefined, Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1)) { @@ -7187,7 +7190,7 @@ module ts { function checkTypeAssertion(node: TypeAssertion): Type { let exprType = checkExpression(node.expression); - let targetType = getTypeFromTypeNodeOrHeritageClauseElement(node.type); + let targetType = getTypeFromTypeNode(node.type); if (produceDiagnostics && targetType !== unknownType) { let widenedType = getWidenedType(exprType); if (!(isTypeAssignableTo(targetType, widenedType))) { @@ -7361,7 +7364,7 @@ module ts { function checkFunctionExpressionOrObjectLiteralMethodBody(node: FunctionExpression | MethodDeclaration) { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); if (node.type && !node.asteriskToken) { - checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNodeOrHeritageClauseElement(node.type)); + checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); } if (node.body) { @@ -7371,7 +7374,7 @@ module ts { else { let exprType = checkExpression(node.body); if (node.type) { - checkTypeAssignableTo(exprType, getTypeFromTypeNodeOrHeritageClauseElement(node.type), node.body, /*headMessage*/ undefined); + checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, /*headMessage*/ undefined); } checkFunctionExpressionBodies(node.body); } @@ -8800,12 +8803,12 @@ module ts { } /** Checks a type reference node as an expression. */ - function checkTypeNodeAsExpression(node: TypeNode | LiteralExpression) { + function checkTypeNodeAsExpression(node: TypeNode) { // When we are emitting type metadata for decorators, we need to try to check the type // as if it were an expression so that we can emit the type in a value position when we // serialize the type metadata. if (node && node.kind === SyntaxKind.TypeReference) { - let type = getTypeFromTypeNodeOrHeritageClauseElement(node); + let type = getTypeFromTypeNode(node); let shouldCheckIfUnknownType = type === unknownType && compilerOptions.separateCompilation; if (!type || (!shouldCheckIfUnknownType && type.flags & (TypeFlags.Intrinsic | TypeFlags.NumberLike | TypeFlags.StringLike))) { return; @@ -8825,7 +8828,8 @@ module ts { case SyntaxKind.PropertyDeclaration: checkTypeNodeAsExpression((node).type); break; - case SyntaxKind.Parameter: checkTypeNodeAsExpression((node).type); + case SyntaxKind.Parameter: + checkTypeNodeAsExpression((node).type); break; case SyntaxKind.MethodDeclaration: checkTypeNodeAsExpression((node).type); @@ -8941,7 +8945,7 @@ module ts { checkSourceElement(node.body); if (node.type && !isAccessor(node.kind) && !node.asteriskToken) { - checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNodeOrHeritageClauseElement(node.type)); + checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); } // Report an implicit any error if there is no body, no explicit return type, and node is not a private method @@ -10105,7 +10109,7 @@ module ts { if (!tp1.constraint || !tp2.constraint) { return false; } - if (!isTypeIdenticalTo(getTypeFromTypeNodeOrHeritageClauseElement(tp1.constraint), getTypeFromTypeNodeOrHeritageClauseElement(tp2.constraint))) { + if (!isTypeIdenticalTo(getTypeFromTypeNode(tp1.constraint), getTypeFromTypeNode(tp2.constraint))) { return false; } } @@ -11136,7 +11140,7 @@ module ts { return node.parent && node.parent.kind === SyntaxKind.HeritageClauseElement; } - function isTypeNodeOrHeritageClauseElement(node: Node): boolean { + function isTypeNode(node: Node): boolean { if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) { return true; } @@ -11385,8 +11389,8 @@ module ts { return unknownType; } - if (isTypeNodeOrHeritageClauseElement(node)) { - return getTypeFromTypeNodeOrHeritageClauseElement(node); + if (isTypeNode(node)) { + return getTypeFromTypeNode(node); } if (isExpression(node)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b3d2f7858ea..39904107e97 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -689,7 +689,7 @@ module ts { hasExtendedUnicodeEscape?: boolean; } - export interface StringLiteralExpression extends LiteralExpression { + export interface StringLiteralExpression extends LiteralExpression, TypeNode { _stringLiteralExpressionBrand: any; } @@ -739,7 +739,7 @@ module ts { arguments: NodeArray; } - export interface HeritageClauseElement extends Node { + export interface HeritageClauseElement extends TypeNode { expression: LeftHandSideExpression; typeArguments?: NodeArray; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 02fae4be6de..0f2bd5daba5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1913,4 +1913,4 @@ module ts { return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength:*/ newEndN - oldStartN); } -} +} From d1c0f8780d6934ef1d9a141303799f240080020d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 Apr 2015 22:54:46 -0700 Subject: [PATCH 2/3] Additional API cleanup. --- src/compiler/declarationEmitter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 30f50faf562..712d2f6b497 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -314,12 +314,12 @@ module ts { } } - function emitTypeWithNewGetSymbolAccessibilityDiagnostic(type: TypeNode | EntityName | HeritageClauseElement, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { + function emitTypeWithNewGetSymbolAccessibilityDiagnostic(type: TypeNode | EntityName, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; emitType(type); } - function emitType(type: TypeNode | StringLiteralExpression | Identifier | QualifiedName | HeritageClauseElement) { + function emitType(type: TypeNode | Identifier | QualifiedName) { switch (type.kind) { case SyntaxKind.AnyKeyword: case SyntaxKind.StringKeyword: From c2e178ee517b75ca3dee308a287100d44b4807b2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 17 Apr 2015 12:06:21 -0700 Subject: [PATCH 3/3] CR feedback. --- src/compiler/checker.ts | 8 ++++---- src/compiler/declarationEmitter.ts | 4 ++-- src/compiler/parser.ts | 2 +- src/compiler/types.ts | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4853e9e44af..f946c05ee70 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3623,7 +3623,7 @@ module ts { return links.resolvedType; } - function getStringLiteralType(node: StringLiteralExpression): StringLiteralType { + function getStringLiteralType(node: StringLiteral): StringLiteralType { if (hasProperty(stringLiteralTypes, node.text)) { return stringLiteralTypes[node.text]; } @@ -3633,7 +3633,7 @@ module ts { return type; } - function getTypeFromStringLiteral(node: StringLiteralExpression): Type { + function getTypeFromStringLiteral(node: StringLiteral): Type { let links = getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = getStringLiteralType(node); @@ -3656,7 +3656,7 @@ module ts { case SyntaxKind.VoidKeyword: return voidType; case SyntaxKind.StringLiteral: - return getTypeFromStringLiteral(node); + return getTypeFromStringLiteral(node); case SyntaxKind.TypeReference: return getTypeFromTypeReference(node); case SyntaxKind.HeritageClauseElement: @@ -6731,7 +6731,7 @@ module ts { let argType = i === 0 && node.kind === SyntaxKind.TaggedTemplateExpression ? globalTemplateStringsArrayType : arg.kind === SyntaxKind.StringLiteral && !reportErrors - ? getStringLiteralType(arg) + ? getStringLiteralType(arg) : checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); // Use argument expression as error location when reporting errors diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 712d2f6b497..3f613271ce1 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -258,7 +258,7 @@ module ts { handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning)); } - function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode | StringLiteralExpression, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { + function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; write(": "); if (type) { @@ -1126,7 +1126,7 @@ module ts { writeLine(); } - function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode | StringLiteralExpression { + function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode { if (accessor) { return accessor.kind === SyntaxKind.GetAccessor ? accessor.type // Getter - return type diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5fce6df3bd5..99803f94e4d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1803,7 +1803,7 @@ module ts { function parseParameterType(): TypeNode { if (parseOptional(SyntaxKind.ColonToken)) { return token === SyntaxKind.StringLiteral - ? parseLiteralNode(/*internName:*/ true) + ? parseLiteralNode(/*internName:*/ true) : parseType(); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 39904107e97..370ece347a3 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -596,7 +596,11 @@ module ts { type: TypeNode; } - export interface StringLiteralTypeNode extends LiteralExpression, TypeNode { } + // Note that a StringLiteral AST node is both an Expression and a TypeNode. The latter is + // because string literals can appear in the type annotation of a parameter node. + export interface StringLiteral extends LiteralExpression, TypeNode { + _stringLiteralBrand: any; + } // Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing. // Consider 'Expression'. Without the brand, 'Expression' is actually no different @@ -689,10 +693,6 @@ module ts { hasExtendedUnicodeEscape?: boolean; } - export interface StringLiteralExpression extends LiteralExpression, TypeNode { - _stringLiteralExpressionBrand: any; - } - export interface TemplateExpression extends PrimaryExpression { head: LiteralExpression; templateSpans: NodeArray;