From 94ea825f95db397756129575ea9f7cd4df347f7c Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 5 Aug 2016 16:20:16 -0700 Subject: [PATCH 01/92] Api Changes and simple superfixes --- src/services/codefixes/codeFixProvider.ts | 53 ++++++++++++++++++ src/services/codefixes/references.ts | 6 +++ src/services/codefixes/superFixes.ts | 65 +++++++++++++++++++++++ src/services/services.ts | 49 ++++++++++++++++- src/services/shims.ts | 19 +++++++ src/services/tsconfig.json | 5 +- 6 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 src/services/codefixes/codeFixProvider.ts create mode 100644 src/services/codefixes/references.ts create mode 100644 src/services/codefixes/superFixes.ts diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts new file mode 100644 index 00000000000..ca877aef46a --- /dev/null +++ b/src/services/codefixes/codeFixProvider.ts @@ -0,0 +1,53 @@ +/* @internal */ +namespace ts { + export interface CodeFix { + name: string; + errorCodes: string[]; + getCodeActions(context: CodeFixContext): CodeAction[]; + } + + export interface CodeFixContext { + errorCode: string; + sourceFile: SourceFile; + span: TextSpan; + checker: TypeChecker; + newLineCharacter: string; + } + + export namespace codeFix { + const codeFixes: Map = {}; + + export function registerCodeFix(action: CodeFix) { + forEach(action.errorCodes, error => { + let fixes = codeFixes[error]; + if (!fixes) { + fixes = []; + codeFixes[error] = fixes; + } + fixes.push(action); + }); + } + + export class CodeFixProvider { + public static getSupportedErrorCodes() { + return getKeys(codeFixes); + } + + public getFixes(context: CodeFixContext): CodeAction[] { + const fixes = codeFixes[context.errorCode]; + let allActions: CodeAction[] = []; + + Debug.assert(fixes && fixes.length > 0, "No fixes found for error: '${errorCode}'."); + + forEach(fixes, f => { + const actions = f.getCodeActions(context); + if (actions && actions.length > 0) { + allActions = allActions.concat(actions); + } + }); + + return allActions; + } + } + } +} \ No newline at end of file diff --git a/src/services/codefixes/references.ts b/src/services/codefixes/references.ts new file mode 100644 index 00000000000..3675d626678 --- /dev/null +++ b/src/services/codefixes/references.ts @@ -0,0 +1,6 @@ +/// +/// +/// +/// +/// +/// diff --git a/src/services/codefixes/superFixes.ts b/src/services/codefixes/superFixes.ts new file mode 100644 index 00000000000..2c6a67de513 --- /dev/null +++ b/src/services/codefixes/superFixes.ts @@ -0,0 +1,65 @@ +/* @internal */ +namespace ts.codeFix { + function getOpenBraceEnd(constructor: ConstructorDeclaration, sourceFile: SourceFile) { + // First token is the open curly, this is where we want to put the 'super' call. + return constructor.body.getFirstToken(sourceFile).getEnd(); + } + + registerCodeFix({ + name: "AddMissingSuperCallFix", + errorCodes: ["TS2377"], + getCodeActions: (context: CodeFixContext) => { + const sourceFile = context.sourceFile; + const token = getTokenAtPosition(sourceFile, context.span.start); + Debug.assert(token.kind === SyntaxKind.ConstructorKeyword, "Failed to find the constructor."); + + const newPosition = getOpenBraceEnd(token.parent, sourceFile); + return [{ + description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call), + changes: [{ fileName: sourceFile.fileName, textChanges: [{ newText: "super();", span: { start: newPosition, length: 0 } }] }] + }]; + } + }); + + registerCodeFix({ + name: "MakeSuperCallTheFirstStatementInTheConstructor", + errorCodes: ["TS17009"], + getCodeActions: (context: CodeFixContext) => { + const sourceFile = context.sourceFile; + + const token = getTokenAtPosition(sourceFile, context.span.start); + const constructor = getContainingFunction(token); + Debug.assert(constructor.kind === SyntaxKind.Constructor, "Failed to find the constructor."); + + const superCall = findSuperCall((constructor).body); + Debug.assert(!!superCall, "Failed to find super call."); + + const newPosition = getOpenBraceEnd(constructor, sourceFile); + const changes = [{ + fileName: sourceFile.fileName, textChanges: [{ + newText: superCall.getText(sourceFile), + span: { start: newPosition, length: 0 } + }, + { + newText: "", + span: { start: superCall.getStart(sourceFile), length: superCall.getWidth(sourceFile) } + }] + }]; + + return [{ + description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor), + changes + }]; + + function findSuperCall(n: Node): Node { + if (n.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((n).expression)) { + return n; + } + if (isFunctionLike(n)) { + return undefined; + } + return forEachChild(n, findSuperCall); + } + } + }); +} \ No newline at end of file diff --git a/src/services/services.ts b/src/services/services.ts index aea4d5f872e..b189a6375b7 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -11,6 +11,7 @@ /// /// /// +/// namespace ts { /** The version of the language service API */ @@ -1237,6 +1238,8 @@ namespace ts { isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): CodeAction[]; + getEmitOutput(fileName: string): EmitOutput; getProgram(): Program; @@ -1283,6 +1286,18 @@ namespace ts { newText: string; } + export interface FileTextChanges { + fileName: string; + textChanges: TextChange[]; + } + + export interface CodeAction { + /** Description of the code action to display in the UI of the editor */ + description: string; + /** Text changes to apply to each file as part of the code action */ + changes: FileTextChanges[]; + } + export interface TextInsertion { newText: string; /** The position in newText the caret should point to after the insertion. */ @@ -1886,9 +1901,13 @@ namespace ts { }; } - // Cache host information about script should be refreshed + export function getSupportedCodeFixes() { + return codeFix.CodeFixProvider.getSupportedErrorCodes(); + } + + // Cache host information about script Should be refreshed // at each language service public entry point, since we don't know when - // set of scripts handled by the host changes. + // the set of scripts handled by the host changes. class HostCache { private fileNameToEntry: FileMap; private _compilationSettings: CompilerOptions; @@ -3022,6 +3041,7 @@ namespace ts { documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService { const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host); + const codeFixProvider: codeFix.CodeFixProvider = new codeFix.CodeFixProvider(); let ruleProvider: formatting.RulesProvider; let program: Program; let lastProjectVersion: string; @@ -7832,6 +7852,30 @@ namespace ts { return []; } + function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): CodeAction[] { + synchronizeHostData(); + const sourceFile = getValidSourceFile(fileName); + const checker = program.getTypeChecker(); + let allFixes: CodeAction[] = []; + + forEach(errorCodes, error => { + const context = { + errorCode: error, + sourceFile: sourceFile, + span: { start, length: end - start }, + checker: checker, + newLineCharacter: getNewLineOrDefaultFromHost(host) + }; + + const fixes = codeFixProvider.getFixes(context); + if (fixes) { + allFixes = allFixes.concat(fixes); + } + }); + + return allFixes; + } + /** * Checks if position points to a valid position to add JSDoc comments, and if so, * returns the appropriate template. Otherwise returns an empty string. @@ -8302,6 +8346,7 @@ namespace ts { getFormattingEditsAfterKeystroke, getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, + getCodeFixesAtPosition, getEmitOutput, getNonBoundSourceFile, getProgram diff --git a/src/services/shims.ts b/src/services/shims.ts index 45c4b284ae7..dfa34d29acc 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -240,6 +240,8 @@ namespace ts { */ isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): string; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string): string; + getEmitOutput(fileName: string): string; getEmitOutputObject(fileName: string): EmitOutput; } @@ -255,6 +257,7 @@ namespace ts { getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; discoverTypings(discoverTypingsJson: string): string; + getSupportedCodeFixes(): string; } function logInternalError(logger: Logger, err: Error) { @@ -901,6 +904,16 @@ namespace ts { ); } + public getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string): string { + return this.forwardJSONCall( + `getCodeFixesAtPosition( '${fileName}', ${start}, ${end}, ${errorCodes}')`, + () => { + const localErrors: string[] = JSON.parse(errorCodes); + return this.languageService.getCodeFixesAtPosition(fileName, start, end, localErrors); + } + ); + } + /// NAVIGATE TO /** Return a list of symbols that are interesting to navigate to */ @@ -1109,6 +1122,12 @@ namespace ts { info.compilerOptions); }); } + + public getSupportedCodeFixes(): string { + return this.forwardJSONCall("getSupportedCodeFixes()", + () => getSupportedCodeFixes() + ); + } } export class TypeScriptServicesFactory implements ShimFactory { diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index cfeb7c2fcd5..2cf75daa861 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -52,6 +52,9 @@ "formatting/rulesMap.ts", "formatting/rulesProvider.ts", "formatting/smartIndenter.ts", - "formatting/tokenRange.ts" + "formatting/tokenRange.ts", + "codeFixes/codeFixProvider.ts", + "codeFixes/references.ts", + "codeFixes/superFixes.ts" ] } From 466d26fc76ff9808383a8973bab4e662ae9b056b Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 5 Aug 2016 16:21:46 -0700 Subject: [PATCH 02/92] Fourslash support --- src/compiler/core.ts | 9 +++++ src/compiler/diagnosticMessages.json | 28 +++++++++++++++ src/compiler/types.ts | 1 + src/harness/fourslash.ts | 51 +++++++++++++++++++++++++-- src/harness/harnessLanguageService.ts | 3 ++ src/server/client.ts | 4 +++ tests/cases/fourslash/fourslash.ts | 1 + 7 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 6c87ad82955..b07c21fa4cb 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -115,6 +115,15 @@ namespace ts { return -1; } + export function firstOrUndefined(array: T[], predicate: (x: T) => boolean): T { + for (let i = 0, len = array.length; i < len; i++) { + if (predicate(array[i])) { + return array[i]; + } + } + return undefined; + } + export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number { for (let i = start || 0, len = text.length; i < len; i++) { if (contains(charCodes, text.charCodeAt(i))) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8126d5c605e..1dffc35c74c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3031,5 +3031,33 @@ "Unknown typing option '{0}'.": { "category": "Error", "code": 17010 + }, + "Add missing 'super()' call.": { + "category": "CodeFix", + "code": 90001 + }, + "Make 'super()' call the first statement in the constructor.": { + "category": "CodeFix", + "code": 90002 + }, + "Change 'extends' to 'implements'": { + "category": "CodeFix", + "code": 90003 + }, + "Remove unused identifiers": { + "category": "CodeFix", + "code": 90004 + }, + "Implement interface on reference": { + "category": "CodeFix", + "code": 90005 + }, + "Implement interface on class": { + "category": "CodeFix", + "code": 90006 + }, + "Implement inherited abstract class": { + "category": "CodeFix", + "code": 90007 } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 28ededebb0d..e31a4b0ace1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2547,6 +2547,7 @@ namespace ts { Warning, Error, Message, + CodeFix, } export enum ModuleResolutionKind { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a42abbbc609..91597d7f593 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -384,7 +384,7 @@ namespace FourSlash { if (exists !== negative) { this.printErrorLog(negative, this.getAllDiagnostics()); - throw new Error("Failure between markers: " + startMarkerName + ", " + endMarkerName); + throw new Error(`Failure between markers: '${startMarkerName}', '${endMarkerName}'`); } } @@ -638,7 +638,6 @@ namespace FourSlash { } } - public verifyCompletionListAllowsNewIdentifier(negative: boolean) { const completions = this.getCompletionListAtCaret(); @@ -1479,7 +1478,7 @@ namespace FourSlash { if (isFormattingEdit) { const newContent = this.getFileContent(fileName); - if (newContent.replace(/\s/g, "") !== oldContent.replace(/\s/g, "")) { + if (this.removeWhitespace(newContent) !== this.removeWhitespace(oldContent)) { this.raiseError("Formatting operation destroyed non-whitespace content"); } } @@ -1545,6 +1544,10 @@ namespace FourSlash { } } + private removeWhitespace(text: string): string { + return text.replace(/\s/g, ""); + } + public goToBOF() { this.goToPosition(0); } @@ -1862,6 +1865,44 @@ namespace FourSlash { } } + public verifyCodeFixAtPosition(expectedText: string, errorCode?: number) { + + const ranges = this.getRanges(); + if (ranges.length == 0) { + this.raiseError("At least one range should be specified in the testfile."); + } + + const fileName = this.activeFile.fileName; + const diagnostics = this.getDiagnostics(fileName); + + if (diagnostics.length === 0) { + this.raiseError("Errors expected."); + } + + if (diagnostics.length > 1 && !errorCode) { + this.raiseError("When there's more than one error, you must specify the errror to fix."); + } + + const diagnostic = !errorCode ? diagnostics[0] : ts.firstOrUndefined(diagnostics, d => d.code == errorCode); + + const actual = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [`TS${diagnostic.code}`]); + + if (!actual || actual.length == 0) { + this.raiseError("No codefixes returned."); + } + + if (actual.length > 1) { + this.raiseError("More than 1 codefix returned."); + } + + this.applyEdits(actual[0].changes[0].fileName, actual[0].changes[0].textChanges, /*isFormattingEdit*/ false); + const actualText = this.rangeText(ranges[0]); + + if (this.removeWhitespace(actualText) !== this.removeWhitespace(expectedText)) { + this.raiseError(`Actual text doesn't match expected text. Actual: '${actualText}' Expected: '${expectedText}'`); + } + } + public verifyDocCommentTemplate(expected?: ts.TextInsertion) { const name = "verifyDocCommentTemplate"; const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition); @@ -3066,6 +3107,10 @@ namespace FourSlashInterface { this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true); } + public codeFixAtPosition(expectedText: string, errorCode?: number): void { + this.state.verifyCodeFixAtPosition(expectedText, errorCode); + } + public navigationBar(json: any) { this.state.verifyNavigationBar(json); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index d7ed04b627f..d2219c3969d 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -453,6 +453,9 @@ namespace Harness.LanguageService { isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean { return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace)); } + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): ts.CodeAction[] { + return unwrapJSONCallResult(this.shim.getCodeFixesAtPosition(fileName, start, end, JSON.stringify(errorCodes))); + } getEmitOutput(fileName: string): ts.EmitOutput { return unwrapJSONCallResult(this.shim.getEmitOutput(fileName)); } diff --git a/src/server/client.ts b/src/server/client.ts index f04dbd8dc02..3c8a07c8cdf 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -592,6 +592,10 @@ namespace ts.server { throw new Error("Not Implemented Yet."); } + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): ts.CodeAction[] { + throw new Error("Not Implemented Yet."); + } + getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] { const lineOffset = this.positionToOneBasedLineOffset(fileName, position); const args: protocol.FileLocationRequestArgs = { diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9ce3197a46f..ee0d4d100d3 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -192,6 +192,7 @@ declare namespace FourSlashInterface { noMatchingBracePositionInCurrentFile(bracePosition: number): void; DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void; noDocCommentTemplate(): void; + codeFixAtPosition(expectedText: string, errorCode?: number): void; navigationBar(json: any): void; navigationItemsListCount(count: number, searchValue: string, matchKind?: string): void; From 6f4fb064ca27792b19828d842bd4472a45b4d7ae Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 5 Aug 2016 16:21:46 -0700 Subject: [PATCH 03/92] SuperFix fourslash tests --- tests/cases/fourslash/superFix1.ts | 10 ++++++++++ tests/cases/fourslash/superFix2.ts | 13 +++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/cases/fourslash/superFix1.ts create mode 100644 tests/cases/fourslash/superFix2.ts diff --git a/tests/cases/fourslash/superFix1.ts b/tests/cases/fourslash/superFix1.ts new file mode 100644 index 00000000000..7fbe2cb4fd7 --- /dev/null +++ b/tests/cases/fourslash/superFix1.ts @@ -0,0 +1,10 @@ +/// + +////class Base{ +////} +////class C extends Base{ +//// constructor() {[| |] +//// } +////} + +verify.codeFixAtPosition('super();'); diff --git a/tests/cases/fourslash/superFix2.ts b/tests/cases/fourslash/superFix2.ts new file mode 100644 index 00000000000..880b5d43167 --- /dev/null +++ b/tests/cases/fourslash/superFix2.ts @@ -0,0 +1,13 @@ +/// + +////class Base{ +////} +////class C extends Base{ +//// private a:number; +//// constructor() {[| +//// this.a = 12; +//// super();|] +//// } +////} + +verify.codeFixAtPosition("super(); this.a = 12;"); \ No newline at end of file From e0b73c4a4a65f3c98a1b99ce5a981a1f89d44283 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 5 Aug 2016 16:44:43 -0700 Subject: [PATCH 04/92] Clean up --- src/services/codefixes/references.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/services/codefixes/references.ts b/src/services/codefixes/references.ts index 3675d626678..a9ab7a98b33 100644 --- a/src/services/codefixes/references.ts +++ b/src/services/codefixes/references.ts @@ -1,6 +1,3 @@ /// /// /// -/// -/// -/// From 42d61b5098ac5847b5e09117cf92092c6dc9f284 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Mon, 8 Aug 2016 11:55:59 -0700 Subject: [PATCH 05/92] Call checkExpression eventhough there is no appropriate type from destructuring of array --- src/compiler/checker.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4ead19f7c48..74a1e17bd6b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4447,9 +4447,14 @@ namespace ts { return property; } - // Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - // necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - // Object and Function as appropriate. + /** + * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when + * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from + * Object and Function as appropriate. + * + * @param type a type to look up property from + * @param name a name of property to look up in a given type + */ function getPropertyOfType(type: Type, name: string): Symbol { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { @@ -12856,6 +12861,9 @@ namespace ts { return checkDestructuringAssignment(element, type, contextualMapper); } else { + // We still need to check element expression here because we may need to set appropriate flag on the expression + // such as NodeCheckFlags.LexicalThis on "this"expression. + checkExpression(element); if (isTupleType(sourceType)) { error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (sourceType).elementTypes.length, elements.length); } From 9a18429184a42f6a34a963908acdc141245d1ade Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Mon, 8 Aug 2016 11:56:20 -0700 Subject: [PATCH 06/92] Add tests and baselines --- ...turingThisInTupleDestructuring1.errors.txt | 13 +++++++++++ .../emitCapturingThisInTupleDestructuring1.js | 11 +++++++++ ...turingThisInTupleDestructuring2.errors.txt | 16 +++++++++++++ .../emitCapturingThisInTupleDestructuring2.js | 23 +++++++++++++++++++ .../emitCapturingThisInTupleDestructuring1.ts | 4 ++++ .../emitCapturingThisInTupleDestructuring2.ts | 10 ++++++++ 6 files changed, 77 insertions(+) create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js create mode 100644 tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts create mode 100644 tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt new file mode 100644 index 00000000000..d0f11d056ca --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt @@ -0,0 +1,13 @@ +tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts(3,17): error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. +tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts(3,29): error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + + +==== tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts (2 errors) ==== + declare function wrapper(x: any); + wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + }); \ No newline at end of file diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js new file mode 100644 index 00000000000..9dd7453ba4f --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js @@ -0,0 +1,11 @@ +//// [emitCapturingThisInTupleDestructuring1.ts] +declare function wrapper(x: any); +wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" +}); + +//// [emitCapturingThisInTupleDestructuring1.js] +var _this = this; +wrapper(function (array) { + _this.test = array[0], _this.test1 = array[1], _this.test2 = array[2]; // even though there is a compiler error, we should still emit lexical capture for "this" +}); diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt new file mode 100644 index 00000000000..25207e28c79 --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts(8,39): error TS2493: Tuple type '[number, number]' with length '2' cannot be assigned to tuple with length '3'. + + +==== tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts (1 errors) ==== + var array1: [number, number] = [1, 2]; + + class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[number, number]' with length '2' cannot be assigned to tuple with length '3'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js new file mode 100644 index 00000000000..f999cfe70f0 --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js @@ -0,0 +1,23 @@ +//// [emitCapturingThisInTupleDestructuring2.ts] +var array1: [number, number] = [1, 2]; + +class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + } +} + +//// [emitCapturingThisInTupleDestructuring2.js] +var array1 = [1, 2]; +var B = (function () { + function B() { + } + B.prototype.method = function () { + var _this = this; + (function () { return (_this.test = array1[0], _this.test1 = array1[1], _this.test2 = array1[2], array1); }); // even though there is a compiler error, we should still emit lexical capture for "this" + }; + return B; +}()); diff --git a/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts b/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts new file mode 100644 index 00000000000..e369cd9fb50 --- /dev/null +++ b/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts @@ -0,0 +1,4 @@ +declare function wrapper(x: any); +wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" +}); \ No newline at end of file diff --git a/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts b/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts new file mode 100644 index 00000000000..2bb931c4f4f --- /dev/null +++ b/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts @@ -0,0 +1,10 @@ +var array1: [number, number] = [1, 2]; + +class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + } +} \ No newline at end of file From f16d599f79457fee725b942d1e0ca13176950d05 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Tue, 9 Aug 2016 15:54:44 -0700 Subject: [PATCH 07/92] Issues an error when there are more than one export default --- src/compiler/binder.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8d8666a67ab..1eedc4535dc 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -337,8 +337,18 @@ namespace ts { ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; + // If the current node has NodeFlags.Default (e.g. if the node is class declaration or function declaration) + // and there is already another default export (i.e. symbol.declarations is not empty), we need to report such error + if (isDefaultExport && symbol.declarations) { + message = Diagnostics.A_module_cannot_have_multiple_default_exports; + } + forEach(symbol.declarations, declaration => { - if (declaration.flags & NodeFlags.Default) { + // Error on multiple export default in the following case: + // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default + // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) + if ((declaration.flags & NodeFlags.Default) || + (declaration.kind === SyntaxKind.ExportAssignment && !(node).isExportEquals)) { message = Diagnostics.A_module_cannot_have_multiple_default_exports; } }); @@ -1898,7 +1908,9 @@ namespace ts { } else { // An export default clause with an expression exports a value - declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); + // We want to exclude both class and function here, this is necessary to issue an error when there are both + // default export-assignment and default export function and class declaration. + declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.Class | SymbolFlags.Function | SymbolFlags.Property); } } From 7c3f27434c13264582e71d7b3580083a847a0fcf Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Tue, 9 Aug 2016 15:55:26 -0700 Subject: [PATCH 08/92] Add tests and baselines --- .../multipleExportDefault1.errors.txt | 19 ++++++++++++++++ .../reference/multipleExportDefault1.js | 20 +++++++++++++++++ .../multipleExportDefault2.errors.txt | 18 +++++++++++++++ .../reference/multipleExportDefault2.js | 18 +++++++++++++++ .../multipleExportDefault3.errors.txt | 18 +++++++++++++++ .../reference/multipleExportDefault3.js | 22 +++++++++++++++++++ .../multipleExportDefault4.errors.txt | 16 ++++++++++++++ .../reference/multipleExportDefault4.js | 20 +++++++++++++++++ .../multipleExportDefault5.errors.txt | 11 ++++++++++ .../reference/multipleExportDefault5.js | 16 ++++++++++++++ .../multipleExportDefault6.errors.txt | 20 +++++++++++++++++ .../reference/multipleExportDefault6.js | 19 ++++++++++++++++ .../externalModules/multipleExportDefault1.ts | 7 ++++++ .../externalModules/multipleExportDefault2.ts | 6 +++++ .../externalModules/multipleExportDefault3.ts | 6 +++++ .../externalModules/multipleExportDefault4.ts | 5 +++++ .../externalModules/multipleExportDefault5.ts | 2 ++ .../externalModules/multipleExportDefault6.ts | 7 ++++++ 18 files changed, 250 insertions(+) create mode 100644 tests/baselines/reference/multipleExportDefault1.errors.txt create mode 100644 tests/baselines/reference/multipleExportDefault1.js create mode 100644 tests/baselines/reference/multipleExportDefault2.errors.txt create mode 100644 tests/baselines/reference/multipleExportDefault2.js create mode 100644 tests/baselines/reference/multipleExportDefault3.errors.txt create mode 100644 tests/baselines/reference/multipleExportDefault3.js create mode 100644 tests/baselines/reference/multipleExportDefault4.errors.txt create mode 100644 tests/baselines/reference/multipleExportDefault4.js create mode 100644 tests/baselines/reference/multipleExportDefault5.errors.txt create mode 100644 tests/baselines/reference/multipleExportDefault5.js create mode 100644 tests/baselines/reference/multipleExportDefault6.errors.txt create mode 100644 tests/baselines/reference/multipleExportDefault6.js create mode 100644 tests/cases/conformance/externalModules/multipleExportDefault1.ts create mode 100644 tests/cases/conformance/externalModules/multipleExportDefault2.ts create mode 100644 tests/cases/conformance/externalModules/multipleExportDefault3.ts create mode 100644 tests/cases/conformance/externalModules/multipleExportDefault4.ts create mode 100644 tests/cases/conformance/externalModules/multipleExportDefault5.ts create mode 100644 tests/cases/conformance/externalModules/multipleExportDefault6.ts diff --git a/tests/baselines/reference/multipleExportDefault1.errors.txt b/tests/baselines/reference/multipleExportDefault1.errors.txt new file mode 100644 index 00000000000..3da6afc41e0 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault1.errors.txt @@ -0,0 +1,19 @@ +tests/cases/conformance/externalModules/multipleExportDefault1.ts(1,25): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault1.ts(5,1): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault1.ts (2 errors) ==== + export default function Foo (){ + ~~~ +!!! error TS2528: A module cannot have multiple default exports. + + } + + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. + \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault1.js b/tests/baselines/reference/multipleExportDefault1.js new file mode 100644 index 00000000000..bec4fb48969 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault1.js @@ -0,0 +1,20 @@ +//// [multipleExportDefault1.ts] +export default function Foo (){ + +} + +export default { + uhoh: "another default", +}; + + +//// [multipleExportDefault1.js] +"use strict"; +function Foo() { +} +exports.__esModule = true; +exports["default"] = Foo; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; diff --git a/tests/baselines/reference/multipleExportDefault2.errors.txt b/tests/baselines/reference/multipleExportDefault2.errors.txt new file mode 100644 index 00000000000..0543e25a163 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault2.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/externalModules/multipleExportDefault2.ts(1,1): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault2.ts(5,25): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault2.ts (2 errors) ==== + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. + + export default function Foo() { } + ~~~ +!!! error TS2528: A module cannot have multiple default exports. + + \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault2.js b/tests/baselines/reference/multipleExportDefault2.js new file mode 100644 index 00000000000..dc82500a1d9 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault2.js @@ -0,0 +1,18 @@ +//// [multipleExportDefault2.ts] +export default { + uhoh: "another default", +}; + +export default function Foo() { } + + + +//// [multipleExportDefault2.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; +function Foo() { } +exports.__esModule = true; +exports["default"] = Foo; diff --git a/tests/baselines/reference/multipleExportDefault3.errors.txt b/tests/baselines/reference/multipleExportDefault3.errors.txt new file mode 100644 index 00000000000..f1e55da9112 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault3.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/externalModules/multipleExportDefault3.ts(1,1): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault3.ts(5,22): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault3.ts (2 errors) ==== + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. + + export default class C { } + ~ +!!! error TS2528: A module cannot have multiple default exports. + + \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault3.js b/tests/baselines/reference/multipleExportDefault3.js new file mode 100644 index 00000000000..5d7ad48e0ca --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault3.js @@ -0,0 +1,22 @@ +//// [multipleExportDefault3.ts] +export default { + uhoh: "another default", +}; + +export default class C { } + + + +//// [multipleExportDefault3.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; +var C = (function () { + function C() { + } + return C; +}()); +exports.__esModule = true; +exports["default"] = C; diff --git a/tests/baselines/reference/multipleExportDefault4.errors.txt b/tests/baselines/reference/multipleExportDefault4.errors.txt new file mode 100644 index 00000000000..bba15527d00 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault4.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/externalModules/multipleExportDefault4.ts(1,22): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault4.ts(3,1): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault4.ts (2 errors) ==== + export default class C { } + ~ +!!! error TS2528: A module cannot have multiple default exports. + + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault4.js b/tests/baselines/reference/multipleExportDefault4.js new file mode 100644 index 00000000000..1199fa91c9c --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault4.js @@ -0,0 +1,20 @@ +//// [multipleExportDefault4.ts] +export default class C { } + +export default { + uhoh: "another default", +}; + +//// [multipleExportDefault4.js] +"use strict"; +var C = (function () { + function C() { + } + return C; +}()); +exports.__esModule = true; +exports["default"] = C; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; diff --git a/tests/baselines/reference/multipleExportDefault5.errors.txt b/tests/baselines/reference/multipleExportDefault5.errors.txt new file mode 100644 index 00000000000..30c87339ff2 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault5.errors.txt @@ -0,0 +1,11 @@ +tests/cases/conformance/externalModules/multipleExportDefault5.ts(1,25): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault5.ts(2,22): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault5.ts (2 errors) ==== + export default function bar() { } + ~~~ +!!! error TS2528: A module cannot have multiple default exports. + export default class C {} + ~ +!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault5.js b/tests/baselines/reference/multipleExportDefault5.js new file mode 100644 index 00000000000..7ae33aee9f4 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault5.js @@ -0,0 +1,16 @@ +//// [multipleExportDefault5.ts] +export default function bar() { } +export default class C {} + +//// [multipleExportDefault5.js] +"use strict"; +function bar() { } +exports.__esModule = true; +exports["default"] = bar; +var C = (function () { + function C() { + } + return C; +}()); +exports.__esModule = true; +exports["default"] = C; diff --git a/tests/baselines/reference/multipleExportDefault6.errors.txt b/tests/baselines/reference/multipleExportDefault6.errors.txt new file mode 100644 index 00000000000..a8bf80e8df0 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault6.errors.txt @@ -0,0 +1,20 @@ +tests/cases/conformance/externalModules/multipleExportDefault6.ts(1,1): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault6.ts(5,1): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault6.ts (2 errors) ==== + export default { + ~~~~~~~~~~~~~~~~ + lol: 1 + ~~~~~~~~~~ + } + ~ +!!! error TS2528: A module cannot have multiple default exports. + + export default { + ~~~~~~~~~~~~~~~~ + lol: 2 + ~~~~~~~~~~ + } + ~ +!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault6.js b/tests/baselines/reference/multipleExportDefault6.js new file mode 100644 index 00000000000..a8b93459e4e --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault6.js @@ -0,0 +1,19 @@ +//// [multipleExportDefault6.ts] +export default { + lol: 1 +} + +export default { + lol: 2 +} + +//// [multipleExportDefault6.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { + lol: 1 +}; +exports.__esModule = true; +exports["default"] = { + lol: 2 +}; diff --git a/tests/cases/conformance/externalModules/multipleExportDefault1.ts b/tests/cases/conformance/externalModules/multipleExportDefault1.ts new file mode 100644 index 00000000000..623cc2acc20 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault1.ts @@ -0,0 +1,7 @@ +export default function Foo (){ + +} + +export default { + uhoh: "another default", +}; diff --git a/tests/cases/conformance/externalModules/multipleExportDefault2.ts b/tests/cases/conformance/externalModules/multipleExportDefault2.ts new file mode 100644 index 00000000000..0e14b570aa1 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault2.ts @@ -0,0 +1,6 @@ +export default { + uhoh: "another default", +}; + +export default function Foo() { } + diff --git a/tests/cases/conformance/externalModules/multipleExportDefault3.ts b/tests/cases/conformance/externalModules/multipleExportDefault3.ts new file mode 100644 index 00000000000..3b5460e8141 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault3.ts @@ -0,0 +1,6 @@ +export default { + uhoh: "another default", +}; + +export default class C { } + diff --git a/tests/cases/conformance/externalModules/multipleExportDefault4.ts b/tests/cases/conformance/externalModules/multipleExportDefault4.ts new file mode 100644 index 00000000000..6181c360798 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault4.ts @@ -0,0 +1,5 @@ +export default class C { } + +export default { + uhoh: "another default", +}; \ No newline at end of file diff --git a/tests/cases/conformance/externalModules/multipleExportDefault5.ts b/tests/cases/conformance/externalModules/multipleExportDefault5.ts new file mode 100644 index 00000000000..d2b67fce46f --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault5.ts @@ -0,0 +1,2 @@ +export default function bar() { } +export default class C {} \ No newline at end of file diff --git a/tests/cases/conformance/externalModules/multipleExportDefault6.ts b/tests/cases/conformance/externalModules/multipleExportDefault6.ts new file mode 100644 index 00000000000..889ae64376b --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault6.ts @@ -0,0 +1,7 @@ +export default { + lol: 1 +} + +export default { + lol: 2 +} \ No newline at end of file From 042da569f71b40b0723b92259dc8a197c64580a5 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 11 Aug 2016 10:04:22 -0700 Subject: [PATCH 09/92] Address PR: add comment --- src/compiler/binder.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1eedc4535dc..2547dd2cd5a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -337,21 +337,24 @@ namespace ts { ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; - // If the current node has NodeFlags.Default (e.g. if the node is class declaration or function declaration) - // and there is already another default export (i.e. symbol.declarations is not empty), we need to report such error + // If the current node is a default export of some sort, then check if + // there are any other default exports that we need to error on. + // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. if (isDefaultExport && symbol.declarations) { message = Diagnostics.A_module_cannot_have_multiple_default_exports; } - - forEach(symbol.declarations, declaration => { - // Error on multiple export default in the following case: - // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default - // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) - if ((declaration.flags & NodeFlags.Default) || - (declaration.kind === SyntaxKind.ExportAssignment && !(node).isExportEquals)) { - message = Diagnostics.A_module_cannot_have_multiple_default_exports; - } - }); + else { + // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. + forEach(symbol.declarations, declaration => { + // Error on multiple export default in the following case: + // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default + // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) + if ((declaration.flags & NodeFlags.Default) || + (declaration.kind === SyntaxKind.ExportAssignment && !(node).isExportEquals)) { + message = Diagnostics.A_module_cannot_have_multiple_default_exports; + } + }); + } forEach(symbol.declarations, declaration => { file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration))); From 821348b75d60f095f4e88a036fd4928a51527b99 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 11 Aug 2016 10:48:43 -0700 Subject: [PATCH 10/92] Update baseline --- ...sFileCompilationBindMultipleDefaultExports.errors.txt | 8 +++++++- .../reference/multipleDefaultExports01.errors.txt | 9 +++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt b/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt index 4c1d237aa47..05fecf03d30 100644 --- a/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt +++ b/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt @@ -1,9 +1,15 @@ +tests/cases/compiler/a.js(1,22): error TS2528: A module cannot have multiple default exports. +tests/cases/compiler/a.js(3,1): error TS2528: A module cannot have multiple default exports. tests/cases/compiler/a.js(3,16): error TS1109: Expression expected. -==== tests/cases/compiler/a.js (1 errors) ==== +==== tests/cases/compiler/a.js (3 errors) ==== export default class a { + ~ +!!! error TS2528: A module cannot have multiple default exports. } export default var a = 10; + ~~~~~~~~~~~~~~ +!!! error TS2528: A module cannot have multiple default exports. ~~~ !!! error TS1109: Expression expected. \ No newline at end of file diff --git a/tests/baselines/reference/multipleDefaultExports01.errors.txt b/tests/baselines/reference/multipleDefaultExports01.errors.txt index b3e7d757289..16aa3b2f7b1 100644 --- a/tests/baselines/reference/multipleDefaultExports01.errors.txt +++ b/tests/baselines/reference/multipleDefaultExports01.errors.txt @@ -1,16 +1,13 @@ -tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2323: Cannot redeclare exported variable 'default'. tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2528: A module cannot have multiple default exports. tests/cases/conformance/es6/modules/m1.ts(6,25): error TS2528: A module cannot have multiple default exports. -tests/cases/conformance/es6/modules/m1.ts(11,1): error TS2323: Cannot redeclare exported variable 'default'. +tests/cases/conformance/es6/modules/m1.ts(11,1): error TS2528: A module cannot have multiple default exports. tests/cases/conformance/es6/modules/m2.ts(3,1): error TS2348: Value of type 'typeof foo' is not callable. Did you mean to include 'new'? -==== tests/cases/conformance/es6/modules/m1.ts (4 errors) ==== +==== tests/cases/conformance/es6/modules/m1.ts (3 errors) ==== export default class foo { ~~~ -!!! error TS2323: Cannot redeclare exported variable 'default'. - ~~~ !!! error TS2528: A module cannot have multiple default exports. } @@ -24,7 +21,7 @@ tests/cases/conformance/es6/modules/m2.ts(3,1): error TS2348: Value of type 'typ var x = 10; export default x; ~~~~~~~~~~~~~~~~~ -!!! error TS2323: Cannot redeclare exported variable 'default'. +!!! error TS2528: A module cannot have multiple default exports. ==== tests/cases/conformance/es6/modules/m2.ts (1 errors) ==== import Entity from "./m1" From 3479f4bcbe233adcd0dd500686de3bf18b8efd57 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 11 Aug 2016 10:49:36 -0700 Subject: [PATCH 11/92] fix linting --- src/compiler/binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 573bd0b7e35..c3dab267d02 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1913,7 +1913,7 @@ namespace ts { ? SymbolFlags.Alias // An export default clause with any other expression exports a value : SymbolFlags.Property; - declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.Property | SymbolFlags.AliasExcludes | SymbolFlags.Class | SymbolFlags.Function); //SymbolFlags.Class | SymbolFlags.Function | SymbolFlags.Property); + declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.Property | SymbolFlags.AliasExcludes | SymbolFlags.Class | SymbolFlags.Function); } } From 7b525ea99fb6befa87032741de972dfc0a8f650e Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Wed, 17 Aug 2016 16:06:13 -0700 Subject: [PATCH 12/92] Use for..of instead --- src/compiler/binder.ts | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 45dbab15732..5e065a00d2f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -349,23 +349,26 @@ namespace ts { ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; - // If the current node is a default export of some sort, then check if - // there are any other default exports that we need to error on. - // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. - if (isDefaultExport && symbol.declarations) { - message = Diagnostics.A_module_cannot_have_multiple_default_exports; - } - else { - // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. - forEach(symbol.declarations, declaration => { - // Error on multiple export default in the following case: - // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default - // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) - if ((declaration.flags & NodeFlags.Default) || - (declaration.kind === SyntaxKind.ExportAssignment && !(node).isExportEquals)) { - message = Diagnostics.A_module_cannot_have_multiple_default_exports; + if (symbol.declarations && symbol.declarations.length) { + // If the current node is a default export of some sort, then check if + // there are any other default exports that we need to error on. + // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. + if (isDefaultExport) { + message = Diagnostics.A_module_cannot_have_multiple_default_exports; + } + else { + // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. + for (const declaration of symbol.declarations) { + // Error on multiple export default in the following case: + // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default + // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) + if ((declaration.flags & NodeFlags.Default) || + (declaration.kind === SyntaxKind.ExportAssignment && !(node).isExportEquals)) { + message = Diagnostics.A_module_cannot_have_multiple_default_exports; + break; + } } - }); + } } forEach(symbol.declarations, declaration => { From 124e05fd68ffe662fca5bc5fa9ee91dd9b52fdcb Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 5 Aug 2016 16:44:43 -0700 Subject: [PATCH 13/92] PR Feedback --- src/compiler/core.ts | 24 +++++++++++++---------- src/compiler/diagnosticMessages.json | 14 ++++++------- src/compiler/types.ts | 1 - src/harness/fourslash.ts | 6 +++--- src/services/codefixes/codeFixProvider.ts | 7 ++----- src/services/codefixes/references.ts | 3 --- src/services/codefixes/superFixes.ts | 8 +++----- src/services/services.ts | 4 ++-- 8 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b07c21fa4cb..835a8be3167 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1,4 +1,4 @@ -/// +/// /// @@ -115,15 +115,6 @@ namespace ts { return -1; } - export function firstOrUndefined(array: T[], predicate: (x: T) => boolean): T { - for (let i = 0, len = array.length; i < len; i++) { - if (predicate(array[i])) { - return array[i]; - } - } - return undefined; - } - export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number { for (let i = start || 0, len = text.length; i < len; i++) { if (contains(charCodes, text.charCodeAt(i))) { @@ -229,6 +220,19 @@ namespace ts { return true; } + /** + * Returns the first element that matches the predicate, or undefined if none + * could be found. + */ + export function find(array: T[], predicate: (item: T) => boolean): T { + for (let i = 0, len = array.length; i < len; i++) { + if (predicate(array[i])) { + return array[i]; + } + } + return undefined; + } + /** * Returns the last element of an array if non-empty, undefined otherwise. */ diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1dffc35c74c..34f5d481075 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3033,31 +3033,31 @@ "code": 17010 }, "Add missing 'super()' call.": { - "category": "CodeFix", + "category": "Message", "code": 90001 }, "Make 'super()' call the first statement in the constructor.": { - "category": "CodeFix", + "category": "Message", "code": 90002 }, "Change 'extends' to 'implements'": { - "category": "CodeFix", + "category": "Message", "code": 90003 }, "Remove unused identifiers": { - "category": "CodeFix", + "category": "Message", "code": 90004 }, "Implement interface on reference": { - "category": "CodeFix", + "category": "Message", "code": 90005 }, "Implement interface on class": { - "category": "CodeFix", + "category": "Message", "code": 90006 }, "Implement inherited abstract class": { - "category": "CodeFix", + "category": "Message", "code": 90007 } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e31a4b0ace1..28ededebb0d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2547,7 +2547,6 @@ namespace ts { Warning, Error, Message, - CodeFix, } export enum ModuleResolutionKind { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 91597d7f593..19fa9ba5b4a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1,4 +1,4 @@ -// +// // Copyright (c) Microsoft Corporation. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -1879,11 +1879,11 @@ namespace FourSlash { this.raiseError("Errors expected."); } - if (diagnostics.length > 1 && !errorCode) { + if (diagnostics.length > 1 && errorCode !== undefined) { this.raiseError("When there's more than one error, you must specify the errror to fix."); } - const diagnostic = !errorCode ? diagnostics[0] : ts.firstOrUndefined(diagnostics, d => d.code == errorCode); + const diagnostic = !errorCode ? diagnostics[0] : ts.find(diagnostics, d => d.code == errorCode); const actual = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [`TS${diagnostic.code}`]); diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index ca877aef46a..71199af88fa 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -1,7 +1,6 @@ -/* @internal */ +/* @internal */ namespace ts { export interface CodeFix { - name: string; errorCodes: string[]; getCodeActions(context: CodeFixContext): CodeAction[]; } @@ -14,7 +13,7 @@ namespace ts { newLineCharacter: string; } - export namespace codeFix { + export namespace codefix { const codeFixes: Map = {}; export function registerCodeFix(action: CodeFix) { @@ -37,8 +36,6 @@ namespace ts { const fixes = codeFixes[context.errorCode]; let allActions: CodeAction[] = []; - Debug.assert(fixes && fixes.length > 0, "No fixes found for error: '${errorCode}'."); - forEach(fixes, f => { const actions = f.getCodeActions(context); if (actions && actions.length > 0) { diff --git a/src/services/codefixes/references.ts b/src/services/codefixes/references.ts index 3675d626678..a9ab7a98b33 100644 --- a/src/services/codefixes/references.ts +++ b/src/services/codefixes/references.ts @@ -1,6 +1,3 @@ /// /// /// -/// -/// -/// diff --git a/src/services/codefixes/superFixes.ts b/src/services/codefixes/superFixes.ts index 2c6a67de513..d06ba9df955 100644 --- a/src/services/codefixes/superFixes.ts +++ b/src/services/codefixes/superFixes.ts @@ -1,13 +1,12 @@ /* @internal */ -namespace ts.codeFix { +namespace ts.codefix { function getOpenBraceEnd(constructor: ConstructorDeclaration, sourceFile: SourceFile) { // First token is the open curly, this is where we want to put the 'super' call. return constructor.body.getFirstToken(sourceFile).getEnd(); } registerCodeFix({ - name: "AddMissingSuperCallFix", - errorCodes: ["TS2377"], + errorCodes: [`TS${Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call.code}`], getCodeActions: (context: CodeFixContext) => { const sourceFile = context.sourceFile; const token = getTokenAtPosition(sourceFile, context.span.start); @@ -22,8 +21,7 @@ namespace ts.codeFix { }); registerCodeFix({ - name: "MakeSuperCallTheFirstStatementInTheConstructor", - errorCodes: ["TS17009"], + errorCodes: [`TS${Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class.code}`], getCodeActions: (context: CodeFixContext) => { const sourceFile = context.sourceFile; diff --git a/src/services/services.ts b/src/services/services.ts index b189a6375b7..839fc4c298e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1902,7 +1902,7 @@ namespace ts { } export function getSupportedCodeFixes() { - return codeFix.CodeFixProvider.getSupportedErrorCodes(); + return codefix.CodeFixProvider.getSupportedErrorCodes(); } // Cache host information about script Should be refreshed @@ -3041,7 +3041,7 @@ namespace ts { documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService { const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host); - const codeFixProvider: codeFix.CodeFixProvider = new codeFix.CodeFixProvider(); + const codeFixProvider: codefix.CodeFixProvider = new codefix.CodeFixProvider(); let ruleProvider: formatting.RulesProvider; let program: Program; let lastProjectVersion: string; From f931e606b18e3635d545b5d1f768b0b05b01a2ca Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 26 Aug 2016 15:33:23 -0700 Subject: [PATCH 14/92] Fix build break caused by merge from master --- src/compiler/core.ts | 13 ------------- src/services/codefixes/codeFixProvider.ts | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index fdfa139a21c..c6effb658b7 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -292,19 +292,6 @@ namespace ts { return true; } - /** - * Returns the first element that matches the predicate, or undefined if none - * could be found. - */ - export function find(array: T[], predicate: (item: T) => boolean): T { - for (let i = 0, len = array.length; i < len; i++) { - if (predicate(array[i])) { - return array[i]; - } - } - return undefined; - } - /** * Returns the last element of an array if non-empty, undefined otherwise. */ diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index 71199af88fa..9c4e7404c95 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -14,7 +14,7 @@ namespace ts { } export namespace codefix { - const codeFixes: Map = {}; + const codeFixes = createMap(); export function registerCodeFix(action: CodeFix) { forEach(action.errorCodes, error => { @@ -29,7 +29,7 @@ namespace ts { export class CodeFixProvider { public static getSupportedErrorCodes() { - return getKeys(codeFixes); + return Object.keys(codeFixes); } public getFixes(context: CodeFixContext): CodeAction[] { From 49b65c749fc291dfb22b997820ce0c369ad40c43 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 9 Sep 2016 14:30:28 -0700 Subject: [PATCH 15/92] PR feedback --- src/services/codefixes/superFixes.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/services/codefixes/superFixes.ts b/src/services/codefixes/superFixes.ts index d06ba9df955..622ded06d31 100644 --- a/src/services/codefixes/superFixes.ts +++ b/src/services/codefixes/superFixes.ts @@ -10,7 +10,10 @@ namespace ts.codefix { getCodeActions: (context: CodeFixContext) => { const sourceFile = context.sourceFile; const token = getTokenAtPosition(sourceFile, context.span.start); - Debug.assert(token.kind === SyntaxKind.ConstructorKeyword, "Failed to find the constructor."); + + if (token.kind !== SyntaxKind.ConstructorKeyword) { + return undefined; + } const newPosition = getOpenBraceEnd(token.parent, sourceFile); return [{ @@ -26,14 +29,18 @@ namespace ts.codefix { const sourceFile = context.sourceFile; const token = getTokenAtPosition(sourceFile, context.span.start); - const constructor = getContainingFunction(token); - Debug.assert(constructor.kind === SyntaxKind.Constructor, "Failed to find the constructor."); + if (token.kind !== SyntaxKind.ConstructorKeyword) { + return undefined; + } + const constructor = getContainingFunction(token); const superCall = findSuperCall((constructor).body); - Debug.assert(!!superCall, "Failed to find super call."); + if (!superCall) { + return undefined; + } const newPosition = getOpenBraceEnd(constructor, sourceFile); - const changes = [{ + const changes = [{ fileName: sourceFile.fileName, textChanges: [{ newText: superCall.getText(sourceFile), span: { start: newPosition, length: 0 } From 682f81257d345f72e230a53aa864d3dee4af6bdc Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Fri, 16 Sep 2016 14:52:33 -0700 Subject: [PATCH 16/92] PR feedback --- src/services/codefixes/codeFixProvider.ts | 32 +++++++++++------------ src/services/services.ts | 8 +++--- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index 9c4e7404c95..55aa8d38f7a 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -9,7 +9,7 @@ namespace ts { errorCode: string; sourceFile: SourceFile; span: TextSpan; - checker: TypeChecker; + program: Program; newLineCharacter: string; } @@ -27,24 +27,22 @@ namespace ts { }); } - export class CodeFixProvider { - public static getSupportedErrorCodes() { - return Object.keys(codeFixes); - } + export function getSupportedErrorCodes() { + return Object.keys(codeFixes); + } - public getFixes(context: CodeFixContext): CodeAction[] { - const fixes = codeFixes[context.errorCode]; - let allActions: CodeAction[] = []; + export function getFixes(context: CodeFixContext): CodeAction[] { + const fixes = codeFixes[context.errorCode]; + let allActions: CodeAction[] = []; - forEach(fixes, f => { - const actions = f.getCodeActions(context); - if (actions && actions.length > 0) { - allActions = allActions.concat(actions); - } - }); + forEach(fixes, f => { + const actions = f.getCodeActions(context); + if (actions && actions.length > 0) { + allActions = allActions.concat(actions); + } + }); - return allActions; - } + return allActions; } } -} \ No newline at end of file +} diff --git a/src/services/services.ts b/src/services/services.ts index b481ffa5269..bf6ea627899 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -695,7 +695,7 @@ namespace ts { } export function getSupportedCodeFixes() { - return codefix.CodeFixProvider.getSupportedErrorCodes(); + return codefix.getSupportedErrorCodes(); } // Cache host information about script Should be refreshed @@ -918,7 +918,6 @@ namespace ts { documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService { const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host); - const codeFixProvider: codefix.CodeFixProvider = new codefix.CodeFixProvider(); let ruleProvider: formatting.RulesProvider; let program: Program; let lastProjectVersion: string; @@ -1588,7 +1587,6 @@ namespace ts { function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): CodeAction[] { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); - const checker = program.getTypeChecker(); let allFixes: CodeAction[] = []; forEach(errorCodes, error => { @@ -1596,11 +1594,11 @@ namespace ts { errorCode: error, sourceFile: sourceFile, span: { start, length: end - start }, - checker: checker, + program: program, newLineCharacter: getNewLineOrDefaultFromHost(host) }; - const fixes = codeFixProvider.getFixes(context); + const fixes = codefix.getFixes(context); if (fixes) { allFixes = allFixes.concat(fixes); } From 2035db1d51b0eaae1d840ceba1a70541a77153c3 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 23 Sep 2016 17:37:11 -0700 Subject: [PATCH 17/92] Convert to Doc Comment --- src/services/types.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/types.ts b/src/services/types.ts index e8c411cc1c8..35d52532687 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -473,7 +473,11 @@ namespace ts { export interface CompletionInfo { isMemberCompletion: boolean; - isNewIdentifierLocation: boolean; // true when the current location also allows for a new identifier + + /** + * true when the current location also allows for a new identifier + */ + isNewIdentifierLocation: boolean; entries: CompletionEntry[]; } From b38d7ac9fd41c46b70c0e5fcfe815a25c98270a4 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 26 Sep 2016 11:19:06 -0700 Subject: [PATCH 18/92] Annotate directorySeparator --- src/compiler/core.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 72d05e1cfd7..aca9701eee7 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1044,9 +1044,14 @@ namespace ts { return 0; } + /** + * Internally, we represent paths as strings with '/' as the directory separator. + * When we make system calls (eg: LanguageServiceHost.getDirectory()), + * we expect the host to correctly handle paths in our specified format. + */ export const directorySeparator = "/"; const directorySeparatorCharCode = CharacterCodes.slash; - function getNormalizedParts(normalizedSlashedPath: string, rootLength: number) { + function getNormalizedParts(normalizedSlashedPath: string, rootLength: number): string[] { const parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator); const normalized: string[] = []; for (const part of parts) { From d423aadc72c5247db5f674f7277429bcf39fc6af Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 26 Sep 2016 13:34:07 -0700 Subject: [PATCH 19/92] comments --- src/services/completions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index d6a4da89ed1..00a5f80379b 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -323,12 +323,12 @@ namespace ts.Completions { } function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { - fragment = getDirectoryPath(fragment); + fragment = getDirectoryPath(fragment); // TODO: modify fragment so it respects our internal path representation? if (!fragment) { - fragment = "./"; + fragment = "." + directorySeparator; } else { - fragment = ensureTrailingDirectorySeparator(fragment); + fragment = ensureTrailingDirectorySeparator(fragment); // TODO: why is this necessary? } const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment)); @@ -544,7 +544,7 @@ namespace ts.Completions { const kind = match[2]; const toComplete = match[3]; - const scriptPath = getDirectoryPath(sourceFile.path); + const scriptPath = getDirectoryPath(sourceFile.path); // TODO: normalize for win10? let entries: CompletionEntry[]; if (kind === "path") { // Give completions for a relative path From 1f7b6e6a31afaaa00a57cf875b7a3a41af267250 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 27 Sep 2016 10:54:03 -0700 Subject: [PATCH 20/92] More comments --- src/compiler/core.ts | 11 +++++++++-- src/services/completions.ts | 14 +++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index aca9701eee7..0b6dd3c9d7c 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1015,7 +1015,9 @@ namespace ts { return path.replace(/\\/g, "/"); } - // Returns length of path root (i.e. length of "/", "x:/", "//server/share/, file:///user/files") + /** + * Returns length of path root (i.e. length of "/", "x:/", "//server/share/, file:///user/files") + */ export function getRootLength(path: string): number { if (path.charCodeAt(0) === CharacterCodes.slash) { if (path.charCodeAt(1) !== CharacterCodes.slash) return 1; @@ -1074,7 +1076,7 @@ namespace ts { export function normalizePath(path: string): string { path = normalizeSlashes(path); - const rootLength = getRootLength(path); + const rootLength = getRootLength(path); // TODO: this expects un-slash-normalized strings. eg: 'x:\\...' const root = path.substr(0, rootLength); const normalized = getNormalizedParts(path, rootLength); if (normalized.length) { @@ -1091,6 +1093,11 @@ namespace ts { return path.charCodeAt(path.length - 1) === directorySeparatorCharCode; } + /** + * Returns the path except for its basename. Eg: + * + * /path/to/file.ext -> /path/to + */ export function getDirectoryPath(path: Path): Path; export function getDirectoryPath(path: string): string; export function getDirectoryPath(path: string): any { diff --git a/src/services/completions.ts b/src/services/completions.ts index 00a5f80379b..04b68544376 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -322,10 +322,22 @@ namespace ts.Completions { return result; } + /** + * Given a path ending at a directory, gets the completions for the path. + */ function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { + /*Debug.assert(fragment !== undefined); + + if (fragment === "") { + fragment = "."; + } else { + fragment = getDirectoryPath(fragment); + } + */ + fragment = getDirectoryPath(fragment); // TODO: modify fragment so it respects our internal path representation? if (!fragment) { - fragment = "." + directorySeparator; + fragment = "."; } else { fragment = ensureTrailingDirectorySeparator(fragment); // TODO: why is this necessary? From 769d248519c28bc538ee9eedf657d9fbacf3f354 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 27 Sep 2016 13:43:42 -0700 Subject: [PATCH 21/92] new test --- .../tripleSlashRefPathRelativePaths.ts | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/cases/fourslash/tripleSlashRefPathRelativePaths.ts diff --git a/tests/cases/fourslash/tripleSlashRefPathRelativePaths.ts b/tests/cases/fourslash/tripleSlashRefPathRelativePaths.ts new file mode 100644 index 00000000000..96d0a11214f --- /dev/null +++ b/tests/cases/fourslash/tripleSlashRefPathRelativePaths.ts @@ -0,0 +1,137 @@ +/// + +// Exercises relative path completions going up and down 2 directories +// and the use of forward- and back-slashes and combinations thereof. + +// @Filename: f1.ts +//// /*f1*/ +// @Filename: f2.ts +//// /*f2*/ +// @Filename: dir1/g1.ts +//// /*g1*/ +// @Filename: dir1/g2.ts +//// /*g2*/ +// @Filename: dir1/dir2/h1.ts +//// /*h1*/ +// @Filename: dir1/dir2/h2.ts +//// /*h2*/ +// @Filename: dir1/dir2/.hidden.ts +//// /*hidden*/ +// @Filename: dir1/dir2/dir3/i1.ts +//// /*i1*/ +// @Filename: dir1/dir2/dir3/i2.ts +//// /*i2*/ +// @Filename: dir1/dir2/dir3/dir4/j1.ts +//// /*j1*/ +// @Filename: dir1/dir2/dir3/dir4/j2.ts +//// /*j2*/ + + +// @Filename: dir1/dir2/test.ts +//// /// Date: Tue, 27 Sep 2016 14:04:57 -0700 Subject: [PATCH 22/92] remove Comment --- src/services/completions.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 04b68544376..35a7bce5561 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -326,18 +326,9 @@ namespace ts.Completions { * Given a path ending at a directory, gets the completions for the path. */ function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { - /*Debug.assert(fragment !== undefined); - - if (fragment === "") { - fragment = "."; - } else { - fragment = getDirectoryPath(fragment); - } - */ - fragment = getDirectoryPath(fragment); // TODO: modify fragment so it respects our internal path representation? if (!fragment) { - fragment = "."; + fragment = "./"; } else { fragment = ensureTrailingDirectorySeparator(fragment); // TODO: why is this necessary? From 629e126718d7a6950e0394082fdd7964bdc4857f Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 28 Sep 2016 16:04:39 -0700 Subject: [PATCH 23/92] Factored out hidden path test --- .../fourslash/tripleSlashRefPathHiddenFile.ts | 28 +++++++++++++++++++ .../tripleSlashRefPathRelativePaths.ts | 8 ++---- 2 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/tripleSlashRefPathHiddenFile.ts diff --git a/tests/cases/fourslash/tripleSlashRefPathHiddenFile.ts b/tests/cases/fourslash/tripleSlashRefPathHiddenFile.ts new file mode 100644 index 00000000000..6c1280637e9 --- /dev/null +++ b/tests/cases/fourslash/tripleSlashRefPathHiddenFile.ts @@ -0,0 +1,28 @@ +/// + +// Exercises completions for hidden files (ie: those beginning with '.') + +// @Filename: f1.ts +//// /*f1*/ +// @Filename: f2.ts +//// /*f2*/ +// @Filename: .hidden.ts +//// /*hidden*/ + +// @Filename: test.ts +//// /// Date: Wed, 28 Sep 2016 16:45:52 -0700 Subject: [PATCH 24/92] factor and simplify rel path test --- .../tripleSlashRefPathBackandForwardSlash.ts | 73 +++++++++ .../tripleSlashRefPathCompletionsNarrow.ts | 21 +++ .../fourslash/tripleSlashRefPathHiddenFile.ts | 9 +- .../tripleSlashRefPathRelativePaths.ts | 143 ++++++------------ 4 files changed, 145 insertions(+), 101 deletions(-) create mode 100644 tests/cases/fourslash/tripleSlashRefPathBackandForwardSlash.ts create mode 100644 tests/cases/fourslash/tripleSlashRefPathCompletionsNarrow.ts diff --git a/tests/cases/fourslash/tripleSlashRefPathBackandForwardSlash.ts b/tests/cases/fourslash/tripleSlashRefPathBackandForwardSlash.ts new file mode 100644 index 00000000000..c7cae3c4a45 --- /dev/null +++ b/tests/cases/fourslash/tripleSlashRefPathBackandForwardSlash.ts @@ -0,0 +1,73 @@ +/// + +// Exercises completions for hidden files (ie: those beginning with '.') + +// @Filename: f.ts +//// /*f1*/ +// @Filename: d1/g.ts +//// /*g1*/ +// @Filename: d1/d2/h.ts +//// /*h1*/ +// @Filename: d1/d2/d3/i.ts +//// /// + +// Exercises relative path completions going up and down 2 directories +// and the use of forward- and back-slashes and combinations thereof. + +// @Filename: f1.ts +//// /*f1*/ +// @Filename: f2.ts +//// /*f2*/ + +// @Filename: test.ts +//// ///