diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 423c3c316275a..a5565a1063278 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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 = {}; let unionTypes: Map = {}; let stringLiteralTypes: Map = {}; 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, node.name); + checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarEvalOrArgumentsInStrictMode(node, 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(node); + case SyntaxKind.IncompleteDeclaration: + return checkIncompleteDeclaration(node); case SyntaxKind.TypeReference: return checkTypeReference(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 ((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): 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) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bdf824504c529..02ca6627b2e0b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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 { diff --git a/src/lib/core.d.ts b/src/lib/core.d.ts index 040eb69ae9613..930f5781c918a 100644 --- a/src/lib/core.d.ts +++ b/src/lib/core.d.ts @@ -1155,3 +1155,18 @@ interface ArrayConstructor { } declare var Array: ArrayConstructor; + +interface TypedPropertyDescriptor { + enumerable?: boolean; + configurable?: boolean; + writable?: boolean; + value?: T; + get?: () => T; + set?: (value: T) => void; +} + +interface ClassDecorator { (target: TFunction): TFunction | void; } +interface PropertyDecorator { (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void; } +interface ClassAnnotation { (target: Function): void; } +interface PropertyAnnotation { (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): void; } +interface ParameterAnnotation { (target: Function, parameterIndex: number): void; }