diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 40a6b2e3f72dc..08cc7fe23788c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7092,6 +7092,11 @@ module ts { return resolveErrorCall(node); } + var valueDecl = (expressionType.symbol ? expressionType.symbol.valueDeclaration : undefined); + if (valueDecl && valueDecl.flags & NodeFlags.Abstract) { + error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(valueDecl.name)); + } + // Technically, this signatures list may be incomplete. We are taking the apparent type, // but we are not including construct signatures that may have been added to the Object or // Function interface, since they have none by default. This is a bit of a leap of faith diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 3f613271ce1b0..7cbf826fac099 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -908,6 +908,10 @@ module ts { emitJsDocComments(node); emitModuleElementDeclarationFlags(node); + if (node.flags & NodeFlags.Abstract) { + write("abstract "); + } + write("class "); writeTextOfNode(currentSourceFile, node.name); let prevEnclosingDeclaration = enclosingDeclaration; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index b44d937950938..3d086b9a07e84 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -365,6 +365,7 @@ module ts { An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2499, category: DiagnosticCategory.Error, key: "An interface can only extend an identifier/qualified-name with optional type arguments." }, A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2500, category: DiagnosticCategory.Error, key: "A class can only implement an identifier/qualified-name with optional type arguments." }, A_rest_element_cannot_contain_a_binding_pattern: { code: 2501, category: DiagnosticCategory.Error, key: "A rest element cannot contain a binding pattern." }, + Cannot_create_an_instance_of_the_abstract_class_0: { code: 2502, category: DiagnosticCategory.Error, key: "Cannot create an instance of the abstract class '{0}'" }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2ab6b8358ebd2..3a9fcd44e733b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1447,6 +1447,10 @@ "category": "Error", "code": 2501 }, + "Cannot create an instance of the abstract class '{0}'": { + "category": "Error", + "code": 2502 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index df6c20a8107dc..ab8b5ab63ebd4 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -43,6 +43,7 @@ module ts { } let textToToken: Map = { + "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, "boolean": SyntaxKind.BooleanKeyword, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5bc3219e4d620..fb3a37b97da11 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -131,6 +131,7 @@ module ts { StaticKeyword, YieldKeyword, // Contextual keywords + AbstractKeyword, AsKeyword, AnyKeyword, BooleanKeyword, @@ -307,15 +308,16 @@ module ts { Protected = 0x00000040, // Property/Method Static = 0x00000080, // Property/Method Default = 0x00000100, // Function/Class (export default declaration) - MultiLine = 0x00000200, // Multi-line array or object literal - Synthetic = 0x00000400, // Synthetic node (for full fidelity) - DeclarationFile = 0x00000800, // Node is a .d.ts file - Let = 0x00001000, // Variable declaration - Const = 0x00002000, // Variable declaration - OctalLiteral = 0x00004000, - ExportContext = 0x00008000, // Export context (initialized by binding) - - Modifier = Export | Ambient | Public | Private | Protected | Static | Default, + Abstract = 0x00000200, + MultiLine = 0x00000400, // Multi-line array or object literal + Synthetic = 0x00000800, // Synthetic node (for full fidelity) + DeclarationFile = 0x00001000, // Node is a .d.ts file + Let = 0x00002000, // Variable declaration + Const = 0x00004000, // Variable declaration + OctalLiteral = 0x00008000, + ExportContext = 0x00010000, // Export context (initialized by binding) + + Modifier = Export | Ambient | Public | Private | Protected | Static | Default | Abstract, AccessibilityModifier = Public | Private | Protected, BlockScoped = Let | Const } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9e77e1c578441..302d4861b4ee2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1137,6 +1137,7 @@ module ts { export function isModifier(token: SyntaxKind): boolean { switch (token) { + case SyntaxKind.AbstractKeyword: case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: @@ -1626,6 +1627,7 @@ module ts { case SyntaxKind.PublicKeyword: return NodeFlags.Public; case SyntaxKind.ProtectedKeyword: return NodeFlags.Protected; case SyntaxKind.PrivateKeyword: return NodeFlags.Private; + case SyntaxKind.AbstractKeyword: return NodeFlags.Abstract; case SyntaxKind.ExportKeyword: return NodeFlags.Export; case SyntaxKind.DeclareKeyword: return NodeFlags.Ambient; case SyntaxKind.ConstKeyword: return NodeFlags.Const; diff --git a/tests/baselines/reference/APISample_linter.js b/tests/baselines/reference/APISample_linter.js index 17061add0388b..cd2df23ffbf4f 100644 --- a/tests/baselines/reference/APISample_linter.js +++ b/tests/baselines/reference/APISample_linter.js @@ -75,26 +75,26 @@ function delint(sourceFile) { delintNode(sourceFile); function delintNode(node) { switch (node.kind) { - case 186 /* ForStatement */: - case 187 /* ForInStatement */: - case 185 /* WhileStatement */: - case 184 /* DoStatement */: - if (node.statement.kind !== 179 /* Block */) { + case 187 /* ForStatement */: + case 188 /* ForInStatement */: + case 186 /* WhileStatement */: + case 185 /* DoStatement */: + if (node.statement.kind !== 180 /* Block */) { report(node, "A looping statement's contents should be wrapped in a block body."); } break; - case 183 /* IfStatement */: + case 184 /* IfStatement */: var ifStatement = node; - if (ifStatement.thenStatement.kind !== 179 /* Block */) { + if (ifStatement.thenStatement.kind !== 180 /* Block */) { report(ifStatement.thenStatement, "An if statement's contents should be wrapped in a block body."); } if (ifStatement.elseStatement && - ifStatement.elseStatement.kind !== 179 /* Block */ && - ifStatement.elseStatement.kind !== 183 /* IfStatement */) { + ifStatement.elseStatement.kind !== 180 /* Block */ && + ifStatement.elseStatement.kind !== 184 /* IfStatement */) { report(ifStatement.elseStatement, "An else statement's contents should be wrapped in a block body."); } break; - case 169 /* BinaryExpression */: + case 170 /* BinaryExpression */: var op = node.operatorToken.kind; if (op === 28 /* EqualsEqualsToken */ || op == 29 /* ExclamationEqualsToken */) { report(node, "Use '===' and '!=='."); diff --git a/tests/baselines/reference/abstractClass1.errors.txt b/tests/baselines/reference/abstractClass1.errors.txt new file mode 100644 index 0000000000000..5892f54266317 --- /dev/null +++ b/tests/baselines/reference/abstractClass1.errors.txt @@ -0,0 +1,57 @@ +tests/cases/compiler/abstractClass1.ts(15,9): error TS2502: Cannot create an instance of the abstract class 'Foo' +tests/cases/compiler/abstractClass1.ts(16,9): error TS2346: Supplied parameters do not match any signature of call target. +tests/cases/compiler/abstractClass1.ts(16,9): error TS2502: Cannot create an instance of the abstract class 'Foo' +tests/cases/compiler/abstractClass1.ts(25,1): error TS2502: Cannot create an instance of the abstract class 'Qux' +tests/cases/compiler/abstractClass1.ts(35,1): error TS2346: Supplied parameters do not match any signature of call target. +tests/cases/compiler/abstractClass1.ts(35,1): error TS2502: Cannot create an instance of the abstract class 'Foo' + + +==== tests/cases/compiler/abstractClass1.ts (6 errors) ==== + + abstract class Foo { + constructor(f: any) { } + public static bar(): void { } + + public empty() { } + } + + class Bar extends Foo { + constructor(f: any) { + super(f); + } + } + + var a = new Foo(1); // Error + ~~~~~~~~~~ +!!! error TS2502: Cannot create an instance of the abstract class 'Foo' + var b = new Foo(); // Error because of invalid constructor arguments + ~~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + ~~~~~~~~~ +!!! error TS2502: Cannot create an instance of the abstract class 'Foo' + + module baz { + export abstract class Qux { + } + export class Quz extends Qux { + } + } + + new baz.Qux(); + ~~~~~~~~~~~~~ +!!! error TS2502: Cannot create an instance of the abstract class 'Qux' + + // Valid + var c = new Bar(1); + c.empty(); + + // Calling a static method on a abstract class is valid + Foo.bar(); + + var Copy = Foo; + new Copy(); + ~~~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + ~~~~~~~~~~ +!!! error TS2502: Cannot create an instance of the abstract class 'Foo' + \ No newline at end of file diff --git a/tests/baselines/reference/abstractClass1.js b/tests/baselines/reference/abstractClass1.js new file mode 100644 index 0000000000000..4cb36d91ae1dd --- /dev/null +++ b/tests/baselines/reference/abstractClass1.js @@ -0,0 +1,107 @@ +//// [abstractClass1.ts] + +abstract class Foo { + constructor(f: any) { } + public static bar(): void { } + + public empty() { } +} + +class Bar extends Foo { + constructor(f: any) { + super(f); + } +} + +var a = new Foo(1); // Error +var b = new Foo(); // Error because of invalid constructor arguments + +module baz { + export abstract class Qux { + } + export class Quz extends Qux { + } +} + +new baz.Qux(); + +// Valid +var c = new Bar(1); +c.empty(); + +// Calling a static method on a abstract class is valid +Foo.bar(); + +var Copy = Foo; +new Copy(); + + +//// [abstractClass1.js] +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Foo = (function () { + function Foo(f) { + } + Foo.bar = function () { }; + Foo.prototype.empty = function () { }; + return Foo; +})(); +var Bar = (function (_super) { + __extends(Bar, _super); + function Bar(f) { + _super.call(this, f); + } + return Bar; +})(Foo); +var a = new Foo(1); // Error +var b = new Foo(); // Error because of invalid constructor arguments +var baz; +(function (baz) { + var Qux = (function () { + function Qux() { + } + return Qux; + })(); + baz.Qux = Qux; + var Quz = (function (_super) { + __extends(Quz, _super); + function Quz() { + _super.apply(this, arguments); + } + return Quz; + })(Qux); + baz.Quz = Quz; +})(baz || (baz = {})); +new baz.Qux(); +// Valid +var c = new Bar(1); +c.empty(); +// Calling a static method on a abstract class is valid +Foo.bar(); +var Copy = Foo; +new Copy(); + + +//// [abstractClass1.d.ts] +declare abstract class Foo { + constructor(f: any); + static bar(): void; + empty(): void; +} +declare class Bar extends Foo { + constructor(f: any); +} +declare var a: Foo; +declare var b: any; +declare module baz { + abstract class Qux { + } + class Quz extends Qux { + } +} +declare var c: Bar; +declare var Copy: typeof Foo; diff --git a/tests/baselines/reference/abstractClassIdentifierName.js b/tests/baselines/reference/abstractClassIdentifierName.js new file mode 100644 index 0000000000000..71827d2cb5bd8 --- /dev/null +++ b/tests/baselines/reference/abstractClassIdentifierName.js @@ -0,0 +1,14 @@ +//// [abstractClassIdentifierName.ts] +class abstract { + + abstract(): void { } +} + + +//// [abstractClassIdentifierName.js] +var abstract = (function () { + function abstract() { + } + abstract.prototype.abstract = function () { }; + return abstract; +})(); diff --git a/tests/baselines/reference/abstractClassIdentifierName.symbols b/tests/baselines/reference/abstractClassIdentifierName.symbols new file mode 100644 index 0000000000000..41c46649c435e --- /dev/null +++ b/tests/baselines/reference/abstractClassIdentifierName.symbols @@ -0,0 +1,8 @@ +=== tests/cases/compiler/abstractClassIdentifierName.ts === +class abstract { +>abstract : Symbol(abstract, Decl(abstractClassIdentifierName.ts, 0, 0)) + + abstract(): void { } +>abstract : Symbol(abstract, Decl(abstractClassIdentifierName.ts, 0, 16)) +} + diff --git a/tests/baselines/reference/abstractClassIdentifierName.types b/tests/baselines/reference/abstractClassIdentifierName.types new file mode 100644 index 0000000000000..ef152979f13a0 --- /dev/null +++ b/tests/baselines/reference/abstractClassIdentifierName.types @@ -0,0 +1,8 @@ +=== tests/cases/compiler/abstractClassIdentifierName.ts === +class abstract { +>abstract : abstract + + abstract(): void { } +>abstract : () => void +} + diff --git a/tests/baselines/reference/abstractIdentifierName.js b/tests/baselines/reference/abstractIdentifierName.js new file mode 100644 index 0000000000000..e70d0cfc7cfc2 --- /dev/null +++ b/tests/baselines/reference/abstractIdentifierName.js @@ -0,0 +1,14 @@ +//// [abstractIdentifierName.ts] +var abstract = true; + +function foo() { + "use strict"; + var abstract = true; +} + +//// [abstractIdentifierName.js] +var abstract = true; +function foo() { + "use strict"; + var abstract = true; +} diff --git a/tests/baselines/reference/abstractIdentifierName.symbols b/tests/baselines/reference/abstractIdentifierName.symbols new file mode 100644 index 0000000000000..0dda219353550 --- /dev/null +++ b/tests/baselines/reference/abstractIdentifierName.symbols @@ -0,0 +1,11 @@ +=== tests/cases/compiler/abstractIdentifierName.ts === +var abstract = true; +>abstract : Symbol(abstract, Decl(abstractIdentifierName.ts, 0, 3)) + +function foo() { +>foo : Symbol(foo, Decl(abstractIdentifierName.ts, 0, 20)) + + "use strict"; + var abstract = true; +>abstract : Symbol(abstract, Decl(abstractIdentifierName.ts, 4, 7)) +} diff --git a/tests/baselines/reference/abstractIdentifierName.types b/tests/baselines/reference/abstractIdentifierName.types new file mode 100644 index 0000000000000..d55b30d030060 --- /dev/null +++ b/tests/baselines/reference/abstractIdentifierName.types @@ -0,0 +1,15 @@ +=== tests/cases/compiler/abstractIdentifierName.ts === +var abstract = true; +>abstract : boolean +>true : boolean + +function foo() { +>foo : () => void + + "use strict"; +>"use strict" : string + + var abstract = true; +>abstract : boolean +>true : boolean +} diff --git a/tests/baselines/reference/abstractInterfaceIdentifierName.js b/tests/baselines/reference/abstractInterfaceIdentifierName.js new file mode 100644 index 0000000000000..c3913ae5cdfaa --- /dev/null +++ b/tests/baselines/reference/abstractInterfaceIdentifierName.js @@ -0,0 +1,8 @@ +//// [abstractInterfaceIdentifierName.ts] + +interface abstract { + abstract(): void; +} + + +//// [abstractInterfaceIdentifierName.js] diff --git a/tests/baselines/reference/abstractInterfaceIdentifierName.symbols b/tests/baselines/reference/abstractInterfaceIdentifierName.symbols new file mode 100644 index 0000000000000..db2ad0aceba69 --- /dev/null +++ b/tests/baselines/reference/abstractInterfaceIdentifierName.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/abstractInterfaceIdentifierName.ts === + +interface abstract { +>abstract : Symbol(abstract, Decl(abstractInterfaceIdentifierName.ts, 0, 0)) + + abstract(): void; +>abstract : Symbol(abstract, Decl(abstractInterfaceIdentifierName.ts, 1, 20)) +} + diff --git a/tests/baselines/reference/abstractInterfaceIdentifierName.types b/tests/baselines/reference/abstractInterfaceIdentifierName.types new file mode 100644 index 0000000000000..3a5c6024f4ca7 --- /dev/null +++ b/tests/baselines/reference/abstractInterfaceIdentifierName.types @@ -0,0 +1,9 @@ +=== tests/cases/compiler/abstractInterfaceIdentifierName.ts === + +interface abstract { +>abstract : abstract + + abstract(): void; +>abstract : () => void +} + diff --git a/tests/cases/compiler/abstractClass1.ts b/tests/cases/compiler/abstractClass1.ts new file mode 100644 index 0000000000000..1590ee907f0d9 --- /dev/null +++ b/tests/cases/compiler/abstractClass1.ts @@ -0,0 +1,36 @@ +// @declaration: true + +abstract class Foo { + constructor(f: any) { } + public static bar(): void { } + + public empty() { } +} + +class Bar extends Foo { + constructor(f: any) { + super(f); + } +} + +var a = new Foo(1); // Error +var b = new Foo(); // Error because of invalid constructor arguments + +module baz { + export abstract class Qux { + } + export class Quz extends Qux { + } +} + +new baz.Qux(); + +// Valid +var c = new Bar(1); +c.empty(); + +// Calling a static method on a abstract class is valid +Foo.bar(); + +var Copy = Foo; +new Copy(); diff --git a/tests/cases/compiler/abstractClassIdentifierName.ts b/tests/cases/compiler/abstractClassIdentifierName.ts new file mode 100644 index 0000000000000..09d07f16fb895 --- /dev/null +++ b/tests/cases/compiler/abstractClassIdentifierName.ts @@ -0,0 +1,4 @@ +class abstract { + + abstract(): void { } +} diff --git a/tests/cases/compiler/abstractIdentifierName.ts b/tests/cases/compiler/abstractIdentifierName.ts new file mode 100644 index 0000000000000..c75fbf4304324 --- /dev/null +++ b/tests/cases/compiler/abstractIdentifierName.ts @@ -0,0 +1,6 @@ +var abstract = true; + +function foo() { + "use strict"; + var abstract = true; +} \ No newline at end of file diff --git a/tests/cases/compiler/abstractInterfaceIdentifierName.ts b/tests/cases/compiler/abstractInterfaceIdentifierName.ts new file mode 100644 index 0000000000000..c7bdc100ccfb6 --- /dev/null +++ b/tests/cases/compiler/abstractInterfaceIdentifierName.ts @@ -0,0 +1,4 @@ + +interface abstract { + abstract(): void; +}