From d1a2ae5e12e10afcd6e7be510533030e96651782 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 17 Jun 2015 15:36:53 -0700 Subject: [PATCH 01/15] Factor switch staement into a map and create a method to add members to it --- src/server/session.ts | 258 ++++++++++++++++++++---------------------- 1 file changed, 122 insertions(+), 136 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index c95b1a7cc1b..e0c540db18b 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -839,6 +839,127 @@ namespace ts.server { exit() { } + private handlers : Map<(request: protocol.Request) => {response?: any, responseRequired?: boolean}> = { + [CommandNames.Exit]: () => { + this.exit(); + return {}; + }, + [CommandNames.Definition]: (request: protocol.Request) => { + var defArgs = request.arguments; + return {response: this.getDefinition(defArgs.line, defArgs.offset, defArgs.file)}; + }, + [CommandNames.TypeDefinition]: (request: protocol.Request) => { + var defArgs = request.arguments; + return {response: this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file)}; + }, + [CommandNames.References]: (request: protocol.Request) => { + var defArgs = request.arguments; + return {response: this.getReferences(defArgs.line, defArgs.offset, defArgs.file)}; + }, + [CommandNames.Rename]: (request: protocol.Request) => { + var renameArgs = request.arguments; + return {response: this.getRenameLocations(renameArgs.line, renameArgs.offset, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings)} + }, + [CommandNames.Open]: (request: protocol.Request) => { + var openArgs = request.arguments; + this.openClientFile(openArgs.file); + return {} + }, + [CommandNames.Quickinfo]: (request: protocol.Request) => { + var quickinfoArgs = request.arguments; + return {response: this.getQuickInfo(quickinfoArgs.line, quickinfoArgs.offset, quickinfoArgs.file)}; + }, + [CommandNames.Format]: (request: protocol.Request) => { + var formatArgs = request.arguments; + return {response: this.getFormattingEditsForRange(formatArgs.line, formatArgs.offset, formatArgs.endLine, formatArgs.endOffset, formatArgs.file)}; + }, + [CommandNames.Formatonkey]: (request: protocol.Request) => { + var formatOnKeyArgs = request.arguments; + return {response: this.getFormattingEditsAfterKeystroke(formatOnKeyArgs.line, formatOnKeyArgs.offset, formatOnKeyArgs.key, formatOnKeyArgs.file)}; + }, + [CommandNames.Completions]: (request: protocol.Request) => { + var completionsArgs = request.arguments; + return {response: this.getCompletions(completionsArgs.line, completionsArgs.offset, completionsArgs.prefix, completionsArgs.file)} + }, + [CommandNames.CompletionDetails]: (request: protocol.Request) => { + var completionDetailsArgs = request.arguments; + return {response: this.getCompletionEntryDetails(completionDetailsArgs.line,completionDetailsArgs.offset, + completionDetailsArgs.entryNames,completionDetailsArgs.file)} + }, + [CommandNames.SignatureHelp]: (request: protocol.Request) => { + var signatureHelpArgs = request.arguments; + return {response: this.getSignatureHelpItems(signatureHelpArgs.line, signatureHelpArgs.offset, signatureHelpArgs.file)} + }, + [CommandNames.Geterr]: (request: protocol.Request) => { + var geterrArgs = request.arguments; + return {response: this.getDiagnostics(geterrArgs.delay, geterrArgs.files), responseRequired: false}; + }, + [CommandNames.Change]: (request: protocol.Request) => { + var changeArgs = request.arguments; + this.change(changeArgs.line, changeArgs.offset, changeArgs.endLine, changeArgs.endOffset, + changeArgs.insertString, changeArgs.file); + return {responseRequired: false} + }, + [CommandNames.Configure]: (request: protocol.Request) => { + var configureArgs = request.arguments; + this.projectService.setHostConfiguration(configureArgs); + this.output(undefined, CommandNames.Configure, request.seq); + return {responseRequired: false} + }, + [CommandNames.Reload]: (request: protocol.Request) => { + var reloadArgs = request.arguments; + this.reload(reloadArgs.file, reloadArgs.tmpfile, request.seq); + return {responseRequired: false} + }, + [CommandNames.Saveto]: (request: protocol.Request) => { + var savetoArgs = request.arguments; + this.saveToTmp(savetoArgs.file, savetoArgs.tmpfile); + return {responseRequired: false} + }, + [CommandNames.Close]: (request: protocol.Request) => { + var closeArgs = request.arguments; + this.closeClientFile(closeArgs.file); + return {responseRequired: false}; + }, + [CommandNames.Navto]: (request: protocol.Request) => { + var navtoArgs = request.arguments; + return {response: this.getNavigateToItems(navtoArgs.searchValue, navtoArgs.file, navtoArgs.maxResultCount)}; + }, + [CommandNames.Brace]: (request: protocol.Request) => { + var braceArguments = request.arguments; + return {response: this.getBraceMatching(braceArguments.line, braceArguments.offset, braceArguments.file)}; + }, + [CommandNames.NavBar]: (request: protocol.Request) => { + var navBarArgs = request.arguments; + return {response: this.getNavigationBarItems(navBarArgs.file)}; + }, + [CommandNames.Occurrences]: (request: protocol.Request) => { + var { line, offset, file: fileName } = request.arguments; + return {response: this.getOccurrences(line, offset, fileName)}; + }, + [CommandNames.ProjectInfo]: (request: protocol.Request) => { + var { file, needFileNameList } = request.arguments; + return {response: this.getProjectInfo(file, needFileNameList)}; + }, + }; + addProtocolHandler(command: string, handler: (request: protocol.Request) => {response?: any, responseRequired: boolean}) { + if (this.handlers[command]) { + throw new Error(`Protocol handler already exists for command "${command}"`); + } + this.handlers[command] = handler; + } + + executeCommand(request: protocol.Request) : {response?: any, responseRequired?: boolean} { + var handler = this.handlers[request.command]; + if (handler) { + return handler(request); + } else { + this.projectService.log("Unrecognized JSON command: " + JSON.stringify(request)); + this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command); + return {responseRequired: false}; + } + } + onMessage(message: string) { if (this.logger.isVerbose()) { this.logger.info("request: " + message); @@ -846,142 +967,7 @@ namespace ts.server { } try { var request = JSON.parse(message); - var response: any; - var errorMessage: string; - var responseRequired = true; - switch (request.command) { - case CommandNames.Exit: { - this.exit(); - responseRequired = false; - break; - } - case CommandNames.Definition: { - var defArgs = request.arguments; - response = this.getDefinition(defArgs.line, defArgs.offset, defArgs.file); - break; - } - case CommandNames.TypeDefinition: { - var defArgs = request.arguments; - response = this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file); - break; - } - case CommandNames.References: { - var refArgs = request.arguments; - response = this.getReferences(refArgs.line, refArgs.offset, refArgs.file); - break; - } - case CommandNames.Rename: { - var renameArgs = request.arguments; - response = this.getRenameLocations(renameArgs.line, renameArgs.offset, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings); - break; - } - case CommandNames.Open: { - var openArgs = request.arguments; - this.openClientFile(openArgs.file); - responseRequired = false; - break; - } - case CommandNames.Quickinfo: { - var quickinfoArgs = request.arguments; - response = this.getQuickInfo(quickinfoArgs.line, quickinfoArgs.offset, quickinfoArgs.file); - break; - } - case CommandNames.Format: { - var formatArgs = request.arguments; - response = this.getFormattingEditsForRange(formatArgs.line, formatArgs.offset, formatArgs.endLine, formatArgs.endOffset, formatArgs.file); - break; - } - case CommandNames.Formatonkey: { - var formatOnKeyArgs = request.arguments; - response = this.getFormattingEditsAfterKeystroke(formatOnKeyArgs.line, formatOnKeyArgs.offset, formatOnKeyArgs.key, formatOnKeyArgs.file); - break; - } - case CommandNames.Completions: { - var completionsArgs = request.arguments; - response = this.getCompletions(completionsArgs.line, completionsArgs.offset, completionsArgs.prefix, completionsArgs.file); - break; - } - case CommandNames.CompletionDetails: { - var completionDetailsArgs = request.arguments; - response = - this.getCompletionEntryDetails(completionDetailsArgs.line,completionDetailsArgs.offset, - completionDetailsArgs.entryNames,completionDetailsArgs.file); - break; - } - case CommandNames.SignatureHelp: { - var signatureHelpArgs = request.arguments; - response = this.getSignatureHelpItems(signatureHelpArgs.line, signatureHelpArgs.offset, signatureHelpArgs.file); - break; - } - case CommandNames.Geterr: { - var geterrArgs = request.arguments; - response = this.getDiagnostics(geterrArgs.delay, geterrArgs.files); - responseRequired = false; - break; - } - case CommandNames.Change: { - var changeArgs = request.arguments; - this.change(changeArgs.line, changeArgs.offset, changeArgs.endLine, changeArgs.endOffset, - changeArgs.insertString, changeArgs.file); - responseRequired = false; - break; - } - case CommandNames.Configure: { - var configureArgs = request.arguments; - this.projectService.setHostConfiguration(configureArgs); - this.output(undefined, CommandNames.Configure, request.seq); - responseRequired = false; - break; - } - case CommandNames.Reload: { - var reloadArgs = request.arguments; - this.reload(reloadArgs.file, reloadArgs.tmpfile, request.seq); - responseRequired = false; - break; - } - case CommandNames.Saveto: { - var savetoArgs = request.arguments; - this.saveToTmp(savetoArgs.file, savetoArgs.tmpfile); - responseRequired = false; - break; - } - case CommandNames.Close: { - var closeArgs = request.arguments; - this.closeClientFile(closeArgs.file); - responseRequired = false; - break; - } - case CommandNames.Navto: { - var navtoArgs = request.arguments; - response = this.getNavigateToItems(navtoArgs.searchValue, navtoArgs.file, navtoArgs.maxResultCount); - break; - } - case CommandNames.Brace: { - var braceArguments = request.arguments; - response = this.getBraceMatching(braceArguments.line, braceArguments.offset, braceArguments.file); - break; - } - case CommandNames.NavBar: { - var navBarArgs = request.arguments; - response = this.getNavigationBarItems(navBarArgs.file); - break; - } - case CommandNames.Occurrences: { - var { line, offset, file: fileName } = request.arguments; - response = this.getOccurrences(line, offset, fileName); - break; - } - case CommandNames.ProjectInfo: { - var { file, needFileNameList } = request.arguments; - response = this.getProjectInfo(file, needFileNameList); - break; - } - default: { - this.projectService.log("Unrecognized JSON command: " + message); - this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command); - break; - } - } + var {response, responseRequired} = this.executeCommand(request); if (this.logger.isVerbose()) { var elapsed = this.hrtime(start); From ddb60fc8ce8c3d37c7acaf588cfb8948b30c2a3a Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Tue, 30 Jun 2015 10:49:15 +0200 Subject: [PATCH 02/15] Node System host write - need to loop since writeSync might not write all bytes --- src/compiler/sys.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index a47d6959ba8..d5e1c77d22f 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -29,6 +29,9 @@ namespace ts { declare var process: any; declare var global: any; declare var __filename: string; + declare var Buffer: { + new (str: string, encoding ?: string): any; + } declare class Enumerator { public atEnd(): boolean; @@ -267,10 +270,17 @@ namespace ts { args: process.argv.slice(2), newLine: _os.EOL, useCaseSensitiveFileNames: useCaseSensitiveFileNames, - write(s: string): void { + write(s: string): void { + var buffer = new Buffer(s, 'utf8'); + var offset: number = 0; + var toWrite: number = buffer.length; + var written = 0; // 1 is a standard descriptor for stdout - _fs.writeSync(1, s); - }, + while ((written = _fs.writeSync(1, buffer, offset, toWrite)) < toWrite) { + offset += written; + toWrite -= written; + } + }, readFile, writeFile, watchFile: (fileName, callback) => { From e2b858dbf826a1187d5b56670633fd4aa4711e5a Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 2 Jul 2015 14:53:03 -0700 Subject: [PATCH 03/15] Check abstractness via constructor return types --- src/compiler/checker.ts | 19 +++++++++++++++---- .../diagnosticInformationMap.generated.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d3d8ac1bc1b..e1826901399 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4872,10 +4872,21 @@ namespace ts { return Ternary.False; } - let t = getReturnTypeOfSignature(target); - if (t === voidType) return result; - let s = getReturnTypeOfSignature(source); - return result & isRelatedTo(s, t, reportErrors); + let targetReturnType = getReturnTypeOfSignature(target); + if (targetReturnType === voidType) return result; + let sourceReturnType = getReturnTypeOfSignature(source); + + let targetReturnDecl = targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); + let sourceReturnDecl = sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); + + if(sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract && (!targetReturnDecl || !(targetReturnDecl.flags & NodeFlags.Abstract))) { + if(reportErrors) { + reportError(Diagnostics.Constructor_objects_of_abstract_type_cannot_be_assigned_to_constructor_objects_of_non_abstract_type); + } + return Ternary.False; + } + + return result & isRelatedTo(sourceReturnType, targetReturnType, reportErrors); } function signaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind): Ternary { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index a17f8857590..fbac2f44fb5 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -407,7 +407,7 @@ namespace ts { Classes_containing_abstract_methods_must_be_marked_abstract: { code: 2514, category: DiagnosticCategory.Error, key: "Classes containing abstract methods must be marked abstract." }, Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2: { code: 2515, category: DiagnosticCategory.Error, key: "Non-abstract class '{0}' does not implement inherited abstract member '{1}' from class '{2}'." }, All_declarations_of_an_abstract_method_must_be_consecutive: { code: 2516, category: DiagnosticCategory.Error, key: "All declarations of an abstract method must be consecutive." }, - Constructor_objects_of_abstract_type_cannot_be_assigned_to_constructor_objects_of_non_abstract_type: { code: 2517, category: DiagnosticCategory.Error, key: "Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type" }, + Constructor_objects_of_abstract_type_cannot_be_assigned_to_constructor_objects_of_non_abstract_type: { code: 2517, category: DiagnosticCategory.Error, key: "Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type." }, Only_an_ambient_class_can_be_merged_with_an_interface: { code: 2518, category: DiagnosticCategory.Error, key: "Only an ambient class can be merged with an interface." }, Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions: { code: 2520, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions." }, Expression_resolves_to_variable_declaration_0_that_compiler_uses_to_support_async_functions: { code: 2521, category: DiagnosticCategory.Error, key: "Expression resolves to variable declaration '{0}' that compiler uses to support async functions." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 94be4f53c97..4c9fbe2d49c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1617,7 +1617,7 @@ "category": "Error", "code": 2516 }, - "Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type": { + "Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type.": { "category": "Error", "code":2517 }, From 0a79c9cd6649432a12cfe8ed2a2b5c06e6b8fa68 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 2 Jul 2015 15:11:25 -0700 Subject: [PATCH 04/15] updated baselines --- .../reference/classAbstractInstantiations2.errors.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/classAbstractInstantiations2.errors.txt b/tests/baselines/reference/classAbstractInstantiations2.errors.txt index b3b01fd656d..43dfb9e009f 100644 --- a/tests/baselines/reference/classAbstractInstantiations2.errors.txt +++ b/tests/baselines/reference/classAbstractInstantiations2.errors.txt @@ -1,4 +1,6 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(10,1): error TS2511: Cannot create an instance of the abstract class 'B'. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(13,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. + Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(17,5): error TS2511: Cannot create an instance of the abstract class 'B'. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(21,1): error TS2511: Cannot create an instance of the abstract class 'B'. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(26,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'. @@ -7,7 +9,7 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbst tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(50,5): error TS1244: Abstract methods can only appear within an abstract class. -==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts (7 errors) ==== +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts (8 errors) ==== class A { // ... } @@ -23,6 +25,9 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbst var BB: typeof B = B; var AA: typeof A = BB; // error, AA is not of abstract type. + ~~ +!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. +!!! error TS2322: Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. new AA; function constructB(Factory : typeof B) { From a4c801d49cb39d4d6f0a349f2dd8a3b6fb069c1c Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 6 Jul 2015 10:31:01 -0700 Subject: [PATCH 05/15] New Test --- .../classAbstractClinterfaceAssignability.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts diff --git a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts new file mode 100644 index 00000000000..682ad6faa0f --- /dev/null +++ b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts @@ -0,0 +1,23 @@ +interface I { + x: number; +} + +interface IConstructor { + new (): I; + + y: number; + prototype: I; +} + +var I: IConstructor; + +abstract class A { + x: number; + static y: number; +} + +var AA: typeof A; +AA = I; + +var AAA: typeof I; +AAA = A; \ No newline at end of file From 7494134041fe7446c10e2765e5ea06ba99d26e4d Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 6 Jul 2015 10:31:15 -0700 Subject: [PATCH 06/15] updated baselines --- ...bstractClinterfaceAssignability.errors.txt | 31 ++++++++++++++++ .../classAbstractClinterfaceAssignability.js | 36 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt create mode 100644 tests/baselines/reference/classAbstractClinterfaceAssignability.js diff --git a/tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt b/tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt new file mode 100644 index 00000000000..17d08981d3e --- /dev/null +++ b/tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt @@ -0,0 +1,31 @@ +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts(23,1): error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'. + Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. + + +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts (1 errors) ==== + interface I { + x: number; + } + + interface IConstructor { + new (): I; + + y: number; + prototype: I; + } + + var I: IConstructor; + + abstract class A { + x: number; + static y: number; + } + + var AA: typeof A; + AA = I; + + var AAA: typeof I; + AAA = A; + ~~~ +!!! error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'. +!!! error TS2322: Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractClinterfaceAssignability.js b/tests/baselines/reference/classAbstractClinterfaceAssignability.js new file mode 100644 index 00000000000..cbcf9dd189c --- /dev/null +++ b/tests/baselines/reference/classAbstractClinterfaceAssignability.js @@ -0,0 +1,36 @@ +//// [classAbstractClinterfaceAssignability.ts] +interface I { + x: number; +} + +interface IConstructor { + new (): I; + + y: number; + prototype: I; +} + +var I: IConstructor; + +abstract class A { + x: number; + static y: number; +} + +var AA: typeof A; +AA = I; + +var AAA: typeof I; +AAA = A; + +//// [classAbstractClinterfaceAssignability.js] +var I; +var A = (function () { + function A() { + } + return A; +})(); +var AA; +AA = I; +var AAA; +AAA = A; From 4627f9c69ba79c7ace9dd0c706ed865df4f997dd Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 11:42:22 -0700 Subject: [PATCH 07/15] Updated Error Message --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticInformationMap.generated.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e1826901399..feb76446e1a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4881,7 +4881,7 @@ namespace ts { if(sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract && (!targetReturnDecl || !(targetReturnDecl.flags & NodeFlags.Abstract))) { if(reportErrors) { - reportError(Diagnostics.Constructor_objects_of_abstract_type_cannot_be_assigned_to_constructor_objects_of_non_abstract_type); + reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); } return Ternary.False; } diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index fbac2f44fb5..a0a95d47c8c 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -407,7 +407,7 @@ namespace ts { Classes_containing_abstract_methods_must_be_marked_abstract: { code: 2514, category: DiagnosticCategory.Error, key: "Classes containing abstract methods must be marked abstract." }, Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2: { code: 2515, category: DiagnosticCategory.Error, key: "Non-abstract class '{0}' does not implement inherited abstract member '{1}' from class '{2}'." }, All_declarations_of_an_abstract_method_must_be_consecutive: { code: 2516, category: DiagnosticCategory.Error, key: "All declarations of an abstract method must be consecutive." }, - Constructor_objects_of_abstract_type_cannot_be_assigned_to_constructor_objects_of_non_abstract_type: { code: 2517, category: DiagnosticCategory.Error, key: "Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type." }, + Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type: { code: 2517, category: DiagnosticCategory.Error, key: "Cannot assign an abstract constructor type to a non-abstract constructor type." }, Only_an_ambient_class_can_be_merged_with_an_interface: { code: 2518, category: DiagnosticCategory.Error, key: "Only an ambient class can be merged with an interface." }, Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions: { code: 2520, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions." }, Expression_resolves_to_variable_declaration_0_that_compiler_uses_to_support_async_functions: { code: 2521, category: DiagnosticCategory.Error, key: "Expression resolves to variable declaration '{0}' that compiler uses to support async functions." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 4c9fbe2d49c..acf4e74ae9f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1617,7 +1617,7 @@ "category": "Error", "code": 2516 }, - "Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type.": { + "Cannot assign an abstract constructor type to a non-abstract constructor type.": { "category": "Error", "code":2517 }, From 84326e6ef62541a0750465c859fa6339fa303171 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 11:47:56 -0700 Subject: [PATCH 08/15] Updated Baselines --- .../classAbstractClinterfaceAssignability.errors.txt | 4 ++-- .../reference/classAbstractInstantiations2.errors.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt b/tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt index 17d08981d3e..d9462f28a22 100644 --- a/tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt +++ b/tests/baselines/reference/classAbstractClinterfaceAssignability.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts(23,1): error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'. - Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. + Cannot assign an abstract constructor type to a non-abstract constructor type. ==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts (1 errors) ==== @@ -28,4 +28,4 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbst AAA = A; ~~~ !!! error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'. -!!! error TS2322: Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. \ No newline at end of file +!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type. \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractInstantiations2.errors.txt b/tests/baselines/reference/classAbstractInstantiations2.errors.txt index 43dfb9e009f..077f87c5050 100644 --- a/tests/baselines/reference/classAbstractInstantiations2.errors.txt +++ b/tests/baselines/reference/classAbstractInstantiations2.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(10,1): error TS2511: Cannot create an instance of the abstract class 'B'. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(13,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. - Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. + Cannot assign an abstract constructor type to a non-abstract constructor type. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(17,5): error TS2511: Cannot create an instance of the abstract class 'B'. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(21,1): error TS2511: Cannot create an instance of the abstract class 'B'. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(26,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'. @@ -27,7 +27,7 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbst var AA: typeof A = BB; // error, AA is not of abstract type. ~~ !!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. -!!! error TS2322: Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type. +!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type. new AA; function constructB(Factory : typeof B) { From dc854158ab50efe69609b2f24c119219872ca71b Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 11:48:09 -0700 Subject: [PATCH 09/15] Made check more readable --- src/compiler/checker.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index feb76446e1a..58f858e4d6d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4875,17 +4875,19 @@ namespace ts { let targetReturnType = getReturnTypeOfSignature(target); if (targetReturnType === voidType) return result; let sourceReturnType = getReturnTypeOfSignature(source); - - let targetReturnDecl = targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); + let sourceReturnDecl = sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); - - if(sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract && (!targetReturnDecl || !(targetReturnDecl.flags & NodeFlags.Abstract))) { - if(reportErrors) { + let targetReturnDecl = targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); + let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; + let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; + + if (sourceIsAbstract && !targetIsAbstract) { + if (reportErrors) { reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); } return Ternary.False; } - + return result & isRelatedTo(sourceReturnType, targetReturnType, reportErrors); } From 4842501ecb6dddea0c84ed7101da28f3768524fc Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 13:44:59 -0700 Subject: [PATCH 10/15] Move assignability test outside inner loop --- src/compiler/checker.ts | 42 ++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 58f858e4d6d..dd944b51fe5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4766,9 +4766,37 @@ namespace ts { outer: for (let t of targetSignatures) { if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) { let localErrors = reportErrors; + let checkedAbstractAssignability = false; for (let s of sourceSignatures) { if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) { - let related = signatureRelatedTo(s, t, localErrors); + let related = Ternary.True; + + // Because the "abstractness" of a class is the same across all construct signatures + // (internally we are checking the corresponding declaration), it is enough to perform + // the check and report an error once over all pairs of source and target construct signatures. + if (!checkedAbstractAssignability) { + checkedAbstractAssignability = true; + + let sourceErasedSignature = getErasedSignature(s); + let targetErasedSignature = getErasedSignature(t); + + let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature); + let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature); + + let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); + let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); + let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; + let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; + + if (sourceIsAbstract && !targetIsAbstract) { + if (reportErrors) { + reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); + } + related = Ternary.False; + } + } + + related &= signatureRelatedTo(s, t, localErrors); if (related) { result &= related; errorInfo = saveErrorInfo; @@ -4876,18 +4904,6 @@ namespace ts { if (targetReturnType === voidType) return result; let sourceReturnType = getReturnTypeOfSignature(source); - let sourceReturnDecl = sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); - let targetReturnDecl = targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); - let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; - let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; - - if (sourceIsAbstract && !targetIsAbstract) { - if (reportErrors) { - reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); - } - return Ternary.False; - } - return result & isRelatedTo(sourceReturnType, targetReturnType, reportErrors); } From e6cf92066452e63eb1df2abdffea9daae1276b02 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 13:47:50 -0700 Subject: [PATCH 11/15] separated tests for readability --- .../classAbstractConstructorAssignability.ts | 14 +++++++++++ .../classAbstractExtends.ts | 16 +++++++++++++ .../classAbstractFactoryFunction.ts | 17 ++++++++++++++ .../classAbstractInstantiations1.ts | 4 ++++ .../classAbstractMethodInNonAbstractClass.ts | 7 ++++++ .../classAbstractOverrideWithAbstract.ts | 23 +++++++++++++++++++ 6 files changed, 81 insertions(+) create mode 100644 tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts create mode 100644 tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts create mode 100644 tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts create mode 100644 tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts create mode 100644 tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts diff --git a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts new file mode 100644 index 00000000000..ecef21cc12a --- /dev/null +++ b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts @@ -0,0 +1,14 @@ + +class A {} + +abstract class B extends A {} + +class C extends B {} + +var AA : typeof A = B; +var BB : typeof B = A; +var CC : typeof C = B; + +new AA; +new BB; +new CC; \ No newline at end of file diff --git a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts new file mode 100644 index 00000000000..0aa57b75835 --- /dev/null +++ b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts @@ -0,0 +1,16 @@ + +class A { + foo() {} +} + +abstract class B extends A { + abstract bar(); +} + +class C extends B { } + +abstract class D extends B {} + +class E extends B { + bar() {} +} \ No newline at end of file diff --git a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts new file mode 100644 index 00000000000..73d33a34e12 --- /dev/null +++ b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts @@ -0,0 +1,17 @@ + +class A {} +abstract class B extends A {} + +function NewA(Factory: typeof A) { + return new A; +} + +function NewB(Factory: typeof B) { + return new B; +} + +NewA(A); +NewA(B); + +NewB(A); +NewB(B); \ No newline at end of file diff --git a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts index 4daf27b53e4..c805a993a68 100644 --- a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts +++ b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts @@ -1,4 +1,8 @@ +// +// Calling new with (non)abstract classes. +// + abstract class A {} class B extends A {} diff --git a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts new file mode 100644 index 00000000000..98a2091bc0c --- /dev/null +++ b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts @@ -0,0 +1,7 @@ +class A { + abstract foo(); +} + +class B { + abstract foo() {} +} \ No newline at end of file diff --git a/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts new file mode 100644 index 00000000000..7e02dbd4d98 --- /dev/null +++ b/tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts @@ -0,0 +1,23 @@ +class A { + foo() {} +} + +abstract class B extends A { + abstract foo(); +} + +abstract class AA { + foo() {} + abstract bar(); +} + +abstract class BB extends AA { + abstract foo(); + bar () {} +} + +class CC extends BB {} // error + +class DD extends BB { + foo() {} +} \ No newline at end of file From 35d2592a510adc8781d3db816cbd71750fb8085e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 13:51:16 -0700 Subject: [PATCH 12/15] Updated Baselines --- ...bstractConstructorAssignability.errors.txt | 30 ++++++++ .../classAbstractConstructorAssignability.js | 47 ++++++++++++ .../reference/classAbstractExtends.errors.txt | 22 ++++++ .../reference/classAbstractExtends.js | 59 +++++++++++++++ .../classAbstractFactoryFunction.errors.txt | 28 +++++++ .../reference/classAbstractFactoryFunction.js | 47 ++++++++++++ .../classAbstractInstantiations1.errors.txt | 10 ++- .../reference/classAbstractInstantiations1.js | 7 ++ ...bstractMethodInNonAbstractClass.errors.txt | 19 +++++ .../classAbstractMethodInNonAbstractClass.js | 21 ++++++ ...assAbstractOverrideWithAbstract.errors.txt | 29 ++++++++ .../classAbstractOverrideWithAbstract.js | 73 +++++++++++++++++++ 12 files changed, 389 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/classAbstractConstructorAssignability.errors.txt create mode 100644 tests/baselines/reference/classAbstractConstructorAssignability.js create mode 100644 tests/baselines/reference/classAbstractExtends.errors.txt create mode 100644 tests/baselines/reference/classAbstractExtends.js create mode 100644 tests/baselines/reference/classAbstractFactoryFunction.errors.txt create mode 100644 tests/baselines/reference/classAbstractFactoryFunction.js create mode 100644 tests/baselines/reference/classAbstractMethodInNonAbstractClass.errors.txt create mode 100644 tests/baselines/reference/classAbstractMethodInNonAbstractClass.js create mode 100644 tests/baselines/reference/classAbstractOverrideWithAbstract.errors.txt create mode 100644 tests/baselines/reference/classAbstractOverrideWithAbstract.js diff --git a/tests/baselines/reference/classAbstractConstructorAssignability.errors.txt b/tests/baselines/reference/classAbstractConstructorAssignability.errors.txt new file mode 100644 index 00000000000..a3e9e077914 --- /dev/null +++ b/tests/baselines/reference/classAbstractConstructorAssignability.errors.txt @@ -0,0 +1,30 @@ +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(8,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. + Cannot assign an abstract constructor type to a non-abstract constructor type. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(10,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof C'. + Cannot assign an abstract constructor type to a non-abstract constructor type. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(13,1): error TS2511: Cannot create an instance of the abstract class 'B'. + + +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts (3 errors) ==== + + class A {} + + abstract class B extends A {} + + class C extends B {} + + var AA : typeof A = B; + ~~ +!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. +!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type. + var BB : typeof B = A; + var CC : typeof C = B; + ~~ +!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof C'. +!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type. + + new AA; + new BB; + ~~~~~~ +!!! error TS2511: Cannot create an instance of the abstract class 'B'. + new CC; \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractConstructorAssignability.js b/tests/baselines/reference/classAbstractConstructorAssignability.js new file mode 100644 index 00000000000..04357a44554 --- /dev/null +++ b/tests/baselines/reference/classAbstractConstructorAssignability.js @@ -0,0 +1,47 @@ +//// [classAbstractConstructorAssignability.ts] + +class A {} + +abstract class B extends A {} + +class C extends B {} + +var AA : typeof A = B; +var BB : typeof B = A; +var CC : typeof C = B; + +new AA; +new BB; +new CC; + +//// [classAbstractConstructorAssignability.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var A = (function () { + function A() { + } + return A; +})(); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +})(A); +var C = (function (_super) { + __extends(C, _super); + function C() { + _super.apply(this, arguments); + } + return C; +})(B); +var AA = B; +var BB = A; +var CC = B; +new AA; +new BB; +new CC; diff --git a/tests/baselines/reference/classAbstractExtends.errors.txt b/tests/baselines/reference/classAbstractExtends.errors.txt new file mode 100644 index 00000000000..550067cd0e1 --- /dev/null +++ b/tests/baselines/reference/classAbstractExtends.errors.txt @@ -0,0 +1,22 @@ +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts(10,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'. + + +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts (1 errors) ==== + + class A { + foo() {} + } + + abstract class B extends A { + abstract bar(); + } + + class C extends B { } + ~ +!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'. + + abstract class D extends B {} + + class E extends B { + bar() {} + } \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractExtends.js b/tests/baselines/reference/classAbstractExtends.js new file mode 100644 index 00000000000..58e49de2c77 --- /dev/null +++ b/tests/baselines/reference/classAbstractExtends.js @@ -0,0 +1,59 @@ +//// [classAbstractExtends.ts] + +class A { + foo() {} +} + +abstract class B extends A { + abstract bar(); +} + +class C extends B { } + +abstract class D extends B {} + +class E extends B { + bar() {} +} + +//// [classAbstractExtends.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var A = (function () { + function A() { + } + A.prototype.foo = function () { }; + return A; +})(); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +})(A); +var C = (function (_super) { + __extends(C, _super); + function C() { + _super.apply(this, arguments); + } + return C; +})(B); +var D = (function (_super) { + __extends(D, _super); + function D() { + _super.apply(this, arguments); + } + return D; +})(B); +var E = (function (_super) { + __extends(E, _super); + function E() { + _super.apply(this, arguments); + } + E.prototype.bar = function () { }; + return E; +})(B); diff --git a/tests/baselines/reference/classAbstractFactoryFunction.errors.txt b/tests/baselines/reference/classAbstractFactoryFunction.errors.txt new file mode 100644 index 00000000000..f1b43adae59 --- /dev/null +++ b/tests/baselines/reference/classAbstractFactoryFunction.errors.txt @@ -0,0 +1,28 @@ +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts(10,12): error TS2511: Cannot create an instance of the abstract class 'B'. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts(14,6): error TS2345: Argument of type 'typeof B' is not assignable to parameter of type 'typeof A'. + Cannot assign an abstract constructor type to a non-abstract constructor type. + + +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts (2 errors) ==== + + class A {} + abstract class B extends A {} + + function NewA(Factory: typeof A) { + return new A; + } + + function NewB(Factory: typeof B) { + return new B; + ~~~~~ +!!! error TS2511: Cannot create an instance of the abstract class 'B'. + } + + NewA(A); + NewA(B); + ~ +!!! error TS2345: Argument of type 'typeof B' is not assignable to parameter of type 'typeof A'. +!!! error TS2345: Cannot assign an abstract constructor type to a non-abstract constructor type. + + NewB(A); + NewB(B); \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractFactoryFunction.js b/tests/baselines/reference/classAbstractFactoryFunction.js new file mode 100644 index 00000000000..b1d89ba385d --- /dev/null +++ b/tests/baselines/reference/classAbstractFactoryFunction.js @@ -0,0 +1,47 @@ +//// [classAbstractFactoryFunction.ts] + +class A {} +abstract class B extends A {} + +function NewA(Factory: typeof A) { + return new A; +} + +function NewB(Factory: typeof B) { + return new B; +} + +NewA(A); +NewA(B); + +NewB(A); +NewB(B); + +//// [classAbstractFactoryFunction.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var A = (function () { + function A() { + } + return A; +})(); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +})(A); +function NewA(Factory) { + return new A; +} +function NewB(Factory) { + return new B; +} +NewA(A); +NewA(B); +NewB(A); +NewB(B); diff --git a/tests/baselines/reference/classAbstractInstantiations1.errors.txt b/tests/baselines/reference/classAbstractInstantiations1.errors.txt index 6171ef2efab..24a969fc6b8 100644 --- a/tests/baselines/reference/classAbstractInstantiations1.errors.txt +++ b/tests/baselines/reference/classAbstractInstantiations1.errors.txt @@ -1,10 +1,14 @@ -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(8,1): error TS2511: Cannot create an instance of the abstract class 'A'. -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(9,1): error TS2511: Cannot create an instance of the abstract class 'A'. -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(11,1): error TS2511: Cannot create an instance of the abstract class 'C'. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(12,1): error TS2511: Cannot create an instance of the abstract class 'A'. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(13,1): error TS2511: Cannot create an instance of the abstract class 'A'. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(15,1): error TS2511: Cannot create an instance of the abstract class 'C'. ==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts (3 errors) ==== + // + // Calling new with (non)abstract classes. + // + abstract class A {} class B extends A {} diff --git a/tests/baselines/reference/classAbstractInstantiations1.js b/tests/baselines/reference/classAbstractInstantiations1.js index f3a0eecada9..689a78f1a06 100644 --- a/tests/baselines/reference/classAbstractInstantiations1.js +++ b/tests/baselines/reference/classAbstractInstantiations1.js @@ -1,5 +1,9 @@ //// [classAbstractInstantiations1.ts] +// +// Calling new with (non)abstract classes. +// + abstract class A {} class B extends A {} @@ -21,6 +25,9 @@ c = new B; //// [classAbstractInstantiations1.js] +// +// Calling new with (non)abstract classes. +// var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } diff --git a/tests/baselines/reference/classAbstractMethodInNonAbstractClass.errors.txt b/tests/baselines/reference/classAbstractMethodInNonAbstractClass.errors.txt new file mode 100644 index 00000000000..d6f9b7540d4 --- /dev/null +++ b/tests/baselines/reference/classAbstractMethodInNonAbstractClass.errors.txt @@ -0,0 +1,19 @@ +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts(2,5): error TS1244: Abstract methods can only appear within an abstract class. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts(6,5): error TS1244: Abstract methods can only appear within an abstract class. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts(6,5): error TS1245: Method 'foo' cannot have an implementation because it is marked abstract. + + +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts (3 errors) ==== + class A { + abstract foo(); + ~~~~~~~~ +!!! error TS1244: Abstract methods can only appear within an abstract class. + } + + class B { + abstract foo() {} + ~~~~~~~~ +!!! error TS1244: Abstract methods can only appear within an abstract class. + ~~~~~~~~~~~~~~~~~ +!!! error TS1245: Method 'foo' cannot have an implementation because it is marked abstract. + } \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractMethodInNonAbstractClass.js b/tests/baselines/reference/classAbstractMethodInNonAbstractClass.js new file mode 100644 index 00000000000..6a38440cf41 --- /dev/null +++ b/tests/baselines/reference/classAbstractMethodInNonAbstractClass.js @@ -0,0 +1,21 @@ +//// [classAbstractMethodInNonAbstractClass.ts] +class A { + abstract foo(); +} + +class B { + abstract foo() {} +} + +//// [classAbstractMethodInNonAbstractClass.js] +var A = (function () { + function A() { + } + return A; +})(); +var B = (function () { + function B() { + } + B.prototype.foo = function () { }; + return B; +})(); diff --git a/tests/baselines/reference/classAbstractOverrideWithAbstract.errors.txt b/tests/baselines/reference/classAbstractOverrideWithAbstract.errors.txt new file mode 100644 index 00000000000..d9d073a07e0 --- /dev/null +++ b/tests/baselines/reference/classAbstractOverrideWithAbstract.errors.txt @@ -0,0 +1,29 @@ +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts(19,7): error TS2515: Non-abstract class 'CC' does not implement inherited abstract member 'foo' from class 'BB'. + + +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts (1 errors) ==== + class A { + foo() {} + } + + abstract class B extends A { + abstract foo(); + } + + abstract class AA { + foo() {} + abstract bar(); + } + + abstract class BB extends AA { + abstract foo(); + bar () {} + } + + class CC extends BB {} // error + ~~ +!!! error TS2515: Non-abstract class 'CC' does not implement inherited abstract member 'foo' from class 'BB'. + + class DD extends BB { + foo() {} + } \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractOverrideWithAbstract.js b/tests/baselines/reference/classAbstractOverrideWithAbstract.js new file mode 100644 index 00000000000..fb03192f35e --- /dev/null +++ b/tests/baselines/reference/classAbstractOverrideWithAbstract.js @@ -0,0 +1,73 @@ +//// [classAbstractOverrideWithAbstract.ts] +class A { + foo() {} +} + +abstract class B extends A { + abstract foo(); +} + +abstract class AA { + foo() {} + abstract bar(); +} + +abstract class BB extends AA { + abstract foo(); + bar () {} +} + +class CC extends BB {} // error + +class DD extends BB { + foo() {} +} + +//// [classAbstractOverrideWithAbstract.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var A = (function () { + function A() { + } + A.prototype.foo = function () { }; + return A; +})(); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +})(A); +var AA = (function () { + function AA() { + } + AA.prototype.foo = function () { }; + return AA; +})(); +var BB = (function (_super) { + __extends(BB, _super); + function BB() { + _super.apply(this, arguments); + } + BB.prototype.bar = function () { }; + return BB; +})(AA); +var CC = (function (_super) { + __extends(CC, _super); + function CC() { + _super.apply(this, arguments); + } + return CC; +})(BB); // error +var DD = (function (_super) { + __extends(DD, _super); + function DD() { + _super.apply(this, arguments); + } + DD.prototype.foo = function () { }; + return DD; +})(BB); From 99d640f0f0bafd029ff09d56a19766babd7d57f1 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 17:53:55 -0700 Subject: [PATCH 13/15] Actually move check outside the loop --- src/compiler/checker.ts | 51 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd944b51fe5..cdaf268fdf3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4763,40 +4763,35 @@ namespace ts { let targetSignatures = getSignaturesOfType(target, kind); let result = Ternary.True; let saveErrorInfo = errorInfo; + + // Because the "abstractness" of a class is the same across all construct signatures + // (internally we are checking the corresponding declaration), it is enough to perform + // the check and report an error once over all pairs of source and target construct signatures. + let sourceErasedSignature = getErasedSignature(s); + let targetErasedSignature = getErasedSignature(t); + + let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature); + let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature); + + let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); + let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); + let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; + let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; + + if (sourceIsAbstract && !targetIsAbstract) { + if (reportErrors) { + reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); + } + result = Ternary.False; + } + outer: for (let t of targetSignatures) { if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) { let localErrors = reportErrors; let checkedAbstractAssignability = false; for (let s of sourceSignatures) { if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) { - let related = Ternary.True; - - // Because the "abstractness" of a class is the same across all construct signatures - // (internally we are checking the corresponding declaration), it is enough to perform - // the check and report an error once over all pairs of source and target construct signatures. - if (!checkedAbstractAssignability) { - checkedAbstractAssignability = true; - - let sourceErasedSignature = getErasedSignature(s); - let targetErasedSignature = getErasedSignature(t); - - let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature); - let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature); - - let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); - let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); - let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; - let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; - - if (sourceIsAbstract && !targetIsAbstract) { - if (reportErrors) { - reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); - } - related = Ternary.False; - } - } - - related &= signatureRelatedTo(s, t, localErrors); + let related = signatureRelatedTo(s, t, localErrors); if (related) { result &= related; errorInfo = saveErrorInfo; From 50f254b2d26fc24761d58617d65638ce60f1bf1f Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 10 Jul 2015 18:38:04 -0700 Subject: [PATCH 14/15] Fixed Testing --- src/compiler/checker.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cdaf268fdf3..2bef37ad52c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4763,26 +4763,32 @@ namespace ts { let targetSignatures = getSignaturesOfType(target, kind); let result = Ternary.True; let saveErrorInfo = errorInfo; - + // Because the "abstractness" of a class is the same across all construct signatures // (internally we are checking the corresponding declaration), it is enough to perform // the check and report an error once over all pairs of source and target construct signatures. - let sourceErasedSignature = getErasedSignature(s); - let targetErasedSignature = getErasedSignature(t); + let sourceSig = sourceSignatures[0]; + // Note that in an extends-clause, targetSignatures is stripped, so the check never proceeds. + let targetSig = targetSignatures[0]; - let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature); - let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature); + if (sourceSig && targetSig) { + let sourceErasedSignature = getErasedSignature(sourceSig); + let targetErasedSignature = getErasedSignature(targetSig); - let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); - let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); - let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; - let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; + let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature); + let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature); - if (sourceIsAbstract && !targetIsAbstract) { - if (reportErrors) { - reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); + let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); + let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); + let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; + let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; + + if (sourceIsAbstract && !targetIsAbstract) { + if (reportErrors) { + reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); + } + return Ternary.False; } - result = Ternary.False; } outer: for (let t of targetSignatures) { From 6dcc9fcf655e30b65cd4a88974d766926b0fda4c Mon Sep 17 00:00:00 2001 From: Dick van den Brink Date: Mon, 13 Jul 2015 22:13:11 +0200 Subject: [PATCH 15/15] Don't fall through to the modifier check for get/set keywords --- src/services/services.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/services.ts b/src/services/services.ts index 10e38f98613..c85c10dc411 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4546,6 +4546,7 @@ namespace ts { if (hasKind(node.parent, SyntaxKind.GetAccessor) || hasKind(node.parent, SyntaxKind.SetAccessor)) { return getGetAndSetOccurrences(node.parent); } + break; default: if (isModifier(node.kind) && node.parent && (isDeclaration(node.parent) || node.parent.kind === SyntaxKind.VariableStatement)) {