Provide greater type safety in the ast system.

This commit is contained in:
Cyrus Najmabadi 2014-12-01 18:42:07 -08:00
parent afa198e9fa
commit a29862eea5
4 changed files with 84 additions and 64 deletions

View File

@ -1666,7 +1666,7 @@ module ts {
return classType.typeParameters ? createTypeReference(<GenericType>classType, map(classType.typeParameters, _ => anyType)) : classType;
}
function getTypeOfVariableOrPropertyDeclaration(declaration: VariableDeclaration | PropertyDeclaration): Type {
function getTypeOfVariableOrParameterOrPropertyDeclaration(declaration: VariableOrParameterOrPropertyDeclaration): Type {
// A variable declared in a for..in statement is always of type any
if (declaration.parent.kind === SyntaxKind.ForInStatement) {
return anyType;
@ -1758,7 +1758,7 @@ module ts {
}
// Handle variable, parameter or property
links.type = resolvingType;
var type = getTypeOfVariableOrPropertyDeclaration(<VariableDeclaration>declaration);
var type = getTypeOfVariableOrParameterOrPropertyDeclaration(<VariableDeclaration>declaration);
if (links.type === resolvingType) {
links.type = type;
}
@ -1775,7 +1775,7 @@ module ts {
return links.type;
}
function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode {
function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode | LiteralExpression {
return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type;
}
@ -3018,14 +3018,17 @@ module ts {
return links.resolvedType;
}
function getStringLiteralType(node: StringLiteralTypeNode): StringLiteralType {
if (hasProperty(stringLiteralTypes, node.text)) return stringLiteralTypes[node.text];
function getStringLiteralType(node: LiteralExpression): StringLiteralType {
if (hasProperty(stringLiteralTypes, node.text)) {
return stringLiteralTypes[node.text];
}
var type = stringLiteralTypes[node.text] = <StringLiteralType>createType(TypeFlags.StringLiteral);
type.text = getTextOfNode(node);
return type;
}
function getTypeFromStringLiteral(node: StringLiteralTypeNode): Type {
function getTypeFromStringLiteral(node: LiteralExpression): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getStringLiteralType(node);
@ -3033,7 +3036,7 @@ module ts {
return links.resolvedType;
}
function getTypeFromTypeNode(node: TypeNode): Type {
function getTypeFromTypeNode(node: TypeNode | LiteralExpression): Type {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
return anyType;
@ -3046,7 +3049,7 @@ module ts {
case SyntaxKind.VoidKeyword:
return voidType;
case SyntaxKind.StringLiteral:
return getTypeFromStringLiteral(<StringLiteralTypeNode>node);
return getTypeFromStringLiteral(<LiteralExpression>node);
case SyntaxKind.TypeReference:
return getTypeFromTypeReferenceNode(<TypeReferenceNode>node);
case SyntaxKind.TypeQuery:
@ -4771,13 +4774,13 @@ module ts {
// typed function expression, the contextual type of an initializer expression is the contextual type of the
// parameter.
function getContextualTypeForInitializerExpression(node: Expression): Type {
var declaration = <VariableDeclaration>node.parent;
var declaration = <VariableOrParameterDeclaration>node.parent;
if (node === declaration.initializer) {
if (declaration.type) {
return getTypeFromTypeNode(declaration.type);
}
if (declaration.kind === SyntaxKind.Parameter) {
return getContextuallyTypedParameterType(declaration);
return getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
}
}
return undefined;
@ -6661,7 +6664,7 @@ module ts {
}
function checkParameter(parameterDeclaration: ParameterDeclaration) {
checkVariableDeclaration(parameterDeclaration);
checkVariableOrParameterDeclaration(parameterDeclaration);
if (fullTypeCheck) {
checkCollisionWithIndexVariableInGeneratedCode(parameterDeclaration, parameterDeclaration.name);
@ -6784,7 +6787,7 @@ module ts {
function checkPropertyDeclaration(node: PropertyDeclaration) {
if (fullTypeCheck) {
checkVariableOrPropertyInFullTypeCheck(node);
checkVariableOrParameterOrPropertyInFullTypeCheck(node);
}
}
@ -7521,7 +7524,7 @@ module ts {
}
}
function checkCollisionWithConstDeclarations(node: VariableDeclaration) {
function checkCollisionWithConstDeclarations(node: VariableOrParameterDeclaration) {
// Variable declarations are hoisted to the top of their function scope. They can shadow
// block scoped declarations, which bind tighter. this will not be flagged as duplicate definition
// by the binder as the declaration scope is different.
@ -7553,7 +7556,7 @@ module ts {
}
}
function checkVariableOrPropertyInFullTypeCheck(node: VariableDeclaration | PropertyDeclaration) {
function checkVariableOrParameterOrPropertyInFullTypeCheck(node: VariableOrParameterOrPropertyDeclaration) {
Debug.assert(fullTypeCheck);
checkSourceElement(node.type);
@ -7565,7 +7568,7 @@ module ts {
var symbol = getSymbolOfNode(node);
var type: Type;
if (symbol.valueDeclaration !== node) {
type = getTypeOfVariableOrPropertyDeclaration(node);
type = getTypeOfVariableOrParameterOrPropertyDeclaration(node);
}
else {
type = getTypeOfVariableOrParameterOrProperty(symbol);
@ -7579,9 +7582,9 @@ module ts {
return type;
}
function checkVariableDeclaration(node: VariableDeclaration) {
function checkVariableOrParameterDeclaration(node: VariableOrParameterDeclaration) {
if (fullTypeCheck) {
var type = checkVariableOrPropertyInFullTypeCheck(node);
var type = checkVariableOrParameterOrPropertyInFullTypeCheck(node);
checkExportsOnMergedDeclarations(node);
if (node.initializer) {
checkCollisionWithConstDeclarations(node);
@ -7604,7 +7607,7 @@ module ts {
}
function checkVariableStatement(node: VariableStatement) {
forEach(node.declarations, checkVariableDeclaration);
forEach(node.declarations, checkVariableOrParameterDeclaration);
}
function checkExpressionStatement(node: ExpressionStatement) {
@ -7628,7 +7631,7 @@ module ts {
}
function checkForStatement(node: ForStatement) {
if (node.declarations) forEach(node.declarations, checkVariableDeclaration);
if (node.declarations) forEach(node.declarations, checkVariableOrParameterDeclaration);
if (node.initializer) checkExpression(node.initializer);
if (node.condition) checkExpression(node.condition);
if (node.iterator) checkExpression(node.iterator);
@ -7645,7 +7648,7 @@ module ts {
if (node.declarations) {
if (node.declarations.length >= 1) {
var decl = node.declarations[0];
checkVariableDeclaration(decl);
checkVariableOrParameterDeclaration(decl);
if (decl.type) {
error(decl, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation);
}
@ -8847,7 +8850,7 @@ module ts {
return node === (<TypeAssertion>parent).type;
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return (<CallExpression>parent).typeArguments && (<CallExpression>parent).typeArguments.indexOf(node) >= 0;
return (<CallExpression>parent).typeArguments && indexOf((<CallExpression>parent).typeArguments, node) >= 0;
case SyntaxKind.TaggedTemplateExpression:
// TODO (drosen): TaggedTemplateExpressions may eventually support type arguments.
return false;
@ -9251,12 +9254,12 @@ module ts {
return undefined;
}
function writeTypeAtLocation(location: Node, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableOrParameterDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
// Get type of the symbol if this is the valid symbol otherwise get type at location
var symbol = getSymbolOfNode(location);
var symbol = getSymbolOfNode(declaration);
var type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.CallSignature | SymbolFlags.ConstructSignature))
? getTypeOfSymbol(symbol)
: getTypeFromTypeNode(location);
: unknownType;
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
}
@ -9280,7 +9283,7 @@ module ts {
isEmitBlocked,
isDeclarationVisible,
isImplementationOfOverload,
writeTypeAtLocation,
writeTypeOfDeclaration,
writeReturnTypeOfSignatureDeclaration,
isSymbolAccessible,
isEntityNameVisible,

View File

@ -440,7 +440,7 @@ module ts {
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning));
}
function writeTypeAtLocation(location: Node, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableOrParameterDeclaration, type: TypeNode | StringLiteralExpression, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
write(": ");
if (type) {
@ -448,7 +448,7 @@ module ts {
emitType(type);
}
else {
resolver.writeTypeAtLocation(location, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
}
}
@ -494,12 +494,12 @@ module ts {
}
}
function emitTypeWithNewGetSymbolAccessibilityDiangostic(type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
function emitTypeWithNewGetSymbolAccessibilityDiagnostic(type: TypeNode | EntityName, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
emitType(type);
}
function emitType(type: TypeNode) {
function emitType(type: TypeNode | StringLiteralExpression | Identifier | QualifiedName) {
switch (type.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.StringKeyword:
@ -522,7 +522,7 @@ module ts {
return emitParenType(<ParenthesizedTypeNode>type);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
return emitSignatureDeclarationWithJsDocComments(<SignatureDeclaration>type);
return emitSignatureDeclarationWithJsDocComments(<FunctionOrConstructorTypeNode>type);
case SyntaxKind.TypeLiteral:
return emitTypeLiteral(<TypeLiteralNode>type);
case SyntaxKind.Identifier:
@ -666,7 +666,7 @@ module ts {
writeTextOfNode(currentSourceFile, node.name);
write(" = ");
if (isInternalModuleImportDeclaration(node)) {
emitTypeWithNewGetSymbolAccessibilityDiangostic(node.moduleReference, getImportEntityNameVisibilityError);
emitTypeWithNewGetSymbolAccessibilityDiagnostic(<EntityName>node.moduleReference, getImportEntityNameVisibilityError);
write(";");
}
else {
@ -716,7 +716,7 @@ module ts {
write("type ");
writeTextOfNode(currentSourceFile, node.name);
write(" = ");
emitTypeWithNewGetSymbolAccessibilityDiangostic(node.type, getTypeAliasDeclarationVisibilityError);
emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.type, getTypeAliasDeclarationVisibilityError);
write(";");
writeLine();
}
@ -780,7 +780,7 @@ module ts {
emitType(node.constraint);
}
else {
emitTypeWithNewGetSymbolAccessibilityDiangostic(node.constraint, getTypeParameterConstraintVisibilityError);
emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.constraint, getTypeParameterConstraintVisibilityError);
}
}
@ -845,8 +845,8 @@ module ts {
emitCommaList(typeReferences, emitTypeOfTypeReference);
}
function emitTypeOfTypeReference(node: Node) {
emitTypeWithNewGetSymbolAccessibilityDiangostic(node, getHeritageClauseVisibilityError);
function emitTypeOfTypeReference(node: TypeReferenceNode) {
emitTypeWithNewGetSymbolAccessibilityDiagnostic(node, getHeritageClauseVisibilityError);
function getHeritageClauseVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic {
var diagnosticMessage: DiagnosticMessage;
@ -950,7 +950,7 @@ module ts {
emitTypeOfVariableDeclarationFromTypeLiteral(node);
}
else if (!(node.flags & NodeFlags.Private)) {
writeTypeAtLocation(node, node.type, getVariableDeclarationTypeVisibilityError);
writeTypeOfDeclaration(node, node.type, getVariableDeclarationTypeVisibilityError);
}
}
@ -996,7 +996,7 @@ module ts {
}
}
function emitTypeOfVariableDeclarationFromTypeLiteral(node: VariableDeclaration) {
function emitTypeOfVariableDeclarationFromTypeLiteral(node: VariableOrParameterDeclaration) {
// if this is property of type literal,
// or is parameter of method/call/construct/index signature of type literal
// emit only if type is specified
@ -1044,17 +1044,17 @@ module ts {
accessorWithTypeAnnotation = anotherAccessor;
}
}
writeTypeAtLocation(node, type, getAccessorDeclarationTypeVisibilityError);
writeTypeOfDeclaration(node, type, getAccessorDeclarationTypeVisibilityError);
}
write(";");
writeLine();
}
function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode {
function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode | StringLiteralExpression {
if (accessor) {
return accessor.kind === SyntaxKind.GetAccessor ?
accessor.type : // Getter - return type
accessor.parameters[0].type; // Setter parameter type
return accessor.kind === SyntaxKind.GetAccessor
? accessor.type // Getter - return type
: accessor.parameters[0].type; // Setter parameter type
}
}
@ -1267,7 +1267,7 @@ module ts {
emitTypeOfVariableDeclarationFromTypeLiteral(node);
}
else if (!(node.parent.flags & NodeFlags.Private)) {
writeTypeAtLocation(node, node.type, getParameterDeclarationTypeVisibilityError);
writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError);
}
function getParameterDeclarationTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic {

View File

@ -1665,8 +1665,8 @@ module ts {
return allowIdentifierNames ? parseIdentifierName() : parseIdentifier();
}
function parseTokenNode(): PrimaryExpression {
var node = <PrimaryExpression>createNode(token);
function parseTokenNode<T extends Node>(): T {
var node = <T>createNode(token);
nextToken();
return finishNode(node);
}
@ -1789,10 +1789,14 @@ module ts {
}
}
function parseParameterType(): TypeNode {
return parseOptional(SyntaxKind.ColonToken)
? token === SyntaxKind.StringLiteral ? parseLiteralNode(/*internName:*/ true) : parseType()
: undefined;
function parseParameterType(): TypeNode | StringLiteralExpression {
if (parseOptional(SyntaxKind.ColonToken)) {
return token === SyntaxKind.StringLiteral
? <StringLiteralExpression>parseLiteralNode(/*internName:*/ true)
: parseType();
}
return undefined;
}
function isStartOfParameter(): boolean {
@ -2095,15 +2099,15 @@ module ts {
return finishNode(node);
}
function parseFunctionType(typeKind: SyntaxKind): SignatureDeclaration {
var node = <SignatureDeclaration>createNode(typeKind);
function parseFunctionType(typeKind: SyntaxKind): FunctionOrConstructorTypeNode {
var node = <FunctionOrConstructorTypeNode>createNode(typeKind);
fillSignature(typeKind === SyntaxKind.FunctionType ? SyntaxKind.CallSignature : SyntaxKind.ConstructSignature,
SyntaxKind.EqualsGreaterThanToken, /* returnTokenRequired */ true, /*yieldAndGeneratorParameterContext:*/ false, node);
return finishNode(node);
}
function parseKeywordAndNoDot(): Node {
var node = parseTokenNode();
function parseKeywordAndNoDot(): TypeNode {
var node = parseTokenNode<TypeNode>();
return token === SyntaxKind.DotToken ? undefined : node;
}
@ -2117,7 +2121,7 @@ module ts {
var node = tryParse(parseKeywordAndNoDot);
return node || parseTypeReference();
case SyntaxKind.VoidKeyword:
return parseTokenNode();
return parseTokenNode<TypeNode>();
case SyntaxKind.TypeOfKeyword:
return parseTypeQuery();
case SyntaxKind.OpenBraceToken:
@ -2888,7 +2892,7 @@ module ts {
}
function parseSuperExpression(): MemberExpression {
var expression = parseTokenNode();
var expression = parseTokenNode<PrimaryExpression>();
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.DotToken) {
return expression;
}
@ -3025,7 +3029,7 @@ module ts {
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return parseTokenNode();
return parseTokenNode<PrimaryExpression>();
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:

View File

@ -368,17 +368,24 @@ module ts {
initializer?: Expression;
}
export interface ParameterDeclaration extends Declaration {
name: Identifier;
type?: TypeNode | StringLiteralExpression;
initializer?: Expression;
}
export interface PropertyDeclaration extends Declaration, ClassElement {
type?: TypeNode;
initializer?: Expression;
}
export type VariableOrParameterDeclaration = VariableDeclaration | ParameterDeclaration;
export type VariableOrParameterOrPropertyDeclaration = VariableOrParameterDeclaration | PropertyDeclaration;
export interface ShortHandPropertyDeclaration extends Declaration {
name: Identifier;
}
export interface ParameterDeclaration extends VariableDeclaration { }
/**
* Several node kinds share function-like features such as a signature,
* a name, and a body. These nodes should extend FunctionLikeDeclaration.
@ -415,7 +422,13 @@ module ts {
_indexSignatureDeclarationBrand: any;
}
export interface TypeNode extends Node { }
export interface TypeNode extends Node {
_typeNodeBrand: any;
}
export interface FunctionOrConstructorTypeNode extends TypeNode, SignatureDeclaration {
_functionOrConstructorTypeNodeBrand: any;
}
export interface TypeReferenceNode extends TypeNode {
typeName: EntityName;
@ -447,10 +460,6 @@ module ts {
type: TypeNode;
}
export interface StringLiteralTypeNode extends TypeNode {
text: string;
}
// 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
// (structurally) than 'Node'. Because of this you can pass any Node to a function that
@ -535,6 +544,10 @@ module ts {
isUnterminated?: boolean;
}
export interface StringLiteralExpression extends LiteralExpression {
_stringLiteralExpressionBrand: any;
}
export interface TemplateExpression extends PrimaryExpression {
head: LiteralExpression;
templateSpans: NodeArray<TemplateSpan>;
@ -963,7 +976,7 @@ module ts {
hasSemanticErrors(): boolean;
isDeclarationVisible(node: Declaration): boolean;
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean;
writeTypeAtLocation(location: Node, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableOrParameterDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult;
isEntityNameVisible(entityName: EntityName, enclosingDeclaration: Node): SymbolVisibilityResult;