Checker updates for decorators

This commit is contained in:
Ron Buckton
2015-03-17 11:28:19 -07:00
parent 8282a0b9ca
commit 39001f5395
3 changed files with 187 additions and 25 deletions

View File

@@ -100,11 +100,18 @@ module ts {
let globalIterableType: ObjectType;
let anyArrayType: Type;
let globalTypedPropertyDescriptorType: ObjectType;
let globalClassDecoratorType: ObjectType;
let globalClassAnnotationType: ObjectType;
let globalParameterAnnotationType: ObjectType;
let globalPropertyAnnotationType: ObjectType;
let globalPropertyDecoratorType: ObjectType;
let tupleTypes: Map<TupleType> = {};
let unionTypes: Map<UnionType> = {};
let stringLiteralTypes: Map<StringLiteralType> = {};
let emitExtends = false;
let emitDecorate = false;
let mergedSymbols: Symbol[] = [];
let symbolLinks: SymbolLinks[] = [];
@@ -311,6 +318,7 @@ module ts {
let lastLocation: Node;
let propertyWithInvalidInitializer: Node;
let errorLocation = location;
let grandparent: Node;
loop: while (location) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
@@ -376,7 +384,7 @@ module ts {
// }
//
case SyntaxKind.ComputedPropertyName:
let grandparent = location.parent.parent;
grandparent = location.parent.parent;
if (grandparent.kind === SyntaxKind.ClassDeclaration || grandparent.kind === SyntaxKind.InterfaceDeclaration) {
// A reference to this grandparent's type parameters would be an error
if (result = getSymbol(getSymbolOfNode(grandparent).members, name, meaning & SymbolFlags.Type)) {
@@ -408,6 +416,26 @@ module ts {
break loop;
}
break;
case SyntaxKind.Decorator:
if (location.parent) {
lastLocation = location;
grandparent = location.parent.parent;
if (location.parent.kind === SyntaxKind.Parameter) {
// Parameter decorators are resolved in the context of their great-grandparent
if (grandparent) {
location = grandparent.parent;
}
else {
break loop;
}
}
else {
// all other decorators are resolved in the context of their grandparent
location = grandparent;
}
continue;
}
break;
}
lastLocation = location;
location = location.parent;
@@ -7852,7 +7880,7 @@ module ts {
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
// Grammar checking
checkGrammarModifiers(node) || checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.name);
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.name);
checkVariableLikeDeclaration(node);
let func = getContainingFunction(node);
@@ -7954,7 +7982,7 @@ module ts {
function checkPropertyDeclaration(node: PropertyDeclaration) {
// Grammar checking
checkGrammarModifiers(node) || checkGrammarProperty(node) || checkGrammarComputedPropertyName(node.name);
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarProperty(node) || checkGrammarComputedPropertyName(node.name);
checkVariableLikeDeclaration(node);
}
@@ -8093,6 +8121,10 @@ module ts {
checkFunctionLikeDeclaration(node);
}
function checkIncompleteDeclaration(node: Declaration) {
error(node, Diagnostics.Declaration_expected);
}
function checkTypeReference(node: TypeReferenceNode) {
// Grammar checking
checkGrammarTypeArguments(node, node.typeArguments);
@@ -8484,6 +8516,70 @@ module ts {
}
}
function checkDecoratorSignature(node: Decorator, exprType: Type, expectedAnnotationType: Type, parentType?: Type, expectedDecoratorType?: Type, message?: DiagnosticMessage) {
if (checkTypeAssignableTo(exprType, expectedAnnotationType, node) && expectedDecoratorType) {
let signature = getSingleCallSignature(expectedDecoratorType);
if (!signature) {
// if we couldn't get the signature of the decorator function type, it is likely because we are using an out-of-date lib.d.ts
// and we have already reported an error in initializeTypeChecker.
return;
}
let instantiatedSignature = getSignatureInstantiation(signature, [parentType]);
let instantiatedSignatureType = getOrCreateTypeFromSignature(instantiatedSignature);
checkTypeAssignableTo(exprType, instantiatedSignatureType, node, message);
}
}
/** Check a decorator */
function checkDecorator(node: Decorator): void {
let expression: Expression = node.expression;
let exprType = checkExpression(expression);
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
let classSymbol = getSymbolOfNode(node.parent);
let classType = getTypeOfSymbol(classSymbol);
checkDecoratorSignature(node, exprType, globalClassAnnotationType, classType, globalClassDecoratorType, Diagnostics.Decorators_may_not_change_the_type_of_a_class);
break;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
let propertyType = getTypeOfNode(node.parent);
checkDecoratorSignature(node, exprType, globalPropertyAnnotationType, propertyType, globalPropertyDecoratorType, Diagnostics.Decorators_may_not_change_the_type_of_a_member);
break;
case SyntaxKind.Parameter:
checkDecoratorSignature(node, exprType, globalParameterAnnotationType);
break;
}
}
/** Check the decorators of a node */
function checkDecorators(node: Node): void {
if (!node.decorators) {
return;
}
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
emitDecorate = true;
break;
default:
return;
}
forEach(node.decorators, checkDecorator);
}
function checkFunctionDeclaration(node: FunctionDeclaration): void {
if (produceDiagnostics) {
checkFunctionLikeDeclaration(node) ||
@@ -8498,6 +8594,7 @@ module ts {
}
function checkFunctionLikeDeclaration(node: FunctionLikeDeclaration): void {
checkDecorators(node);
checkSignatureDeclaration(node);
// Do not use hasDynamicName here, because that returns false for well known symbols.
@@ -8780,6 +8877,7 @@ module ts {
// Check variable, parameter, or property declaration
function checkVariableLikeDeclaration(node: VariableLikeDeclaration) {
checkDecorators(node);
checkSourceElement(node.type);
// For a computed property, just check the initializer and exit
// Do not use hasDynamicName here, because that returns false for well known symbols.
@@ -8852,7 +8950,7 @@ module ts {
function checkVariableStatement(node: VariableStatement) {
// Grammar checking
checkGrammarDisallowedModifiersInBlockOrObjectLiteralExpression(node) || checkGrammarModifiers(node) || checkGrammarVariableDeclarationList(node.declarationList) || checkGrammarForDisallowedLetOrConstStatement(node);
checkGrammarDecorators(node) || checkGrammarDisallowedModifiersInBlockOrObjectLiteralExpression(node) || checkGrammarModifiers(node) || checkGrammarVariableDeclarationList(node.declarationList) || checkGrammarForDisallowedLetOrConstStatement(node);
forEach(node.declarationList.declarations, checkSourceElement);
}
@@ -9483,7 +9581,7 @@ module ts {
function checkClassDeclaration(node: ClassDeclaration) {
// Grammar checking
checkGrammarClassDeclarationHeritageClauses(node);
checkDecorators(node);
if (node.name) {
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
@@ -9688,7 +9786,7 @@ module ts {
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
// Grammar checking
checkGrammarModifiers(node) || checkGrammarInterfaceDeclaration(node);
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarInterfaceDeclaration(node);
checkTypeParameters(node.typeParameters);
if (produceDiagnostics) {
@@ -9725,7 +9823,7 @@ module ts {
function checkTypeAliasDeclaration(node: TypeAliasDeclaration) {
// Grammar checking
checkGrammarModifiers(node);
checkGrammarDecorators(node) || checkGrammarModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
checkSourceElement(node.type);
@@ -9894,7 +9992,7 @@ module ts {
}
// Grammar checking
checkGrammarModifiers(node) || checkGrammarEnumDeclaration(node);
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarEnumDeclaration(node);
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
@@ -9960,7 +10058,7 @@ module ts {
function checkModuleDeclaration(node: ModuleDeclaration) {
if (produceDiagnostics) {
// Grammar checking
if (!checkGrammarModifiers(node)) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node)) {
if (!isInAmbientContext(node) && node.name.kind === SyntaxKind.StringLiteral) {
grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names);
}
@@ -10055,7 +10153,7 @@ module ts {
}
function checkImportDeclaration(node: ImportDeclaration) {
if (!checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers);
}
if (checkExternalImportOrExportDeclaration(node)) {
@@ -10077,7 +10175,7 @@ module ts {
}
function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) {
checkGrammarModifiers(node);
checkGrammarDecorators(node) || checkGrammarModifiers(node);
if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) {
checkImportBinding(node);
if (node.flags & NodeFlags.Export) {
@@ -10108,7 +10206,7 @@ module ts {
}
function checkExportDeclaration(node: ExportDeclaration) {
if (!checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers);
}
if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) {
@@ -10132,7 +10230,7 @@ module ts {
return;
}
// Grammar checking
if (!checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
}
if (node.expression) {
@@ -10232,6 +10330,8 @@ module ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return checkAccessorDeclaration(<AccessorDeclaration>node);
case SyntaxKind.IncompleteDeclaration:
return checkIncompleteDeclaration(<Declaration>node);
case SyntaxKind.TypeReference:
return checkTypeReference(<TypeReferenceNode>node);
case SyntaxKind.TypeQuery:
@@ -10436,6 +10536,10 @@ module ts {
links.flags |= NodeCheckFlags.EmitExtends;
}
if (emitDecorate) {
links.flags |= NodeCheckFlags.EmitDecorate;
}
links.flags |= NodeCheckFlags.TypeChecked;
}
}
@@ -11235,6 +11339,12 @@ module ts {
globalNumberType = getGlobalType("Number");
globalBooleanType = getGlobalType("Boolean");
globalRegExpType = getGlobalType("RegExp");
globalTypedPropertyDescriptorType = getTypeOfGlobalSymbol(getGlobalTypeSymbol("TypedPropertyDescriptor"), 1);
globalClassDecoratorType = getGlobalType("ClassDecorator");
globalPropertyDecoratorType = getGlobalType("PropertyDecorator");
globalClassAnnotationType = getGlobalType("ClassAnnotation");
globalPropertyAnnotationType = getGlobalType("PropertyAnnotation");
globalParameterAnnotationType = getGlobalType("ParameterAnnotation");
// If we're in ES6 mode, load the TemplateStringsArray.
// Otherwise, default to 'unknown' for the purposes of type checking in LS scenarios.
@@ -11259,6 +11369,42 @@ module ts {
// GRAMMAR CHECKING
function checkGrammarDecorators(node: Node): boolean {
if (!node.decorators) {
return false;
}
let target = node;
while (target) {
switch (target.kind) {
case SyntaxKind.ClassDeclaration:
return false;
case SyntaxKind.Constructor:
if (node.kind !== SyntaxKind.Parameter) {
break;
}
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
target = target.parent;
continue;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
if ((<FunctionLikeDeclaration>target).body) {
target = target.parent;
continue;
}
break;
}
break;
}
return grammarErrorOnNode(node, Diagnostics.Decorators_are_not_valid_on_this_declaration_type);
}
function checkGrammarModifiers(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.GetAccessor:
@@ -11455,7 +11601,7 @@ module ts {
function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
// Prevent cascading error by short-circuit
let file = getSourceFileOfNode(node);
return checkGrammarModifiers(node) || checkGrammarTypeParameterList(node, node.typeParameters, file) ||
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarTypeParameterList(node, node.typeParameters, file) ||
checkGrammarParameterList(node.parameters) || checkGrammarArrowFunction(node, file);
}
@@ -11512,7 +11658,7 @@ module ts {
function checkGrammarIndexSignature(node: SignatureDeclaration) {
// Prevent cascading error by short-circuit
checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node) || checkGrammarForIndexSignatureModifier(node);
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node) || checkGrammarForIndexSignatureModifier(node);
}
function checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
@@ -11561,7 +11707,7 @@ module ts {
let seenExtendsClause = false;
let seenImplementsClause = false;
if (!checkGrammarModifiers(node) && node.heritageClauses) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && node.heritageClauses) {
for (let heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {

View File

@@ -1346,17 +1346,18 @@ module ts {
}
export const enum NodeCheckFlags {
TypeChecked = 0x00000001, // Node has been type checked
LexicalThis = 0x00000002, // Lexical 'this' reference
CaptureThis = 0x00000004, // Lexical 'this' used in body
EmitExtends = 0x00000008, // Emit __extends
SuperInstance = 0x00000010, // Instance 'super' reference
SuperStatic = 0x00000020, // Static 'super' reference
ContextChecked = 0x00000040, // Contextual types have been assigned
TypeChecked = 0x00000001, // Node has been type checked
LexicalThis = 0x00000002, // Lexical 'this' reference
CaptureThis = 0x00000004, // Lexical 'this' used in body
EmitExtends = 0x00000008, // Emit __extends
SuperInstance = 0x00000010, // Instance 'super' reference
SuperStatic = 0x00000020, // Static 'super' reference
ContextChecked = 0x00000040, // Contextual types have been assigned
// Values for enum members have been computed, and any errors have been reported for them.
EnumValuesComputed = 0x00000080,
BlockScopedBindingInLoop = 0x00000100,
EnumValuesComputed = 0x00000080,
BlockScopedBindingInLoop = 0x00000100,
EmitDecorate = 0x00000200, // Emit __decorate
}
export interface NodeLinks {

15
src/lib/core.d.ts vendored
View File

@@ -1155,3 +1155,18 @@ interface ArrayConstructor {
}
declare var Array: ArrayConstructor;
interface TypedPropertyDescriptor<T> {
enumerable?: boolean;
configurable?: boolean;
writable?: boolean;
value?: T;
get?: () => T;
set?: (value: T) => void;
}
interface ClassDecorator { <TFunction extends Function>(target: TFunction): TFunction | void; }
interface PropertyDecorator { <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void; }
interface ClassAnnotation { (target: Function): void; }
interface PropertyAnnotation { (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): void; }
interface ParameterAnnotation { (target: Function, parameterIndex: number): void; }