From 9a85ad6a4eac71150923b3406a3db7500574d102 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Wed, 30 Sep 2015 09:10:56 +0200 Subject: [PATCH 01/34] Fixed #5032: tsserver: Format on type broken --- src/server/session.ts | 73 +++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index da044e7b4c6..7a95f6d75e9 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -17,10 +17,25 @@ namespace ts.server { strBuilder += " "; } spaceCache[n] = strBuilder; - } + } return spaceCache[n]; } + export function generateIndentString(n: number, editorOptions: EditorOptions): string { + if (editorOptions.ConvertTabsToSpaces) { + return generateSpaces(n); + } else { + var result = ""; + for (var i = 0; i < Math.floor(n / editorOptions.TabSize); i++) { + result += "\t"; + } + for (var i = 0; i < n % editorOptions.TabSize; i++) { + result += " "; + } + return result; + } + } + interface FileStart { file: string; start: ILineInfo; @@ -51,7 +66,7 @@ namespace ts.server { return 1; } } - + function formatDiag(fileName: string, project: Project, diag: ts.Diagnostic) { return { start: project.compilerService.host.positionToLineOffset(fileName, diag.start), @@ -104,7 +119,7 @@ namespace ts.server { export const Unknown = "unknown"; } - module Errors { + module Errors { export var NoProject = new Error("No Project."); } @@ -121,9 +136,9 @@ namespace ts.server { private changeSeq = 0; constructor( - private host: ServerHost, - private byteLength: (buf: string, encoding?: string) => number, - private hrtime: (start?: number[]) => number[], + private host: ServerHost, + private byteLength: (buf: string, encoding?: string) => number, + private hrtime: (start?: number[]) => number[], private logger: Logger ) { this.projectService = @@ -227,7 +242,7 @@ namespace ts.server { this.syntacticCheck(file, project); this.semanticCheck(file, project); } - + private reloadProjects() { this.projectService.reloadProjects(); } @@ -360,9 +375,9 @@ namespace ts.server { let { compilerService } = project; let position = compilerService.host.lineOffsetToPosition(fileName, line, offset); - + let documentHighlights = compilerService.languageService.getDocumentHighlights(fileName, position, filesToSearch); - + if (!documentHighlights) { return undefined; } @@ -557,7 +572,7 @@ namespace ts.server { var compilerService = project.compilerService; var startPosition = compilerService.host.lineOffsetToPosition(file, line, offset); var endPosition = compilerService.host.lineOffsetToPosition(file, endLine, endOffset); - + // TODO: avoid duplicate code (with formatonkey) var edits = compilerService.languageService.getFormattingEditsForRange(file, startPosition, endPosition, this.projectService.getFormatCodeOptions(file)); @@ -607,27 +622,25 @@ namespace ts.server { NewLineCharacter: "\n", ConvertTabsToSpaces: formatOptions.ConvertTabsToSpaces, }; - var indentPosition = - compilerService.languageService.getIndentationAtPosition(file, position, editorOptions); + var preferredIndent = compilerService.languageService.getIndentationAtPosition(file, position, editorOptions); + var hasIndent = 0; for (var i = 0, len = lineText.length; i < len; i++) { if (lineText.charAt(i) == " ") { - indentPosition--; + hasIndent++; } else if (lineText.charAt(i) == "\t") { - indentPosition -= editorOptions.IndentSize; + hasIndent += editorOptions.TabSize; } else { break; } } - if (indentPosition > 0) { - var spaces = generateSpaces(indentPosition); - edits.push({ span: ts.createTextSpanFromBounds(position, position), newText: spaces }); - } - else if (indentPosition < 0) { + // i points to the first non whitespace character + if (preferredIndent !== hasIndent) { + var firstNoWhiteSpacePosition = lineInfo.offset + i; edits.push({ - span: ts.createTextSpanFromBounds(position, position - indentPosition), - newText: "" + span: ts.createTextSpanFromBounds(lineInfo.offset, firstNoWhiteSpacePosition), + newText: generateIndentString(preferredIndent, editorOptions) }); } } @@ -702,14 +715,14 @@ namespace ts.server { if (!project) { throw Errors.NoProject; } - + var compilerService = project.compilerService; var position = compilerService.host.lineOffsetToPosition(file, line, offset); var helpItems = compilerService.languageService.getSignatureHelpItems(file, position); if (!helpItems) { return undefined; } - + var span = helpItems.applicableSpan; var result: protocol.SignatureHelpItems = { items: helpItems.items, @@ -721,10 +734,10 @@ namespace ts.server { argumentIndex: helpItems.argumentIndex, argumentCount: helpItems.argumentCount, } - + return result; } - + private getDiagnostics(delay: number, fileNames: string[]) { var checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => { fileName = ts.normalizePath(fileName); @@ -859,20 +872,20 @@ namespace ts.server { private getBraceMatching(line: number, offset: number, fileName: string): protocol.TextSpan[] { var file = ts.normalizePath(fileName); - + var project = this.projectService.getProjectForFile(file); if (!project) { throw Errors.NoProject; } - + var compilerService = project.compilerService; var position = compilerService.host.lineOffsetToPosition(file, line, offset); - + var spans = compilerService.languageService.getBraceMatchingAtPosition(file, position); if (!spans) { return undefined; } - + return spans.map(span => ({ start: compilerService.host.positionToLineOffset(file, span.start), end: compilerService.host.positionToLineOffset(file, span.start + span.length) @@ -1064,7 +1077,7 @@ namespace ts.server { public onMessage(message: string) { if (this.logger.isVerbose()) { this.logger.info("request: " + message); - var start = this.hrtime(); + var start = this.hrtime(); } try { var request = JSON.parse(message); From 2f8e4fa6bd1697c6ec82afc269ee7a51640ce78f Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Wed, 30 Sep 2015 09:18:50 +0200 Subject: [PATCH 02/34] Fixed unnecessay whitespace changes --- src/server/session.ts | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index 7a95f6d75e9..41a0e409d57 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -17,7 +17,7 @@ namespace ts.server { strBuilder += " "; } spaceCache[n] = strBuilder; - } + } return spaceCache[n]; } @@ -35,7 +35,7 @@ namespace ts.server { return result; } } - + interface FileStart { file: string; start: ILineInfo; @@ -66,7 +66,7 @@ namespace ts.server { return 1; } } - + function formatDiag(fileName: string, project: Project, diag: ts.Diagnostic) { return { start: project.compilerService.host.positionToLineOffset(fileName, diag.start), @@ -119,7 +119,7 @@ namespace ts.server { export const Unknown = "unknown"; } - module Errors { + module Errors { export var NoProject = new Error("No Project."); } @@ -136,9 +136,9 @@ namespace ts.server { private changeSeq = 0; constructor( - private host: ServerHost, - private byteLength: (buf: string, encoding?: string) => number, - private hrtime: (start?: number[]) => number[], + private host: ServerHost, + private byteLength: (buf: string, encoding?: string) => number, + private hrtime: (start?: number[]) => number[], private logger: Logger ) { this.projectService = @@ -242,7 +242,7 @@ namespace ts.server { this.syntacticCheck(file, project); this.semanticCheck(file, project); } - + private reloadProjects() { this.projectService.reloadProjects(); } @@ -375,9 +375,9 @@ namespace ts.server { let { compilerService } = project; let position = compilerService.host.lineOffsetToPosition(fileName, line, offset); - + let documentHighlights = compilerService.languageService.getDocumentHighlights(fileName, position, filesToSearch); - + if (!documentHighlights) { return undefined; } @@ -572,7 +572,7 @@ namespace ts.server { var compilerService = project.compilerService; var startPosition = compilerService.host.lineOffsetToPosition(file, line, offset); var endPosition = compilerService.host.lineOffsetToPosition(file, endLine, endOffset); - + // TODO: avoid duplicate code (with formatonkey) var edits = compilerService.languageService.getFormattingEditsForRange(file, startPosition, endPosition, this.projectService.getFormatCodeOptions(file)); @@ -715,14 +715,14 @@ namespace ts.server { if (!project) { throw Errors.NoProject; } - + var compilerService = project.compilerService; var position = compilerService.host.lineOffsetToPosition(file, line, offset); var helpItems = compilerService.languageService.getSignatureHelpItems(file, position); if (!helpItems) { return undefined; } - + var span = helpItems.applicableSpan; var result: protocol.SignatureHelpItems = { items: helpItems.items, @@ -734,10 +734,10 @@ namespace ts.server { argumentIndex: helpItems.argumentIndex, argumentCount: helpItems.argumentCount, } - + return result; } - + private getDiagnostics(delay: number, fileNames: string[]) { var checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => { fileName = ts.normalizePath(fileName); @@ -872,20 +872,20 @@ namespace ts.server { private getBraceMatching(line: number, offset: number, fileName: string): protocol.TextSpan[] { var file = ts.normalizePath(fileName); - + var project = this.projectService.getProjectForFile(file); if (!project) { throw Errors.NoProject; } - + var compilerService = project.compilerService; var position = compilerService.host.lineOffsetToPosition(file, line, offset); - + var spans = compilerService.languageService.getBraceMatchingAtPosition(file, position); if (!spans) { return undefined; } - + return spans.map(span => ({ start: compilerService.host.positionToLineOffset(file, span.start), end: compilerService.host.positionToLineOffset(file, span.start + span.length) @@ -1077,7 +1077,7 @@ namespace ts.server { public onMessage(message: string) { if (this.logger.isVerbose()) { this.logger.info("request: " + message); - var start = this.hrtime(); + var start = this.hrtime(); } try { var request = JSON.parse(message); From 0bc5c14d51b253527cb2dee2e17927d6a215444d Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 1 Oct 2015 15:25:43 -0700 Subject: [PATCH 03/34] Change fileWatcher in sys for node 4 --- src/compiler/sys.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index f3f2b02a30e..3782f4d11e9 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -9,6 +9,7 @@ namespace ts { readFile(path: string, encoding?: string): string; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; watchFile?(path: string, callback: (path: string) => void): FileWatcher; + watchDirectory?(path: string, callback: (path: string) => void): FileWatcher; resolvePath(path: string): string; fileExists(path: string): boolean; directoryExists(path: string): boolean; @@ -191,6 +192,12 @@ namespace ts { const _fs = require("fs"); const _path = require("path"); const _os = require("os"); + const _process = require("process"); + + + function isNode4OrLater(): Boolean { + return parseInt(_process.version.charAt(1)) >= 4; + } const platform: string = _os.platform(); // win32\win64 are case insensitive platforms, MacOS (darwin) by default is also case insensitive @@ -284,6 +291,15 @@ namespace ts { readFile, writeFile, watchFile: (fileName, callback) => { + + // Node 4.0 stablized the `fs.watch` function which avoids polling + // and is more efficient than `fs.watchFile` (ref: https://github.com/nodejs/node/pull/2649 + // and https://github.com/Microsoft/TypeScript/issues/4643), therefore + // if the current node.js version is newer than 4, use `fs.watch` instead. + if (isNode4OrLater()) { + return _fs.watch(fileName, (eventName: string, path: string) => callback(path)); + } + // watchFile polls a file every 250ms, picking up file notifications. _fs.watchFile(fileName, { persistent: true, interval: 250 }, fileChanged); From 5daa100bf40fdb5cc13d5ae1555dc09c1e9078e4 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 1 Oct 2015 15:40:13 -0700 Subject: [PATCH 04/34] unify the node filewatcher in sys.ts and server.ts --- src/compiler/sys.ts | 122 +++++++++++++++++++++++++++++++++++++------ src/server/server.ts | 110 -------------------------------------- 2 files changed, 107 insertions(+), 125 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 3782f4d11e9..91856a46b29 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -21,6 +21,12 @@ namespace ts { exit(exitCode?: number): void; } + interface WatchedFile { + fileName: string; + callback: (fileName: string) => void; + mtime: Date; + } + export interface FileWatcher { close(): void; } @@ -193,7 +199,104 @@ namespace ts { const _path = require("path"); const _os = require("os"); const _process = require("process"); - + + class WatchedFileSet { + private watchedFiles: WatchedFile[] = []; + private nextFileToCheck = 0; + private watchTimer: NodeJS.Timer; + + // average async stat takes about 30 microseconds + // set chunk size to do 30 files in < 1 millisecond + constructor(public interval = 2500, public chunkSize = 30) { + } + + private static copyListRemovingItem(item: T, list: T[]) { + var copiedList: T[] = []; + for (var i = 0, len = list.length; i < len; i++) { + if (list[i] != item) { + copiedList.push(list[i]); + } + } + return copiedList; + } + + private static getModifiedTime(fileName: string): Date { + return _fs.statSync(fileName).mtime; + } + + private poll(checkedIndex: number) { + var watchedFile = this.watchedFiles[checkedIndex]; + if (!watchedFile) { + return; + } + + _fs.stat(watchedFile.fileName, (err: any, stats: any) => { + if (err) { + watchedFile.callback(watchedFile.fileName); + } + else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { + watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName); + watchedFile.callback(watchedFile.fileName); + } + }); + } + + // this implementation uses polling and + // stat due to inconsistencies of fs.watch + // and efficiency of stat on modern filesystems + private startWatchTimer() { + this.watchTimer = setInterval(() => { + var count = 0; + var nextToCheck = this.nextFileToCheck; + var firstCheck = -1; + while ((count < this.chunkSize) && (nextToCheck !== firstCheck)) { + this.poll(nextToCheck); + if (firstCheck < 0) { + firstCheck = nextToCheck; + } + nextToCheck++; + if (nextToCheck === this.watchedFiles.length) { + nextToCheck = 0; + } + count++; + } + this.nextFileToCheck = nextToCheck; + }, this.interval); + } + + addFile(fileName: string, callback: (fileName: string) => void): WatchedFile { + var file: WatchedFile = { + fileName, + callback, + mtime: WatchedFileSet.getModifiedTime(fileName) + }; + + this.watchedFiles.push(file); + if (this.watchedFiles.length === 1) { + this.startWatchTimer(); + } + return file; + } + + removeFile(file: WatchedFile) { + this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles); + } + } + + // REVIEW: for now this implementation uses polling. + // The advantage of polling is that it works reliably + // on all os and with network mounted files. + // For 90 referenced files, the average time to detect + // changes is 2*msInterval (by default 5 seconds). + // The overhead of this is .04 percent (1/2500) with + // average pause of < 1 millisecond (and max + // pause less than 1.5 milliseconds); question is + // do we anticipate reference sets in the 100s and + // do we care about waiting 10-20 seconds to detect + // changes for large reference sets? If so, do we want + // to increase the chunk size or decrease the interval + // time dynamically to match the large reference set? + var watchedFileSet = new WatchedFileSet(); function isNode4OrLater(): Boolean { return parseInt(_process.version.charAt(1)) >= 4; @@ -291,8 +394,7 @@ namespace ts { readFile, writeFile, watchFile: (fileName, callback) => { - - // Node 4.0 stablized the `fs.watch` function which avoids polling + // Node 4.0 stablized the `fs.watch` function on Windows which avoids polling // and is more efficient than `fs.watchFile` (ref: https://github.com/nodejs/node/pull/2649 // and https://github.com/Microsoft/TypeScript/issues/4643), therefore // if the current node.js version is newer than 4, use `fs.watch` instead. @@ -300,19 +402,9 @@ namespace ts { return _fs.watch(fileName, (eventName: string, path: string) => callback(path)); } - // watchFile polls a file every 250ms, picking up file notifications. - _fs.watchFile(fileName, { persistent: true, interval: 250 }, fileChanged); - + var watchedFile = watchedFileSet.addFile(fileName, callback); return { - close() { _fs.unwatchFile(fileName, fileChanged); } - }; - - function fileChanged(curr: any, prev: any) { - if (+curr.mtime <= +prev.mtime) { - return; - } - - callback(fileName); + close: () => watchedFileSet.removeFile(watchedFile) } }, resolvePath: function (path: string): string { diff --git a/src/server/server.ts b/src/server/server.ts index 843197b918a..39864fc8477 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -83,95 +83,6 @@ namespace ts.server { } } - interface WatchedFile { - fileName: string; - callback: (fileName: string) => void; - mtime: Date; - } - - class WatchedFileSet { - private watchedFiles: WatchedFile[] = []; - private nextFileToCheck = 0; - private watchTimer: NodeJS.Timer; - - // average async stat takes about 30 microseconds - // set chunk size to do 30 files in < 1 millisecond - constructor(public interval = 2500, public chunkSize = 30) { - } - - private static copyListRemovingItem(item: T, list: T[]) { - var copiedList: T[] = []; - for (var i = 0, len = list.length; i < len; i++) { - if (list[i] != item) { - copiedList.push(list[i]); - } - } - return copiedList; - } - - private static getModifiedTime(fileName: string): Date { - return fs.statSync(fileName).mtime; - } - - private poll(checkedIndex: number) { - var watchedFile = this.watchedFiles[checkedIndex]; - if (!watchedFile) { - return; - } - - fs.stat(watchedFile.fileName,(err, stats) => { - if (err) { - watchedFile.callback(watchedFile.fileName); - } - else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { - watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName); - watchedFile.callback(watchedFile.fileName); - } - }); - } - - // this implementation uses polling and - // stat due to inconsistencies of fs.watch - // and efficiency of stat on modern filesystems - private startWatchTimer() { - this.watchTimer = setInterval(() => { - var count = 0; - var nextToCheck = this.nextFileToCheck; - var firstCheck = -1; - while ((count < this.chunkSize) && (nextToCheck !== firstCheck)) { - this.poll(nextToCheck); - if (firstCheck < 0) { - firstCheck = nextToCheck; - } - nextToCheck++; - if (nextToCheck === this.watchedFiles.length) { - nextToCheck = 0; - } - count++; - } - this.nextFileToCheck = nextToCheck; - }, this.interval); - } - - addFile(fileName: string, callback: (fileName: string) => void ): WatchedFile { - var file: WatchedFile = { - fileName, - callback, - mtime: WatchedFileSet.getModifiedTime(fileName) - }; - - this.watchedFiles.push(file); - if (this.watchedFiles.length === 1) { - this.startWatchTimer(); - } - return file; - } - - removeFile(file: WatchedFile) { - this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles); - } - } - class IOSession extends Session { constructor(host: ServerHost, logger: ts.server.Logger) { super(host, Buffer.byteLength, process.hrtime, logger); @@ -243,28 +154,7 @@ namespace ts.server { // TODO: check that this location is writable var logger = createLoggerFromEnv(); - - // REVIEW: for now this implementation uses polling. - // The advantage of polling is that it works reliably - // on all os and with network mounted files. - // For 90 referenced files, the average time to detect - // changes is 2*msInterval (by default 5 seconds). - // The overhead of this is .04 percent (1/2500) with - // average pause of < 1 millisecond (and max - // pause less than 1.5 milliseconds); question is - // do we anticipate reference sets in the 100s and - // do we care about waiting 10-20 seconds to detect - // changes for large reference sets? If so, do we want - // to increase the chunk size or decrease the interval - // time dynamically to match the large reference set? - var watchedFileSet = new WatchedFileSet(); - ts.sys.watchFile = function (fileName, callback) { - var watchedFile = watchedFileSet.addFile(fileName, callback); - return { - close: () => watchedFileSet.removeFile(watchedFile) - } - }; var ioSession = new IOSession(ts.sys, logger); process.on('uncaughtException', function(err: Error) { ioSession.logError(err, "unknown"); From 4dcf8c773729e577ad49a64255e1d9c76c09f7c1 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 1 Oct 2015 15:59:03 -0700 Subject: [PATCH 05/34] bug fixes --- src/compiler/sys.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 91856a46b29..0b4d4dabd52 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -198,12 +198,11 @@ namespace ts { const _fs = require("fs"); const _path = require("path"); const _os = require("os"); - const _process = require("process"); class WatchedFileSet { private watchedFiles: WatchedFile[] = []; private nextFileToCheck = 0; - private watchTimer: NodeJS.Timer; + private watchTimer: any; // average async stat takes about 30 microseconds // set chunk size to do 30 files in < 1 millisecond @@ -299,7 +298,7 @@ namespace ts { var watchedFileSet = new WatchedFileSet(); function isNode4OrLater(): Boolean { - return parseInt(_process.version.charAt(1)) >= 4; + return parseInt(process.version.charAt(1)) >= 4; } const platform: string = _os.platform(); From 98eaeba4f162049f6595aa05127e9a9f9704d8e4 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Fri, 2 Oct 2015 11:49:30 -0700 Subject: [PATCH 06/34] temp save --- src/compiler/sys.ts | 16 ++++++- src/server/editorServices.ts | 92 +++++++++++++++++++++++++++--------- 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 0b4d4dabd52..85c674ec904 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -9,7 +9,7 @@ namespace ts { readFile(path: string, encoding?: string): string; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; watchFile?(path: string, callback: (path: string) => void): FileWatcher; - watchDirectory?(path: string, callback: (path: string) => void): FileWatcher; + watchDirectory?(path: string, callback: (path: string) => void, recursive?: boolean): FileWatcher; resolvePath(path: string): string; fileExists(path: string): boolean; directoryExists(path: string): boolean; @@ -406,6 +406,20 @@ namespace ts { close: () => watchedFileSet.removeFile(watchedFile) } }, + watchDirectory: (path, callback, recursive) => { + // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows + // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) + // therefore if the current node.js version is newer than 4, use `fs.watch` instead. + if (isNode4OrLater()) { + return _fs.watch(path, { persisten: true, recursive: !!recursive }, (eventName: string, modifiedPath: string) => callback(modifiedPath)); + } + + // If Node version is older than 4.0, the "recursive" parameter will be ignored + var watchedFile = watchedFileSet.addFile(path, callback); + return { + close: () => watchedFileSet.removeFile(watchedFile) + } + }, resolvePath: function (path: string): string { return _path.resolve(path); }, diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 7ab46fc689e..dc6b8700a3d 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -78,19 +78,19 @@ namespace ts.server { return this.snap().getChangeRange(oldSnapshot); } } - + interface TimestampedResolvedModule extends ResolvedModuleWithFailedLookupLocations { - lastCheckTime: number; + lastCheckTime: number; } - + export class LSHost implements ts.LanguageServiceHost { ls: ts.LanguageService = null; compilationSettings: ts.CompilerOptions; filenameToScript: ts.Map = {}; roots: ScriptInfo[] = []; - private resolvedModuleNames: ts.FileMap>; + private resolvedModuleNames: ts.FileMap>; private moduleResolutionHost: ts.ModuleResolutionHost; - + constructor(public host: ServerHost, public project: Project) { this.resolvedModuleNames = ts.createFileMap>(ts.createGetCanonicalFileName(host.useCaseSensitiveFileNames)) this.moduleResolutionHost = { @@ -98,15 +98,15 @@ namespace ts.server { readFile: fileName => this.host.readFile(fileName) } } - + resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] { let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile); - + let newResolutions: Map = {}; let resolvedModules: ResolvedModule[] = []; - + let compilerOptions = this.getCompilationSettings(); - + for (let moduleName of moduleNames) { // check if this is a duplicate entry in the list let resolution = lookUp(newResolutions, moduleName); @@ -122,21 +122,21 @@ namespace ts.server { newResolutions[moduleName] = resolution; } } - + ts.Debug.assert(resolution !== undefined); - + resolvedModules.push(resolution.resolvedModule); } // replace old results with a new one this.resolvedModuleNames.set(containingFile, newResolutions); return resolvedModules; - + function moduleResolutionIsValid(resolution: TimestampedResolvedModule): boolean { if (!resolution) { return false; } - + if (resolution.resolvedModule) { // TODO: consider checking failedLookupLocations // TODO: use lastCheckTime to track expiration for module name resolution @@ -147,7 +147,7 @@ namespace ts.server { // after all there is no point to invalidate it if we have no idea where to look for the module. return resolution.failedLookupLocations.length === 0; } - } + } getDefaultLibFileName() { var nodeModuleBinDir = ts.getDirectoryPath(ts.normalizePath(this.host.getExecutingFilePath())); @@ -224,7 +224,7 @@ namespace ts.server { this.roots.push(info); } } - + removeRoot(info: ScriptInfo) { var scriptInfo = ts.lookUp(this.filenameToScript, info.fileName); if (scriptInfo) { @@ -354,6 +354,11 @@ namespace ts.server { compilerService: CompilerService; projectFilename: string; projectFileWatcher: FileWatcher; + // Inferred projects have a collection of non-recursive directory watchers starting + // from the root path (e.g. "C:\" or "/") to the current path; + // while configured projects whose tsconfig files don't have a "files" array have one + // recursive directory watcher starting from the current path + directoryWatchers: FileWatcher[] = []; program: ts.Program; filenameToSourceFile: ts.Map = {}; updateGraphSeq = 0; @@ -532,6 +537,41 @@ namespace ts.server { } } + /** + * This is the callback function when the directory that an inferred project belongs + * to changed. The function looks for newly added tsconfig.json files; if it found one, + * and the tsconfig.json file contains the root file of the current inferred project, + * it will update the project structure. + */ + watchedDirectoryChanged(project: Project, path: string) { + if (project.isConfiguredProject()) { + return; + } + + let configFileName = ts.combinePaths(path, "tsconfig.json"); + if (sys.fileExists(configFileName)) { + let {succeeded, projectOptions, error} = this.configFileToProjectOptions(configFileName); + if (!succeeded) { + return; + } + + let newProjectFileNames = projectOptions.files.map(f => this.getCanonicalFileName(f)); + let rootFiles = project.getRootFiles().map(f => this.getCanonicalFileName(f)); + for (let rootFile of rootFiles) { + if (newProjectFileNames.indexOf(rootFile) >= 0) { + this.reloadProjects(); + return; + } + } + + } + } + + getCanonicalFileName(fileName: string) { + let name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); + return ts.normalizePath(name); + } + watchedProjectConfigFileChanged(project: Project) { this.log("Config File Changed: " + project.projectFilename); this.updateConfiguredProject(project); @@ -567,11 +607,19 @@ namespace ts.server { } createInferredProject(root: ScriptInfo) { - var iproj = new Project(this); - iproj.addRoot(root); - iproj.finishGraph(); - this.inferredProjects.push(iproj); - return iproj; + var project = new Project(this); + project.addRoot(root); + + let currentPath = ts.getDirectoryPath(root.fileName); + let parentPath = ts.getDirectoryPath(currentPath); + while (currentPath != parentPath) { + // To finish + let directoryWatcher = this.host.watchDirectory(currentPath, p => this.);; + } + + project.finishGraph(); + this.inferredProjects.push(project); + return project; } fileDeletedInFilesystem(info: ScriptInfo) { @@ -1217,9 +1265,9 @@ namespace ts.server { goSubtree: boolean; done: boolean; leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void; - pre? (relativeStart: number, relativeLength: number, lineCollection: LineCollection, + pre?(relativeStart: number, relativeLength: number, lineCollection: LineCollection, parent: LineNode, nodeType: CharRangeSection): LineCollection; - post? (relativeStart: number, relativeLength: number, lineCollection: LineCollection, + post?(relativeStart: number, relativeLength: number, lineCollection: LineCollection, parent: LineNode, nodeType: CharRangeSection): LineCollection; } From 7fa26adf28b2e628787b84484eead5f87c7db438 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 5 Oct 2015 02:58:40 -0700 Subject: [PATCH 07/34] Redesigned directory watchers --- src/compiler/core.ts | 36 +++++++ src/compiler/sys.ts | 19 +++- src/server/editorServices.ts | 186 ++++++++++++++++++++++++----------- 3 files changed, 182 insertions(+), 59 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ce59c3b3bc6..a4c9a987267 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -700,6 +700,9 @@ namespace ts { } export function getBaseFileName(path: string) { + if (!path) { + return undefined; + } let i = path.lastIndexOf(directorySeparator); return i < 0 ? path : path.substring(i + 1); } @@ -723,6 +726,18 @@ namespace ts { */ export const supportedExtensions = [".ts", ".tsx", ".d.ts"]; + export function isSupportedSourceFileName(fileName: string) { + if (!fileName) { return false; } + + let dotIndex = fileName.lastIndexOf("."); + if (dotIndex < 0) { + return false; + } + + let extension = fileName.slice(dotIndex, fileName.length); + return supportedExtensions.indexOf(extension) >= 0; + } + const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"]; export function removeFileExtension(path: string): string { for (let ext of extensionsToRemove) { @@ -817,4 +832,25 @@ namespace ts { Debug.assert(false, message); } } + + export function doTwoArraysHaveTheSameElements(array1: Array, array2: Array): Boolean { + if (!array1 || !array2) { + return false; + } + + if (array1.length != array2.length) { + return false; + } + + array1 = array1.sort(); + array2 = array2.sort(); + + for (let i = 0; i < array1.length; i++) { + if (array1[i] != array2[i]) { + return false; + } + } + + return true; + } } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 85c674ec904..02728dffaf4 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -398,7 +398,8 @@ namespace ts { // and https://github.com/Microsoft/TypeScript/issues/4643), therefore // if the current node.js version is newer than 4, use `fs.watch` instead. if (isNode4OrLater()) { - return _fs.watch(fileName, (eventName: string, path: string) => callback(path)); + // Note: in node the callback of fs.watch is given only the base file name as a parameter + return _fs.watch(fileName, (eventName: string, baseFileName: string) => callback(fileName)); } var watchedFile = watchedFileSet.addFile(fileName, callback); @@ -410,8 +411,22 @@ namespace ts { // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) // therefore if the current node.js version is newer than 4, use `fs.watch` instead. + + // In watchDirectory we only care about adding and removing files (when event name is + // "rename"); changes made within files are handled by corresponding fileWatchers (when + // event name is "change") + if (isNode4OrLater()) { - return _fs.watch(path, { persisten: true, recursive: !!recursive }, (eventName: string, modifiedPath: string) => callback(modifiedPath)); + return _fs.watch( + path, + { persisten: true, recursive: !!recursive }, + (eventName: string, relativeFileName: string) => { + if (eventName == "rename") { + // when deleting a file, the passed baseFileName is null + callback(relativeFileName == null ? null : ts.combinePaths(path, ts.normalizeSlashes(relativeFileName))) + }; + } + ); } // If Node version is older than 4.0, the "recursive" parameter will be ignored diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index dc6b8700a3d..80cebe665ae 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -354,11 +354,9 @@ namespace ts.server { compilerService: CompilerService; projectFilename: string; projectFileWatcher: FileWatcher; - // Inferred projects have a collection of non-recursive directory watchers starting - // from the root path (e.g. "C:\" or "/") to the current path; - // while configured projects whose tsconfig files don't have a "files" array have one - // recursive directory watcher starting from the current path - directoryWatchers: FileWatcher[] = []; + directoryWatcher: FileWatcher; + // Used to keep track of what directories are watched for this project + directoriesWatchedForTsconfig: string[] = []; program: ts.Program; filenameToSourceFile: ts.Map = {}; updateGraphSeq = 0; @@ -382,6 +380,10 @@ namespace ts.server { return this.projectService.openFile(filename, false); } + getRootFiles() { + return this.compilerService.host.roots.map(info => info.fileName); + } + getFileNames() { let sourceFiles = this.program.getSourceFiles(); return sourceFiles.map(sourceFile => sourceFile.fileName); @@ -434,13 +436,11 @@ namespace ts.server { // add a root file to project addRoot(info: ScriptInfo) { - info.defaultProject = this; this.compilerService.host.addRoot(info); } // remove a root file from project removeRoot(info: ScriptInfo) { - info.defaultProject = undefined; this.compilerService.host.removeRoot(info); } @@ -496,6 +496,11 @@ namespace ts.server { openFilesReferenced: ScriptInfo[] = []; // open files that are roots of a configured project openFileRootsConfigured: ScriptInfo[] = []; + // a path to directory watcher map that detects added tsconfig files + directoryWatchersForTsconfig: ts.Map = {}; + // count of how many projects are using the directory watcher. If the + // number becomes 0 for a watcher, then we should close it. + directoryWatchersRefCount: ts.Map = {}; hostConfiguration: HostConfiguration; constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) { @@ -538,32 +543,53 @@ namespace ts.server { } /** - * This is the callback function when the directory that an inferred project belongs - * to changed. The function looks for newly added tsconfig.json files; if it found one, - * and the tsconfig.json file contains the root file of the current inferred project, - * it will update the project structure. + * This is the callback function when a watched directory has added or removed files. + * @param project the project that associates with this directory watcher + * @param fileName the absolute file name that changed in watched directory */ - watchedDirectoryChanged(project: Project, path: string) { - if (project.isConfiguredProject()) { + directoryWatchedForSourceFilesChanged(project: Project, fileName: string) { + // If a change was made inside "folder/file", node will trigger the callback twice: + // one with the fileName being "folder/file", and the other one with "folder". + // We don't respond to the second one. + if (fileName && !ts.isSupportedSourceFileName(fileName)) { return; } - let configFileName = ts.combinePaths(path, "tsconfig.json"); - if (sys.fileExists(configFileName)) { - let {succeeded, projectOptions, error} = this.configFileToProjectOptions(configFileName); - if (!succeeded) { + this.log("Detected source file changes: " + fileName); + + let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename); + let newRootFiles = projectOptions.files.map(f => this.getCanonicalFileName(f)); + let currentRootFiles = project.getRootFiles().map(f => this.getCanonicalFileName(f)); + + if (!doTwoArraysHaveTheSameElements(currentRootFiles, newRootFiles)) { + // For configured projects, the change is made outside the tsconfig file, and + // it is not likely to affect the project for other files opened by the client. We can + // just update the current project. + this.updateConfiguredProject(project); + + // Call updateProjectStructure to clean up inferred projects we may have created for the + // new files + this.updateProjectStructure(); + } + } + + directoryWatchedForTsconfigChanged(fileName: string) { + if (ts.getBaseFileName(fileName) != "tsconfig.json") { + this.log(fileName + " is not tsconfig.json"); + return; + } + + this.log("Detected newly added tsconfig file: " + fileName); + + let { succeeded, projectOptions, error } = this.configFileToProjectOptions(fileName); + let rootFilesInTsconfig = projectOptions.files.map(f => this.getCanonicalFileName(f)); + let openFileRoots = this.openFileRoots.map(s => this.getCanonicalFileName(s.fileName)); + + for (let openFileRoot of openFileRoots) { + if (rootFilesInTsconfig.indexOf(openFileRoot) >= 0) { + this.reloadProjects(); return; } - - let newProjectFileNames = projectOptions.files.map(f => this.getCanonicalFileName(f)); - let rootFiles = project.getRootFiles().map(f => this.getCanonicalFileName(f)); - for (let rootFile of rootFiles) { - if (newProjectFileNames.indexOf(rootFile) >= 0) { - this.reloadProjects(); - return; - } - } - } } @@ -573,7 +599,7 @@ namespace ts.server { } watchedProjectConfigFileChanged(project: Project) { - this.log("Config File Changed: " + project.projectFilename); + this.log("Config file changed: " + project.projectFilename); this.updateConfiguredProject(project); this.updateProjectStructure(); } @@ -613,8 +639,18 @@ namespace ts.server { let currentPath = ts.getDirectoryPath(root.fileName); let parentPath = ts.getDirectoryPath(currentPath); while (currentPath != parentPath) { - // To finish - let directoryWatcher = this.host.watchDirectory(currentPath, p => this.);; + if (!project.projectService.directoryWatchersForTsconfig[currentPath]) { + this.log("Add watcher for: " + currentPath); + project.projectService.directoryWatchersForTsconfig[currentPath] = + this.host.watchDirectory(currentPath, fileName => this.directoryWatchedForTsconfigChanged(fileName)); + project.projectService.directoryWatchersRefCount[currentPath] = 1; + } + else { + project.projectService.directoryWatchersRefCount[currentPath] += 1; + } + project.directoriesWatchedForTsconfig.push(currentPath); + currentPath = parentPath; + parentPath = ts.getDirectoryPath(parentPath); } project.finishGraph(); @@ -663,9 +699,23 @@ namespace ts.server { this.configuredProjects = configuredProjects; } - removeConfiguredProject(project: Project) { - project.projectFileWatcher.close(); - this.configuredProjects = copyListRemovingItem(project, this.configuredProjects); + removeProject(project: Project) { + this.log("remove project: " + project.getRootFiles().toString()); + if (project.isConfiguredProject()) { + project.projectFileWatcher.close(); + project.directoryWatcher.close(); + this.configuredProjects = copyListRemovingItem(project, this.configuredProjects); + } + else { + for (let directory of project.directoriesWatchedForTsconfig) { + if (!(--project.projectService.directoryWatchersRefCount[directory])) { + this.log("Close directory watcher for: " + directory); + project.projectService.directoryWatchersForTsconfig[directory].close(); + project.projectService.directoryWatchersForTsconfig[directory] = undefined; + } + } + this.inferredProjects = copyListRemovingItem(project, this.inferredProjects); + } let fileNames = project.getFileNames(); for (let fileName of fileNames) { @@ -707,8 +757,7 @@ namespace ts.server { // if r referenced by the new project if (info.defaultProject.getSourceFile(r)) { // remove project rooted at r - this.inferredProjects = - copyListRemovingItem(r.defaultProject, this.inferredProjects); + this.removeProject(r.defaultProject); // put r in referenced open file list this.openFilesReferenced.push(r); // set default project of r to the new project @@ -761,19 +810,14 @@ namespace ts.server { this.openFileRootsConfigured = openFileRootsConfigured; } if (removedProject) { - if (removedProject.isConfiguredProject()) { - this.configuredProjects = copyListRemovingItem(removedProject, this.configuredProjects); - } - else { - this.inferredProjects = copyListRemovingItem(removedProject, this.inferredProjects); - } + this.removeProject(removedProject); var openFilesReferenced: ScriptInfo[] = []; var orphanFiles: ScriptInfo[] = []; // for all open, referenced files f for (var i = 0, len = this.openFilesReferenced.length; i < len; i++) { var f = this.openFilesReferenced[i]; // if f was referenced by the removed project, remember it - if (f.defaultProject === removedProject) { + if (f.defaultProject === removedProject || !f.defaultProject) { f.defaultProject = undefined; orphanFiles.push(f); } @@ -817,7 +861,11 @@ namespace ts.server { return referencingProjects; } + /** + * This function rebuilds the project for every file opened by the client + */ reloadProjects() { + this.log("reload projects."); // First check if there is new tsconfig file added for inferred project roots for (let info of this.openFileRoots) { this.openOrUpdateConfiguredProjectForFile(info.fileName); @@ -878,14 +926,25 @@ namespace ts.server { var rootFile = this.openFileRoots[i]; var rootedProject = rootFile.defaultProject; var referencingProjects = this.findReferencingProjects(rootFile, rootedProject); - if (referencingProjects.length === 0) { - rootFile.defaultProject = rootedProject; - openFileRoots.push(rootFile); + + if (rootFile.defaultProject.isConfiguredProject()) { + // If the root file has already been added into a configured project, + // meaning the original inferred project is gone already. + if (!rootedProject.isConfiguredProject()) { + this.removeProject(rootedProject); + } + this.openFileRootsConfigured.push(rootFile); } else { - // remove project from inferred projects list because root captured - this.inferredProjects = copyListRemovingItem(rootedProject, this.inferredProjects); - this.openFilesReferenced.push(rootFile); + if (referencingProjects.length === 0) { + rootFile.defaultProject = rootedProject; + openFileRoots.push(rootFile); + } + else { + // remove project from inferred projects list because root captured + this.removeProject(rootedProject); + this.openFilesReferenced.push(rootFile); + } } } this.openFileRoots = openFileRoots; @@ -897,6 +956,9 @@ namespace ts.server { this.addOpenFile(unattachedOpenFiles[i]); } this.printProjects(); + + this.log("Current openFileRoots: " + this.openFileRoots.map(s => s.fileName).toString()); + this.log("Current openFileRootsConfigured: " + this.openFileRootsConfigured.map(s => s.fileName).toString()); } getScriptInfo(filename: string) { @@ -970,6 +1032,11 @@ namespace ts.server { return info; } + /** + * This function tries to search for a tsconfig.json for the given file. If we found it, + * we first detect if there is already a configured project created for it: if so, we re-read + * the tsconfig file content and update the project; otherwise we create a new one. + */ openOrUpdateConfiguredProjectForFile(fileName: string) { let searchPath = ts.normalizePath(getDirectoryPath(fileName)); this.log("Search path: " + searchPath, "Info"); @@ -1099,7 +1166,7 @@ namespace ts.server { return { succeeded: false, error: { errorMsg: "tsconfig option errors" } }; } else if (parsedCommandLine.fileNames == null) { - return { succeeded: false, error: { errorMsg: "no files found" } } + return { succeeded: false, error: { errorMsg: "no files found" } }; } else { var projectOptions: ProjectOptions = { @@ -1118,27 +1185,32 @@ namespace ts.server { return error; } else { - let proj = this.createProject(configFilename, projectOptions); - for (let i = 0, len = projectOptions.files.length; i < len; i++) { - let rootFilename = projectOptions.files[i]; + let project = this.createProject(configFilename, projectOptions); + for (let rootFilename of projectOptions.files) { if (this.host.fileExists(rootFilename)) { let info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename); - proj.addRoot(info); + project.addRoot(info); } else { return { errorMsg: "specified file " + rootFilename + " not found" }; } } - proj.finishGraph(); - proj.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(proj)); - return { success: true, project: proj }; + project.finishGraph(); + project.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(project)); + this.log("Add recursive watcher for: " + ts.getDirectoryPath(configFilename)); + project.directoryWatcher = this.host.watchDirectory( + ts.getDirectoryPath(configFilename), + path => this.directoryWatchedForSourceFilesChanged(project, path), + /*recursive*/ true + ); + return { success: true, project: project }; } } updateConfiguredProject(project: Project) { if (!this.host.fileExists(project.projectFilename)) { this.log("Config file deleted"); - this.removeConfiguredProject(project); + this.removeProject(project); } else { let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename); From 9db53f23cfffd88067ce241f0dc4841359c0a760 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 5 Oct 2015 13:12:13 -0700 Subject: [PATCH 08/34] Add directory watcher to tsc --- src/compiler/sys.ts | 2 +- src/compiler/tsc.ts | 57 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 02728dffaf4..840e8de8271 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -423,7 +423,7 @@ namespace ts { (eventName: string, relativeFileName: string) => { if (eventName == "rename") { // when deleting a file, the passed baseFileName is null - callback(relativeFileName == null ? null : ts.combinePaths(path, ts.normalizeSlashes(relativeFileName))) + callback(relativeFileName == null ? null : normalizePath(ts.combinePaths(path, relativeFileName))) }; } ); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 02b8e636772..d5682d8860e 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -149,6 +149,7 @@ namespace ts { let commandLine = parseCommandLine(args); let configFileName: string; // Configuration file name (if any) let configFileWatcher: FileWatcher; // Configuration file watcher + let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal let cachedProgram: Program; // Program cached from last compilation let rootFileNames: string[]; // Root fileNames for compilation let compilerOptions: CompilerOptions; // Compiler options for compilation @@ -218,28 +219,43 @@ namespace ts { if (configFileName) { configFileWatcher = sys.watchFile(configFileName, configFileChanged); } + if (sys.watchDirectory && configFileName) { + let directory = ts.getDirectoryPath(configFileName); + directoryWatcher = sys.watchDirectory( + // When the configFileName is just "tsconfig.json", the watched directory should be + // the current direcotry; if there is a given "project" parameter, then the configFileName + // is an absolute file name. + directory == "" ? "." : directory, + watchedDirectoryChanged, /*recursive*/ true); + } } performCompilation(); + function configFileToParsedCommandLine(configFilename: string): ParsedCommandLine { + let result = readConfigFile(configFileName, sys.readFile); + if (result.error) { + reportWatchDiagnostic(result.error); + sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return; + } + + let configObject = result.config; + let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); + if (configParseResult.errors.length > 0) { + reportDiagnostics(configParseResult.errors); + sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return; + } + return configParseResult; + } + // Invoked to perform initial compilation or re-compilation in watch mode function performCompilation() { if (!cachedProgram) { if (configFileName) { - - let result = readConfigFile(configFileName, sys.readFile); - if (result.error) { - reportWatchDiagnostic(result.error); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - let configObject = result.config; - let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); - if (configParseResult.errors.length > 0) { - reportDiagnostics(configParseResult.errors); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } + let configParseResult = configFileToParsedCommandLine(configFileName); rootFileNames = configParseResult.fileNames; compilerOptions = extend(commandLine.options, configParseResult.options); } @@ -309,6 +325,21 @@ namespace ts { startTimer(); } + function watchedDirectoryChanged(fileName: string) { + if (fileName && !ts.isSupportedSourceFileName(fileName)) { + return; + } + + let parsedCommandLine = configFileToParsedCommandLine(configFileName); + let newFileNames = parsedCommandLine.fileNames.map(compilerHost.getCanonicalFileName); + let canonicalRootFileNames = rootFileNames.map(compilerHost.getCanonicalFileName); + + if (!doTwoArraysHaveTheSameElements(newFileNames, canonicalRootFileNames)) { + setCachedProgram(undefined); + startTimer(); + } + } + // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch // operations (such as saving all modified files in an editor) a chance to complete before we kick // off a new compilation. From 7741ec09999af8c387116a80069f09766c28a240 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 5 Oct 2015 14:07:51 -0700 Subject: [PATCH 09/34] Use fs.watch for all directory watchers and some bug fixes --- src/compiler/sys.ts | 37 +++++++++++++----------------------- src/server/editorServices.ts | 2 +- src/server/session.ts | 1 + 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 840e8de8271..55235e3eda0 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -410,30 +410,19 @@ namespace ts { watchDirectory: (path, callback, recursive) => { // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) - // therefore if the current node.js version is newer than 4, use `fs.watch` instead. - - // In watchDirectory we only care about adding and removing files (when event name is - // "rename"); changes made within files are handled by corresponding fileWatchers (when - // event name is "change") - - if (isNode4OrLater()) { - return _fs.watch( - path, - { persisten: true, recursive: !!recursive }, - (eventName: string, relativeFileName: string) => { - if (eventName == "rename") { - // when deleting a file, the passed baseFileName is null - callback(relativeFileName == null ? null : normalizePath(ts.combinePaths(path, relativeFileName))) - }; - } - ); - } - - // If Node version is older than 4.0, the "recursive" parameter will be ignored - var watchedFile = watchedFileSet.addFile(path, callback); - return { - close: () => watchedFileSet.removeFile(watchedFile) - } + return _fs.watch( + path, + { persisten: true, recursive: !!recursive }, + (eventName: string, relativeFileName: string) => { + // In watchDirectory we only care about adding and removing files (when event name is + // "rename"); changes made within files are handled by corresponding fileWatchers (when + // event name is "change") + if (eventName == "rename") { + // When deleting a file, the passed baseFileName is null + callback(relativeFileName == null ? null : normalizePath(ts.combinePaths(path, relativeFileName))) + }; + } + ); }, resolvePath: function (path: string): string { return _path.resolve(path); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 80cebe665ae..49ddd3a1a4c 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -927,7 +927,7 @@ namespace ts.server { var rootedProject = rootFile.defaultProject; var referencingProjects = this.findReferencingProjects(rootFile, rootedProject); - if (rootFile.defaultProject.isConfiguredProject()) { + if (rootFile.defaultProject && rootFile.defaultProject.isConfiguredProject()) { // If the root file has already been added into a configured project, // meaning the original inferred project is gone already. if (!rootedProject.isConfiguredProject()) { diff --git a/src/server/session.ts b/src/server/session.ts index da044e7b4c6..edc79838c92 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -779,6 +779,7 @@ namespace ts.server { } private closeClientFile(fileName: string) { + if (!fileName) { return; } var file = ts.normalizePath(fileName); this.projectService.closeClientFile(file); } From 17f0cce7724e52da55db0b5903c8dfc46d35fc84 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 5 Oct 2015 14:31:43 -0700 Subject: [PATCH 10/34] Update comments --- src/compiler/sys.ts | 4 ++-- src/server/editorServices.ts | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 55235e3eda0..1b16efe4a7a 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -398,8 +398,8 @@ namespace ts { // and https://github.com/Microsoft/TypeScript/issues/4643), therefore // if the current node.js version is newer than 4, use `fs.watch` instead. if (isNode4OrLater()) { - // Note: in node the callback of fs.watch is given only the base file name as a parameter - return _fs.watch(fileName, (eventName: string, baseFileName: string) => callback(fileName)); + // Note: in node the callback of fs.watch is given only the relative file name as a parameter + return _fs.watch(fileName, (eventName: string, relativeFileName: string) => callback(fileName)); } var watchedFile = watchedFileSet.addFile(fileName, callback); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 49ddd3a1a4c..974f51906f6 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -543,7 +543,7 @@ namespace ts.server { } /** - * This is the callback function when a watched directory has added or removed files. + * This is the callback function when a watched directory has added or removed source code files. * @param project the project that associates with this directory watcher * @param fileName the absolute file name that changed in watched directory */ @@ -567,12 +567,15 @@ namespace ts.server { // just update the current project. this.updateConfiguredProject(project); - // Call updateProjectStructure to clean up inferred projects we may have created for the - // new files + // Call updateProjectStructure to clean up inferred projects we may have + // created for the new files this.updateProjectStructure(); } } + /** + * This is the callback function when a watched directory has an added tsconfig file. + */ directoryWatchedForTsconfigChanged(fileName: string) { if (ts.getBaseFileName(fileName) != "tsconfig.json") { this.log(fileName + " is not tsconfig.json"); @@ -585,6 +588,8 @@ namespace ts.server { let rootFilesInTsconfig = projectOptions.files.map(f => this.getCanonicalFileName(f)); let openFileRoots = this.openFileRoots.map(s => this.getCanonicalFileName(s.fileName)); + // We should only care about the new tsconfig file if it contains any + // opened root files of existing inferred projects for (let openFileRoot of openFileRoots) { if (rootFilesInTsconfig.indexOf(openFileRoot) >= 0) { this.reloadProjects(); @@ -708,6 +713,7 @@ namespace ts.server { } else { for (let directory of project.directoriesWatchedForTsconfig) { + // if the ref count for this directory watcher drops to 0, it's time to close it if (!(--project.projectService.directoryWatchersRefCount[directory])) { this.log("Close directory watcher for: " + directory); project.projectService.directoryWatchersForTsconfig[directory].close(); @@ -956,9 +962,6 @@ namespace ts.server { this.addOpenFile(unattachedOpenFiles[i]); } this.printProjects(); - - this.log("Current openFileRoots: " + this.openFileRoots.map(s => s.fileName).toString()); - this.log("Current openFileRootsConfigured: " + this.openFileRootsConfigured.map(s => s.fileName).toString()); } getScriptInfo(filename: string) { From f7e35d597589af03fcffdecb74061a27f3538bb5 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 5 Oct 2015 16:19:09 -0700 Subject: [PATCH 11/34] Incorporating changes from #3780 --- src/compiler/sys.ts | 8 ++++---- src/compiler/tsc.ts | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 1b16efe4a7a..e8ad3c309fd 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -8,7 +8,7 @@ namespace ts { write(s: string): void; readFile(path: string, encoding?: string): string; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - watchFile?(path: string, callback: (path: string) => void): FileWatcher; + watchFile?(path: string, callback: (path: string, removed?: boolean) => void): FileWatcher; watchDirectory?(path: string, callback: (path: string) => void, recursive?: boolean): FileWatcher; resolvePath(path: string): string; fileExists(path: string): boolean; @@ -23,7 +23,7 @@ namespace ts { interface WatchedFile { fileName: string; - callback: (fileName: string) => void; + callback: (fileName: string, removed?: boolean) => void; mtime: Date; } @@ -235,7 +235,7 @@ namespace ts { } else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName); - watchedFile.callback(watchedFile.fileName); + watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0); } }); } @@ -263,7 +263,7 @@ namespace ts { }, this.interval); } - addFile(fileName: string, callback: (fileName: string) => void): WatchedFile { + addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile { var file: WatchedFile = { fileName, callback, diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index d5682d8860e..a6d51542989 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -291,7 +291,7 @@ namespace ts { let sourceFile = hostGetSourceFile(fileName, languageVersion, onError); if (sourceFile && compilerOptions.watch) { // Attach a file watcher - sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, () => sourceFileChanged(sourceFile)); + sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (fileName: string, removed?: boolean) => sourceFileChanged(sourceFile, removed)); } return sourceFile; } @@ -313,9 +313,15 @@ namespace ts { } // If a source file changes, mark it as unwatched and start the recompilation timer - function sourceFileChanged(sourceFile: SourceFile) { + function sourceFileChanged(sourceFile: SourceFile, removed?: boolean) { sourceFile.fileWatcher.close(); sourceFile.fileWatcher = undefined; + if (removed) { + var index = rootFileNames.indexOf(sourceFile.fileName); + if (index !== -1) { + rootFileNames.splice(index, 1); + } + } startTimer(); } From 8171dede90ba326575cc992d584c1a98a7695a60 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 6 Oct 2015 12:33:06 -0700 Subject: [PATCH 12/34] Lint fixes and test fixes --- src/compiler/sys.ts | 20 ++++++++++---------- src/harness/harnessLanguageService.ts | 8 +++++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index e8ad3c309fd..d191d406c52 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -210,7 +210,7 @@ namespace ts { } private static copyListRemovingItem(item: T, list: T[]) { - var copiedList: T[] = []; + let copiedList: T[] = []; for (var i = 0, len = list.length; i < len; i++) { if (list[i] != item) { copiedList.push(list[i]); @@ -224,7 +224,7 @@ namespace ts { } private poll(checkedIndex: number) { - var watchedFile = this.watchedFiles[checkedIndex]; + let watchedFile = this.watchedFiles[checkedIndex]; if (!watchedFile) { return; } @@ -245,9 +245,9 @@ namespace ts { // and efficiency of stat on modern filesystems private startWatchTimer() { this.watchTimer = setInterval(() => { - var count = 0; - var nextToCheck = this.nextFileToCheck; - var firstCheck = -1; + let count = 0; + let nextToCheck = this.nextFileToCheck; + let firstCheck = -1; while ((count < this.chunkSize) && (nextToCheck !== firstCheck)) { this.poll(nextToCheck); if (firstCheck < 0) { @@ -264,7 +264,7 @@ namespace ts { } addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile { - var file: WatchedFile = { + let file: WatchedFile = { fileName, callback, mtime: WatchedFileSet.getModifiedTime(fileName) @@ -295,7 +295,7 @@ namespace ts { // changes for large reference sets? If so, do we want // to increase the chunk size or decrease the interval // time dynamically to match the large reference set? - var watchedFileSet = new WatchedFileSet(); + let watchedFileSet = new WatchedFileSet(); function isNode4OrLater(): Boolean { return parseInt(process.version.charAt(1)) >= 4; @@ -402,10 +402,10 @@ namespace ts { return _fs.watch(fileName, (eventName: string, relativeFileName: string) => callback(fileName)); } - var watchedFile = watchedFileSet.addFile(fileName, callback); + let watchedFile = watchedFileSet.addFile(fileName, callback); return { close: () => watchedFileSet.removeFile(watchedFile) - } + }; }, watchDirectory: (path, callback, recursive) => { // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows @@ -419,7 +419,7 @@ namespace ts { // event name is "change") if (eventName == "rename") { // When deleting a file, the passed baseFileName is null - callback(relativeFileName == null ? null : normalizePath(ts.combinePaths(path, relativeFileName))) + callback(!relativeFileName ? relativeFileName : normalizePath(ts.combinePaths(path, relativeFileName))); }; } ); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index dfde2bd1c08..df5efccdcb5 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -572,6 +572,10 @@ namespace Harness.LanguageService { return { close() { } }; } + watchDirectory(path: string, callback: (path: string) => void, recursive?: boolean): ts.FileWatcher { + return { close() { } }; + } + close(): void { } @@ -614,7 +618,9 @@ namespace Harness.LanguageService { // This host is just a proxy for the clientHost, it uses the client // host to answer server queries about files on disk let serverHost = new SessionServerHost(clientHost); - let server = new ts.server.Session(serverHost, Buffer.byteLength, process.hrtime, serverHost); + let server = new ts.server.Session(serverHost, + Buffer ? Buffer.byteLength : (string: string, encoding?: string) => string.length, + process.hrtime, serverHost); // Fake the connection between the client and the server serverHost.writeMessage = client.onMessage.bind(client); From 9ed5b4c435825cdc70643bfb8d391416d2c1ec2c Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 6 Oct 2015 13:10:03 -0700 Subject: [PATCH 13/34] more test fixes --- tests/cases/unittests/cachingInServerLSHost.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/cases/unittests/cachingInServerLSHost.ts b/tests/cases/unittests/cachingInServerLSHost.ts index ff2da76ba4f..88b44a693b9 100644 --- a/tests/cases/unittests/cachingInServerLSHost.ts +++ b/tests/cases/unittests/cachingInServerLSHost.ts @@ -45,6 +45,11 @@ module ts { return { close: () => { } } + }, + watchDirectory: (path, callback, recursive?) => { + return { + close: () => { } + } } }; } From 88bffac07ff5288531c139f221576fc4235e4923 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 8 Oct 2015 14:26:43 -0700 Subject: [PATCH 14/34] Don't issue completion in JSX text Fixes #5096 --- src/services/services.ts | 15 ++++++++++++++- tests/cases/fourslash/tsxCompletion9.ts | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/tsxCompletion9.ts diff --git a/src/services/services.ts b/src/services/services.ts index 9a6afb5f1e0..3eb5ec319ad 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3311,11 +3311,24 @@ namespace ts { let start = new Date().getTime(); let result = isInStringOrRegularExpressionOrTemplateLiteral(contextToken) || isSolelyIdentifierDefinitionLocation(contextToken) || - isDotOfNumericLiteral(contextToken); + isDotOfNumericLiteral(contextToken) || + isInJsxText(contextToken); log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start)); return result; } + function isInJsxText(contextToken: Node): boolean { + if (contextToken.kind === SyntaxKind.JsxText) { + return true; + } + + return contextToken.kind === SyntaxKind.GreaterThanToken && + contextToken.parent && + (contextToken.parent.kind === SyntaxKind.JsxOpeningElement || + contextToken.parent.kind === SyntaxKind.JsxSelfClosingElement || + contextToken.parent.kind === SyntaxKind.JsxClosingElement); + } + function isNewIdentifierDefinitionLocation(previousToken: Node): boolean { if (previousToken) { let containingNodeKind = previousToken.parent.kind; diff --git a/tests/cases/fourslash/tsxCompletion9.ts b/tests/cases/fourslash/tsxCompletion9.ts new file mode 100644 index 00000000000..dbb6c46293b --- /dev/null +++ b/tests/cases/fourslash/tsxCompletion9.ts @@ -0,0 +1,18 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { ONE: string; TWO: number; } +//// } +//// } +//// var x1 =
/*1*/ hello /*2*/ world /*3*/
; +//// var x2 =
/*4*/
/*5*/ world /*6*/
; +//// var x3 =
/*7*/
/*8*/world/*9*/
; +//// var x4 =
/*10*/
; + +for (var i = 1; i <= 10; i++) { + goTo.marker(i + ''); + verify.completionListIsEmpty(); +} From b1c83033007657b32d8ee777130357447700feed Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 8 Oct 2015 14:55:11 -0700 Subject: [PATCH 15/34] Fix case for completion on the line after a self-closing element --- src/services/services.ts | 15 ++++++++++----- tests/cases/fourslash/tsxCompletion9.ts | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 3eb5ec319ad..0fd76eff61f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3322,11 +3322,16 @@ namespace ts { return true; } - return contextToken.kind === SyntaxKind.GreaterThanToken && - contextToken.parent && - (contextToken.parent.kind === SyntaxKind.JsxOpeningElement || - contextToken.parent.kind === SyntaxKind.JsxSelfClosingElement || - contextToken.parent.kind === SyntaxKind.JsxClosingElement); + if (contextToken.kind === SyntaxKind.GreaterThanToken && contextToken.parent) { + if (contextToken.parent.kind === SyntaxKind.JsxOpeningElement) { + return true; + } + + if (contextToken.parent.kind === SyntaxKind.JsxClosingElement || contextToken.parent.kind === SyntaxKind.JsxSelfClosingElement) { + return contextToken.parent.parent && contextToken.parent.parent.kind === SyntaxKind.JsxElement; + } + } + return false; } function isNewIdentifierDefinitionLocation(previousToken: Node): boolean { diff --git a/tests/cases/fourslash/tsxCompletion9.ts b/tests/cases/fourslash/tsxCompletion9.ts index dbb6c46293b..12542cc6b5b 100644 --- a/tests/cases/fourslash/tsxCompletion9.ts +++ b/tests/cases/fourslash/tsxCompletion9.ts @@ -11,8 +11,14 @@ //// var x2 =
/*4*/
/*5*/ world /*6*/
; //// var x3 =
/*7*/
/*8*/world/*9*/
; //// var x4 =
/*10*/
; +////
+//// /*end*/ +//// for (var i = 1; i <= 10; i++) { goTo.marker(i + ''); verify.completionListIsEmpty(); } + +goTo.marker('end'); +verify.not.completionListIsEmpty(); From 1e708b46a7251ea9a8b6b4b8c5c1d925e098fed6 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 8 Oct 2015 15:31:59 -0700 Subject: [PATCH 16/34] Implement not.greaterThan for completion list --- src/harness/fourslash.ts | 13 ++++++++++--- tests/cases/fourslash/fourslash.ts | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a846077a85f..2b6f6ee8d0e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -588,14 +588,21 @@ namespace FourSlash { } } - public verifyCompletionListItemsCountIsGreaterThan(count: number) { + public verifyCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) { this.taoInvalidReason = "verifyCompletionListItemsCountIsGreaterThan NYI"; let completions = this.getCompletionListAtCaret(); let itemsCount = completions.entries.length; - if (itemsCount <= count) { - this.raiseError(`Expected completion list items count to be greater than ${count}, but is actually ${itemsCount}`); + if (negative) { + if (itemsCount > count) { + this.raiseError(`Expected completion list items count to not be greater than ${count}, but is actually ${itemsCount}`); + } + } + else { + if (itemsCount <= count) { + this.raiseError(`Expected completion list items count to be greater than ${count}, but is actually ${itemsCount}`); + } } } diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 42cfc1248b0..8d1ee35dc1a 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -205,7 +205,7 @@ module FourSlashInterface { // Verifies the completion list items count to be greater than the specified amount. The // completion list is brought up if necessary public completionListItemsCountIsGreaterThan(count: number) { - FourSlash.currentTestState.verifyCompletionListItemsCountIsGreaterThan(count); + FourSlash.currentTestState.verifyCompletionListItemsCountIsGreaterThan(count, this.negative); } public completionListIsEmpty() { From 24334b506c28b58ba3c30528b5044cd4db05e778 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 8 Oct 2015 15:32:36 -0700 Subject: [PATCH 17/34] Only show the opening tag name when completing a close tag Fixes #5096 --- src/services/services.ts | 19 ++++++++++++++++--- tests/cases/fourslash/tsxCompletion10.ts | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/tsxCompletion10.ts diff --git a/src/services/services.ts b/src/services/services.ts index 0fd76eff61f..84794e95a6e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3105,6 +3105,7 @@ namespace ts { let node = currentToken; let isRightOfDot = false; let isRightOfOpenTag = false; + let isStartingCloseTag = false; let location = getTouchingPropertyName(sourceFile, position); if (contextToken) { @@ -3130,9 +3131,14 @@ namespace ts { return undefined; } } - else if (kind === SyntaxKind.LessThanToken && sourceFile.languageVariant === LanguageVariant.JSX) { - isRightOfOpenTag = true; - location = contextToken; + else if (sourceFile.languageVariant === LanguageVariant.JSX) { + if (kind === SyntaxKind.LessThanToken) { + isRightOfOpenTag = true; + location = contextToken; + } + else if (kind === SyntaxKind.SlashToken && contextToken.parent.kind === SyntaxKind.JsxClosingElement) { + isStartingCloseTag = true; + } } } @@ -3155,6 +3161,13 @@ namespace ts { isMemberCompletion = true; isNewIdentifierLocation = false; } + else if (isStartingCloseTag) { + let tagName = (contextToken.parent.parent).openingElement.tagName; + symbols = [typeChecker.getSymbolAtLocation(tagName)]; + + isMemberCompletion = true; + isNewIdentifierLocation = false; + } else { // For JavaScript or TypeScript, if we're not after a dot, then just try to get the // global symbols in scope. These results should be valid for either language as diff --git a/tests/cases/fourslash/tsxCompletion10.ts b/tests/cases/fourslash/tsxCompletion10.ts new file mode 100644 index 00000000000..cf8b3068f10 --- /dev/null +++ b/tests/cases/fourslash/tsxCompletion10.ts @@ -0,0 +1,15 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { ONE: string; TWO: number; } +//// } +//// } +//// var x1 =
Date: Mon, 12 Oct 2015 14:27:33 -0700 Subject: [PATCH 18/34] Remove --experimentalAsyncFunctions --- src/compiler/checker.ts | 4 ---- src/compiler/commandLineParser.ts | 5 ----- src/compiler/diagnosticMessages.json | 8 -------- src/compiler/program.ts | 5 ----- src/compiler/types.ts | 1 - 5 files changed, 23 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 903addec182..110332b6224 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11759,10 +11759,6 @@ namespace ts { checkSignatureDeclaration(node); let isAsync = isAsyncFunctionLike(node); if (isAsync) { - if (!compilerOptions.experimentalAsyncFunctions) { - error(node, Diagnostics.Experimental_support_for_async_functions_is_a_feature_that_is_subject_to_change_in_a_future_release_Specify_experimentalAsyncFunctions_to_remove_this_warning); - } - emitAwaiter = true; } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 731a37a8cb7..9a8c0ac416f 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -222,11 +222,6 @@ namespace ts { type: "boolean", description: Diagnostics.Watch_input_files, }, - { - name: "experimentalAsyncFunctions", - type: "boolean", - description: Diagnostics.Enables_experimental_support_for_ES7_async_functions - }, { name: "experimentalDecorators", type: "boolean", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b391545a733..fda876fa36b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -783,10 +783,6 @@ "category": "Error", "code": 1245 }, - "Experimental support for async functions is a feature that is subject to change in a future release. Specify '--experimentalAsyncFunctions' to remove this warning.": { - "category": "Error", - "code": 1246 - }, "'with' statements are not allowed in an async function block.": { "category": "Error", @@ -2282,10 +2278,6 @@ "category": "Message", "code": 6066 }, - "Option 'experimentalAsyncFunctions' cannot be specified when targeting ES5 or lower.": { - "category": "Message", - "code": 6067 - }, "Enables experimental support for ES7 async functions.": { "category": "Message", "code": 6068 diff --git a/src/compiler/program.ts b/src/compiler/program.ts index c6d3a245a8d..abef5088fbb 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1076,11 +1076,6 @@ namespace ts { !options.experimentalDecorators) { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators")); } - - if (options.experimentalAsyncFunctions && - options.target !== ScriptTarget.ES6) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_experimentalAsyncFunctions_cannot_be_specified_when_targeting_ES5_or_lower)); - } } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cbaa3e65e27..2de545ce32f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2080,7 +2080,6 @@ namespace ts { watch?: boolean; isolatedModules?: boolean; experimentalDecorators?: boolean; - experimentalAsyncFunctions?: boolean; emitDecoratorMetadata?: boolean; moduleResolution?: ModuleResolutionKind; /* @internal */ stripInternal?: boolean; From e9bed76baaaeca2c7593adbb3315cb0dde7d014c Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 12 Oct 2015 14:27:58 -0700 Subject: [PATCH 19/34] Remove --experimentalAsyncFunctions from tests --- .../async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts | 1 - .../async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts | 1 - .../asyncArrowFunctionCapturesArguments_es6.ts | 1 - .../es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts | 1 - .../cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts | 1 - tests/cases/conformance/async/es6/asyncAwait_es6.ts | 1 - tests/cases/conformance/async/es6/asyncClass_es6.ts | 1 - tests/cases/conformance/async/es6/asyncConstructor_es6.ts | 1 - tests/cases/conformance/async/es6/asyncDeclare_es6.ts | 1 - tests/cases/conformance/async/es6/asyncEnum_es6.ts | 1 - tests/cases/conformance/async/es6/asyncGetter_es6.ts | 1 - tests/cases/conformance/async/es6/asyncInterface_es6.ts | 1 - tests/cases/conformance/async/es6/asyncModule_es6.ts | 1 - tests/cases/conformance/async/es6/asyncSetter_es6.ts | 1 - .../es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts | 1 - .../es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts | 1 - .../es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts | 1 - .../es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts | 1 - .../es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression1_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression2_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression3_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression4_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression5_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression6_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression7_es6.ts | 1 - .../async/es6/awaitCallExpression/awaitCallExpression8_es6.ts | 1 - tests/cases/conformance/async/es6/awaitUnion_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts | 1 - .../es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts | 1 - 51 files changed, 51 deletions(-) diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts index bb9f8a54e44..0959ed6c561 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async foo(): Promise => { // Legal to use 'await' in a type context. diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts index 45e686fd280..83e7413c527 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (): Promise => { }; \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts index 8d792fdff9c..bf39735d460 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var f = (await) => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts index 9ca200f8d76..4ae27c596f1 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function f(await = await) { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts index 4946592c45b..51b34abeca1 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var await = () => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts index 53052368b31..174a619bdf9 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (await): Promise => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts index 69768429ecf..dace96c4933 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (a = await): Promise => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts index a034381ba57..b1fd5cc030d 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var bar = async (): Promise => { // 'await' here is an identifier, and not an await expression. diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts index 397e06f703d..5d53338b1bd 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (): Promise => { var v = { [await]: foo } diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts index 3f961c9d595..da041fe472b 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (a = await => await): Promise => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts index f00d0d16545..8dab2c04086 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { method() { function other() {} diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts index a90f08cb9f7..f2c507cbdd6 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { method() { var fn = async () => await this; diff --git a/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts b/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts index 52694f24118..8e2cfd8c6c5 100644 --- a/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts +++ b/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @isolatedModules: true -// @experimentalAsyncFunctions: true import { MyPromise } from "missing"; declare var p: Promise; diff --git a/tests/cases/conformance/async/es6/asyncAwait_es6.ts b/tests/cases/conformance/async/es6/asyncAwait_es6.ts index eee2e73d023..8e72197a98d 100644 --- a/tests/cases/conformance/async/es6/asyncAwait_es6.ts +++ b/tests/cases/conformance/async/es6/asyncAwait_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 -// @experimentalAsyncFunctions: true type MyPromise = Promise; declare var MyPromise: typeof Promise; declare var p: Promise; diff --git a/tests/cases/conformance/async/es6/asyncClass_es6.ts b/tests/cases/conformance/async/es6/asyncClass_es6.ts index c4a444ca909..22ffe997b03 100644 --- a/tests/cases/conformance/async/es6/asyncClass_es6.ts +++ b/tests/cases/conformance/async/es6/asyncClass_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async class C { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncConstructor_es6.ts b/tests/cases/conformance/async/es6/asyncConstructor_es6.ts index 998156f19f4..d769fad0395 100644 --- a/tests/cases/conformance/async/es6/asyncConstructor_es6.ts +++ b/tests/cases/conformance/async/es6/asyncConstructor_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { async constructor() { } diff --git a/tests/cases/conformance/async/es6/asyncDeclare_es6.ts b/tests/cases/conformance/async/es6/asyncDeclare_es6.ts index d83c25a421a..5e0fb536b39 100644 --- a/tests/cases/conformance/async/es6/asyncDeclare_es6.ts +++ b/tests/cases/conformance/async/es6/asyncDeclare_es6.ts @@ -1,4 +1,3 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare async function foo(): Promise; \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncEnum_es6.ts b/tests/cases/conformance/async/es6/asyncEnum_es6.ts index d9569bef9d1..4fad7923a8c 100644 --- a/tests/cases/conformance/async/es6/asyncEnum_es6.ts +++ b/tests/cases/conformance/async/es6/asyncEnum_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async enum E { Value } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncGetter_es6.ts b/tests/cases/conformance/async/es6/asyncGetter_es6.ts index fe5751ece0b..79fde60fdc4 100644 --- a/tests/cases/conformance/async/es6/asyncGetter_es6.ts +++ b/tests/cases/conformance/async/es6/asyncGetter_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { async get foo() { } diff --git a/tests/cases/conformance/async/es6/asyncInterface_es6.ts b/tests/cases/conformance/async/es6/asyncInterface_es6.ts index c7bb460ea2b..5d5497d3cd4 100644 --- a/tests/cases/conformance/async/es6/asyncInterface_es6.ts +++ b/tests/cases/conformance/async/es6/asyncInterface_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async interface I { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncModule_es6.ts b/tests/cases/conformance/async/es6/asyncModule_es6.ts index cf660d25d1f..aa4c295d0e3 100644 --- a/tests/cases/conformance/async/es6/asyncModule_es6.ts +++ b/tests/cases/conformance/async/es6/asyncModule_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async module M { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncSetter_es6.ts b/tests/cases/conformance/async/es6/asyncSetter_es6.ts index 2ca1c805a45..8eedcbb5288 100644 --- a/tests/cases/conformance/async/es6/asyncSetter_es6.ts +++ b/tests/cases/conformance/async/es6/asyncSetter_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { async set foo(value) { } diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts index df0d0e459d4..46060f309b2 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts index 7678c4b17ab..362f1ecc446 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts index 0b441f063dd..d3b02033252 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: number; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts index a0bdf58710f..eba3be31acf 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts index 3276d24ee4b..71b173f9e73 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts index 02de646112c..d0442f0968d 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts index 5d2e4046349..6de07ef12ff 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts index 702e1f4cbcc..2cc0b7b12d0 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts index aeeb192b4ba..8e349fbc5c3 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts index 82bb1f88ad2..bbe81ac94b1 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts index d5778df71ba..09e88f5acbc 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts index db01f34c825..fe0182da4aa 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts index b0b10a02a6f..e3280eb7c10 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitUnion_es6.ts b/tests/cases/conformance/async/es6/awaitUnion_es6.ts index 59c5285556c..a132ae01dfd 100644 --- a/tests/cases/conformance/async/es6/awaitUnion_es6.ts +++ b/tests/cases/conformance/async/es6/awaitUnion_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare let a: number | string; declare let b: PromiseLike | PromiseLike; declare let c: PromiseLike; diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts index d543e424a38..aab1f0013ec 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(a = await => await): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts index 23e40a0e5ba..747d20d7ea8 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function await(): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts index a457d27073f..bbd77250365 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts @@ -1,4 +1,3 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var v = async function await(): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts index 83b6a99579c..8670fa02811 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { // Legal to use 'await' in a type context. var v: await; diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts index b837e6618d1..ba70c61b492 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { return; } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts index 56bcf84911a..74018b73678 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare class Thenable { then(): void; } declare let a: any; declare let obj: { then: string; }; diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts index 3a792140011..289a65bb74f 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts index e75ef1d36a3..25a153b4a34 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function f(await) { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts index 9ca200f8d76..4ae27c596f1 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function f(await = await) { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts index ac4447a0e4d..5d1ec389da3 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function await() { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts index 60a0a8f4b74..eb3cd1db556 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(await): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts index a3ab64b9d29..89b0445fd2e 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(a = await): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts index cde12c7dbe3..5a964695da5 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function bar(): Promise { // 'await' here is an identifier, and not a yield expression. async function foo(a = await): Promise { diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts index 52b6dba2792..764b0f3fb8a 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts @@ -1,4 +1,3 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var v = { [await]: foo } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts index 662fa017606..7671764ad2a 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { var v = { [await]: foo } } \ No newline at end of file From f952873ef10d2ad70b680fa773c4999fc0555193 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 12 Oct 2015 15:58:34 -0700 Subject: [PATCH 20/34] allow forward references to block scoped variables from functions --- src/compiler/checker.ts | 106 +++++++++++++----- .../blockScopedVariablesUseBeforeDef.js | 74 ++++++++++++ .../blockScopedVariablesUseBeforeDef.symbols | 76 +++++++++++++ .../blockScopedVariablesUseBeforeDef.types | 81 +++++++++++++ .../blockScopedVariablesUseBeforeDef.ts | 35 ++++++ 5 files changed, 341 insertions(+), 31 deletions(-) create mode 100644 tests/baselines/reference/blockScopedVariablesUseBeforeDef.js create mode 100644 tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols create mode 100644 tests/baselines/reference/blockScopedVariablesUseBeforeDef.types create mode 100644 tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad772bd1a70..3eead9fc957 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -383,20 +383,42 @@ namespace ts { // return undefined if we can't find a symbol. } - /** Returns true if node1 is defined before node 2**/ - function isDefinedBefore(node1: Node, node2: Node): boolean { - let file1 = getSourceFileOfNode(node1); - let file2 = getSourceFileOfNode(node2); + const enum RelativeLocation { + Unknown, + SameFileLocatedBefore, + SameFileLocatedAfter, + DifferentFilesLocatedBefore, + DifferentFilesLocatedAfter, + } + + function isLocatedBefore(origin: Node, target: Node): boolean { + switch (getRelativeLocation(origin, target)) { + // unknown is returned with nodes are in different files and order cannot be determined based on compilation settings + // optimistically assume this is ok + case RelativeLocation.Unknown: + case RelativeLocation.SameFileLocatedBefore: + case RelativeLocation.DifferentFilesLocatedBefore: + return true; + default: + return false; + } + } + + /** gets relative location of target comparing to origin **/ + function getRelativeLocation(origin: Node, target: Node): RelativeLocation { + let file1 = getSourceFileOfNode(origin); + let file2 = getSourceFileOfNode(target); if (file1 === file2) { - return node1.pos <= node2.pos; + return origin.pos > target.pos ? RelativeLocation.SameFileLocatedBefore : RelativeLocation.SameFileLocatedAfter; } if (!compilerOptions.outFile && !compilerOptions.out) { - return true; + // nodes are in different files and order cannot be determines + return RelativeLocation.Unknown; } let sourceFiles = host.getSourceFiles(); - return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2); + return sourceFiles.indexOf(file1) > sourceFiles.indexOf(file2) ? RelativeLocation.DifferentFilesLocatedBefore : RelativeLocation.DifferentFilesLocatedAfter; } // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and @@ -628,31 +650,53 @@ namespace ts { Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined"); // first check if usage is lexically located after the declaration - let isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation); - if (!isUsedBeforeDeclaration) { - // lexical check succeeded however code still can be illegal. - // - block scoped variables cannot be used in its initializers - // let x = x; // illegal but usage is lexically after definition - // - in ForIn/ForOf statements variable cannot be contained in expression part - // for (let x in x) - // for (let x of x) + let isUsedBeforeDeclaration = false; + switch (getRelativeLocation(declaration, errorLocation)) { + case RelativeLocation.DifferentFilesLocatedBefore: + isUsedBeforeDeclaration = true; + break; + case RelativeLocation.SameFileLocatedBefore: + // try to detect if forward reference to block scoped variable is inside function + // such forward references are permitted (they are still technically can be incorrect (i.e. in case of IIFEs) + // but detecting these case is more complicated task) + const declarationContainer = getEnclosingBlockScopeContainer(declaration); + let current = errorLocation; + while (current) { + if (current === declarationContainer) { + isUsedBeforeDeclaration = true; + break; + } + else if (isFunctionLike(current)) { + break; + } + current = current.parent; + } + break; + case RelativeLocation.SameFileLocatedAfter: + // lexical check succeeded however code still can be illegal. + // - block scoped variables cannot be used in its initializers + // let x = x; // illegal but usage is lexically after definition + // - in ForIn/ForOf statements variable cannot be contained in expression part + // for (let x in x) + // for (let x of x) - // climb up to the variable declaration skipping binding patterns - let variableDeclaration = getAncestor(declaration, SyntaxKind.VariableDeclaration); - let container = getEnclosingBlockScopeContainer(variableDeclaration); + // climb up to the variable declaration skipping binding patterns + let variableDeclaration = getAncestor(declaration, SyntaxKind.VariableDeclaration); + let container = getEnclosingBlockScopeContainer(variableDeclaration); - if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) { - // variable statement/for statement case, - // use site should not be inside variable declaration (initializer of declaration or binding element) - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container); - } - else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) { - // ForIn/ForOf case - use site should not be used in expression part - let expression = (variableDeclaration.parent.parent).expression; - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container); - } + if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement || + variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) { + // variable statement/for statement case, + // use site should not be inside variable declaration (initializer of declaration or binding element) + isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container); + } + else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement || + variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) { + // ForIn/ForOf case - use site should not be used in expression part + let expression = (variableDeclaration.parent.parent).expression; + isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container); + } + break; } if (isUsedBeforeDeclaration) { error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name)); @@ -13356,7 +13400,7 @@ namespace ts { } // illegal case: forward reference - if (!isDefinedBefore(propertyDecl, member)) { + if (isLocatedBefore(propertyDecl, member)) { reportError = false; error(e, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums); return undefined; diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js new file mode 100644 index 00000000000..2792521344f --- /dev/null +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js @@ -0,0 +1,74 @@ +//// [blockScopedVariablesUseBeforeDef.ts] +function foo1() { + let a = () => x; + let x; +} + +function foo2() { + let a = function () { return x; } + let x; +} + +function foo3() { + class X { + m() { return x;} + } + let x; +} + +function foo4() { + let y = class { + m() { return x; } + }; + let x; +} + +function foo5() { + let x = () => y; + let y = () => x; +} + +function foo6() { + function f() { + return x; + } + let x; +} + +//// [blockScopedVariablesUseBeforeDef.js] +function foo1() { + var a = function () { return x; }; + var x; +} +function foo2() { + var a = function () { return x; }; + var x; +} +function foo3() { + var X = (function () { + function X() { + } + X.prototype.m = function () { return x; }; + return X; + })(); + var x; +} +function foo4() { + var y = (function () { + function class_1() { + } + class_1.prototype.m = function () { return x; }; + return class_1; + })(); + var x; +} +function foo5() { + var x = function () { return y; }; + var y = function () { return x; }; +} +function foo6() { + function f() { + return x; + } + var x; +} diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols new file mode 100644 index 00000000000..a528af236c1 --- /dev/null +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols @@ -0,0 +1,76 @@ +=== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts === +function foo1() { +>foo1 : Symbol(foo1, Decl(blockScopedVariablesUseBeforeDef.ts, 0, 0)) + + let a = () => x; +>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 1, 4)) +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 2, 4)) + + let x; +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 2, 4)) +} + +function foo2() { +>foo2 : Symbol(foo2, Decl(blockScopedVariablesUseBeforeDef.ts, 3, 1)) + + let a = function () { return x; } +>a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 6, 4)) +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 7, 4)) + + let x; +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 7, 4)) +} + +function foo3() { +>foo3 : Symbol(foo3, Decl(blockScopedVariablesUseBeforeDef.ts, 8, 1)) + + class X { +>X : Symbol(X, Decl(blockScopedVariablesUseBeforeDef.ts, 10, 17)) + + m() { return x;} +>m : Symbol(m, Decl(blockScopedVariablesUseBeforeDef.ts, 11, 10)) +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 14, 4)) + } + let x; +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 14, 4)) +} + +function foo4() { +>foo4 : Symbol(foo4, Decl(blockScopedVariablesUseBeforeDef.ts, 15, 1)) + + let y = class { +>y : Symbol(y, Decl(blockScopedVariablesUseBeforeDef.ts, 18, 4)) + + m() { return x; } +>m : Symbol((Anonymous class).m, Decl(blockScopedVariablesUseBeforeDef.ts, 18, 16)) +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 21, 4)) + + }; + let x; +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 21, 4)) +} + +function foo5() { +>foo5 : Symbol(foo5, Decl(blockScopedVariablesUseBeforeDef.ts, 22, 1)) + + let x = () => y; +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 25, 4)) +>y : Symbol(y, Decl(blockScopedVariablesUseBeforeDef.ts, 26, 4)) + + let y = () => x; +>y : Symbol(y, Decl(blockScopedVariablesUseBeforeDef.ts, 26, 4)) +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 25, 4)) +} + +function foo6() { +>foo6 : Symbol(foo6, Decl(blockScopedVariablesUseBeforeDef.ts, 27, 1)) + + function f() { +>f : Symbol(f, Decl(blockScopedVariablesUseBeforeDef.ts, 29, 17)) + + return x; +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 33, 4)) + } + let x; +>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 33, 4)) +} diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.types b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.types new file mode 100644 index 00000000000..bc3d5f8eba8 --- /dev/null +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.types @@ -0,0 +1,81 @@ +=== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts === +function foo1() { +>foo1 : () => void + + let a = () => x; +>a : () => any +>() => x : () => any +>x : any + + let x; +>x : any +} + +function foo2() { +>foo2 : () => void + + let a = function () { return x; } +>a : () => any +>function () { return x; } : () => any +>x : any + + let x; +>x : any +} + +function foo3() { +>foo3 : () => void + + class X { +>X : X + + m() { return x;} +>m : () => any +>x : any + } + let x; +>x : any +} + +function foo4() { +>foo4 : () => void + + let y = class { +>y : typeof (Anonymous class) +>class { m() { return x; } } : typeof (Anonymous class) + + m() { return x; } +>m : () => any +>x : any + + }; + let x; +>x : any +} + +function foo5() { +>foo5 : () => void + + let x = () => y; +>x : () => () => any +>() => y : () => () => any +>y : () => () => any + + let y = () => x; +>y : () => () => any +>() => x : () => () => any +>x : () => () => any +} + +function foo6() { +>foo6 : () => void + + function f() { +>f : () => any + + return x; +>x : any + } + let x; +>x : any +} diff --git a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts new file mode 100644 index 00000000000..7ded92df4c9 --- /dev/null +++ b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts @@ -0,0 +1,35 @@ +function foo1() { + let a = () => x; + let x; +} + +function foo2() { + let a = function () { return x; } + let x; +} + +function foo3() { + class X { + m() { return x;} + } + let x; +} + +function foo4() { + let y = class { + m() { return x; } + }; + let x; +} + +function foo5() { + let x = () => y; + let y = () => x; +} + +function foo6() { + function f() { + return x; + } + let x; +} \ No newline at end of file From eb3b91cf1b1e681eac27c9b650830b8c3c978cd6 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 12 Oct 2015 17:24:39 -0700 Subject: [PATCH 21/34] addressed PR feedback --- src/compiler/checker.ts | 15 +- ...lockScopedVariablesUseBeforeDef.errors.txt | 93 ++++++++++++ .../blockScopedVariablesUseBeforeDef.js | 143 +++++++++++++++--- .../blockScopedVariablesUseBeforeDef.symbols | 76 ---------- .../blockScopedVariablesUseBeforeDef.types | 81 ---------- ...clarations-useBeforeDefinition2.errors.txt | 11 -- ...tDeclarations-useBeforeDefinition2.symbols | 9 ++ ...nstDeclarations-useBeforeDefinition2.types | 10 ++ ...clarations-useBeforeDefinition2.errors.txt | 11 -- ...tDeclarations-useBeforeDefinition2.symbols | 9 ++ ...letDeclarations-useBeforeDefinition2.types | 10 ++ .../blockScopedVariablesUseBeforeDef.ts | 85 ++++++++--- 12 files changed, 336 insertions(+), 217 deletions(-) create mode 100644 tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt delete mode 100644 tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols delete mode 100644 tests/baselines/reference/blockScopedVariablesUseBeforeDef.types delete mode 100644 tests/baselines/reference/constDeclarations-useBeforeDefinition2.errors.txt create mode 100644 tests/baselines/reference/constDeclarations-useBeforeDefinition2.symbols create mode 100644 tests/baselines/reference/constDeclarations-useBeforeDefinition2.types delete mode 100644 tests/baselines/reference/letDeclarations-useBeforeDefinition2.errors.txt create mode 100644 tests/baselines/reference/letDeclarations-useBeforeDefinition2.symbols create mode 100644 tests/baselines/reference/letDeclarations-useBeforeDefinition2.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3eead9fc957..0213f594f5b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -412,7 +412,7 @@ namespace ts { return origin.pos > target.pos ? RelativeLocation.SameFileLocatedBefore : RelativeLocation.SameFileLocatedAfter; } - if (!compilerOptions.outFile && !compilerOptions.out) { + if (modulekind || (!compilerOptions.outFile && !compilerOptions.out)) { // nodes are in different files and order cannot be determines return RelativeLocation.Unknown; } @@ -666,7 +666,18 @@ namespace ts { isUsedBeforeDeclaration = true; break; } - else if (isFunctionLike(current)) { + + if (isFunctionLike(current)) { + break; + } + + const isInitializerOfNonStaticProperty = + current.parent && + current.parent.kind === SyntaxKind.PropertyDeclaration && + (current.parent.flags & NodeFlags.Static) === 0 && + (current.parent).initializer === current; + + if (isInitializerOfNonStaticProperty) { break; } current = current.parent; diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt new file mode 100644 index 00000000000..a2bae3307a1 --- /dev/null +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt @@ -0,0 +1,93 @@ +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(53,20): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(60,20): error TS2448: Block-scoped variable 'x' used before its declaration. + + +==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (2 errors) ==== + function foo1() { + let a = () => x; + let x; + } + + function foo2() { + let a = function () { return x; } + let x; + } + + function foo3() { + class X { + m() { return x;} + } + let x; + } + + function foo4() { + let y = class { + m() { return x; } + }; + let x; + } + + function foo5() { + let x = () => y; + let y = () => x; + } + + function foo6() { + function f() { + return x; + } + let x; + } + + function foo7() { + class A { + a = x; + } + let x; + } + + function foo8() { + let y = class { + a = x; + } + let x; + } + + function foo9() { + let y = class { + static a = x; + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + } + let x; + } + + function foo10() { + class A { + static a = x; + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + } + let x; + } + + function foo11() { + function f () { + let y = class { + static a = x; + } + } + let x; + } + + function foo12() { + function f () { + let y = class { + a; + constructor() { + this.a = x; + } + } + } + let x; + } \ No newline at end of file diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js index 2792521344f..212b9d8b7b8 100644 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js @@ -1,38 +1,87 @@ //// [blockScopedVariablesUseBeforeDef.ts] function foo1() { - let a = () => x; - let x; + let a = () => x; + let x; } function foo2() { - let a = function () { return x; } - let x; + let a = function () { return x; } + let x; } function foo3() { - class X { - m() { return x;} - } - let x; + class X { + m() { return x;} + } + let x; } function foo4() { - let y = class { - m() { return x; } - }; - let x; + let y = class { + m() { return x; } + }; + let x; } function foo5() { - let x = () => y; - let y = () => x; + let x = () => y; + let y = () => x; } function foo6() { - function f() { - return x; - } - let x; + function f() { + return x; + } + let x; +} + +function foo7() { + class A { + a = x; + } + let x; +} + +function foo8() { + let y = class { + a = x; + } + let x; +} + +function foo9() { + let y = class { + static a = x; + } + let x; +} + +function foo10() { + class A { + static a = x; + } + let x; +} + +function foo11() { + function f () { + let y = class { + static a = x; + } + } + let x; +} + +function foo12() { + function f () { + let y = class { + a; + constructor() { + this.a = x; + } + } + } + let x; } //// [blockScopedVariablesUseBeforeDef.js] @@ -72,3 +121,61 @@ function foo6() { } var x; } +function foo7() { + var A = (function () { + function A() { + this.a = x; + } + return A; + })(); + var x; +} +function foo8() { + var y = (function () { + function class_2() { + this.a = x; + } + return class_2; + })(); + var x; +} +function foo9() { + var y = (function () { + function class_3() { + } + class_3.a = x; + return class_3; + })(); + var x; +} +function foo10() { + var A = (function () { + function A() { + } + A.a = x; + return A; + })(); + var x; +} +function foo11() { + function f() { + var y = (function () { + function class_4() { + } + class_4.a = x; + return class_4; + })(); + } + var x; +} +function foo12() { + function f() { + var y = (function () { + function class_5() { + this.a = x; + } + return class_5; + })(); + } + var x; +} diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols deleted file mode 100644 index a528af236c1..00000000000 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.symbols +++ /dev/null @@ -1,76 +0,0 @@ -=== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts === -function foo1() { ->foo1 : Symbol(foo1, Decl(blockScopedVariablesUseBeforeDef.ts, 0, 0)) - - let a = () => x; ->a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 1, 4)) ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 2, 4)) - - let x; ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 2, 4)) -} - -function foo2() { ->foo2 : Symbol(foo2, Decl(blockScopedVariablesUseBeforeDef.ts, 3, 1)) - - let a = function () { return x; } ->a : Symbol(a, Decl(blockScopedVariablesUseBeforeDef.ts, 6, 4)) ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 7, 4)) - - let x; ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 7, 4)) -} - -function foo3() { ->foo3 : Symbol(foo3, Decl(blockScopedVariablesUseBeforeDef.ts, 8, 1)) - - class X { ->X : Symbol(X, Decl(blockScopedVariablesUseBeforeDef.ts, 10, 17)) - - m() { return x;} ->m : Symbol(m, Decl(blockScopedVariablesUseBeforeDef.ts, 11, 10)) ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 14, 4)) - } - let x; ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 14, 4)) -} - -function foo4() { ->foo4 : Symbol(foo4, Decl(blockScopedVariablesUseBeforeDef.ts, 15, 1)) - - let y = class { ->y : Symbol(y, Decl(blockScopedVariablesUseBeforeDef.ts, 18, 4)) - - m() { return x; } ->m : Symbol((Anonymous class).m, Decl(blockScopedVariablesUseBeforeDef.ts, 18, 16)) ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 21, 4)) - - }; - let x; ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 21, 4)) -} - -function foo5() { ->foo5 : Symbol(foo5, Decl(blockScopedVariablesUseBeforeDef.ts, 22, 1)) - - let x = () => y; ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 25, 4)) ->y : Symbol(y, Decl(blockScopedVariablesUseBeforeDef.ts, 26, 4)) - - let y = () => x; ->y : Symbol(y, Decl(blockScopedVariablesUseBeforeDef.ts, 26, 4)) ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 25, 4)) -} - -function foo6() { ->foo6 : Symbol(foo6, Decl(blockScopedVariablesUseBeforeDef.ts, 27, 1)) - - function f() { ->f : Symbol(f, Decl(blockScopedVariablesUseBeforeDef.ts, 29, 17)) - - return x; ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 33, 4)) - } - let x; ->x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 33, 4)) -} diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.types b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.types deleted file mode 100644 index bc3d5f8eba8..00000000000 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.types +++ /dev/null @@ -1,81 +0,0 @@ -=== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts === -function foo1() { ->foo1 : () => void - - let a = () => x; ->a : () => any ->() => x : () => any ->x : any - - let x; ->x : any -} - -function foo2() { ->foo2 : () => void - - let a = function () { return x; } ->a : () => any ->function () { return x; } : () => any ->x : any - - let x; ->x : any -} - -function foo3() { ->foo3 : () => void - - class X { ->X : X - - m() { return x;} ->m : () => any ->x : any - } - let x; ->x : any -} - -function foo4() { ->foo4 : () => void - - let y = class { ->y : typeof (Anonymous class) ->class { m() { return x; } } : typeof (Anonymous class) - - m() { return x; } ->m : () => any ->x : any - - }; - let x; ->x : any -} - -function foo5() { ->foo5 : () => void - - let x = () => y; ->x : () => () => any ->() => y : () => () => any ->y : () => () => any - - let y = () => x; ->y : () => () => any ->() => x : () => () => any ->x : () => () => any -} - -function foo6() { ->foo6 : () => void - - function f() { ->f : () => any - - return x; ->x : any - } - let x; ->x : any -} diff --git a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.errors.txt b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.errors.txt deleted file mode 100644 index a4d28f51878..00000000000 --- a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.errors.txt +++ /dev/null @@ -1,11 +0,0 @@ -tests/cases/compiler/file1.ts(2,1): error TS2448: Block-scoped variable 'c' used before its declaration. - - -==== tests/cases/compiler/file1.ts (1 errors) ==== - - c; - ~ -!!! error TS2448: Block-scoped variable 'c' used before its declaration. - -==== tests/cases/compiler/file2.ts (0 errors) ==== - const c = 0; \ No newline at end of file diff --git a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.symbols b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.symbols new file mode 100644 index 00000000000..281ce427733 --- /dev/null +++ b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/file1.ts === + +c; +>c : Symbol(c, Decl(file2.ts, 0, 5)) + +=== tests/cases/compiler/file2.ts === +const c = 0; +>c : Symbol(c, Decl(file2.ts, 0, 5)) + diff --git a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.types b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.types new file mode 100644 index 00000000000..ae60fdfa477 --- /dev/null +++ b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.types @@ -0,0 +1,10 @@ +=== tests/cases/compiler/file1.ts === + +c; +>c : number + +=== tests/cases/compiler/file2.ts === +const c = 0; +>c : number +>0 : number + diff --git a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.errors.txt b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.errors.txt deleted file mode 100644 index 5b8633312d9..00000000000 --- a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.errors.txt +++ /dev/null @@ -1,11 +0,0 @@ -tests/cases/compiler/file1.ts(2,1): error TS2448: Block-scoped variable 'l' used before its declaration. - - -==== tests/cases/compiler/file1.ts (1 errors) ==== - - l; - ~ -!!! error TS2448: Block-scoped variable 'l' used before its declaration. - -==== tests/cases/compiler/file2.ts (0 errors) ==== - const l = 0; \ No newline at end of file diff --git a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.symbols b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.symbols new file mode 100644 index 00000000000..c5a067ede4d --- /dev/null +++ b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/file1.ts === + +l; +>l : Symbol(l, Decl(file2.ts, 0, 5)) + +=== tests/cases/compiler/file2.ts === +const l = 0; +>l : Symbol(l, Decl(file2.ts, 0, 5)) + diff --git a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.types b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.types new file mode 100644 index 00000000000..793a7a78ba7 --- /dev/null +++ b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.types @@ -0,0 +1,10 @@ +=== tests/cases/compiler/file1.ts === + +l; +>l : number + +=== tests/cases/compiler/file2.ts === +const l = 0; +>l : number +>0 : number + diff --git a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts index 7ded92df4c9..287f5ae117e 100644 --- a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts +++ b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts @@ -1,35 +1,84 @@ function foo1() { - let a = () => x; - let x; + let a = () => x; + let x; } function foo2() { - let a = function () { return x; } - let x; + let a = function () { return x; } + let x; } function foo3() { - class X { - m() { return x;} - } - let x; + class X { + m() { return x;} + } + let x; } function foo4() { - let y = class { - m() { return x; } - }; - let x; + let y = class { + m() { return x; } + }; + let x; } function foo5() { - let x = () => y; - let y = () => x; + let x = () => y; + let y = () => x; } function foo6() { - function f() { - return x; - } - let x; + function f() { + return x; + } + let x; +} + +function foo7() { + class A { + a = x; + } + let x; +} + +function foo8() { + let y = class { + a = x; + } + let x; +} + +function foo9() { + let y = class { + static a = x; + } + let x; +} + +function foo10() { + class A { + static a = x; + } + let x; +} + +function foo11() { + function f () { + let y = class { + static a = x; + } + } + let x; +} + +function foo12() { + function f () { + let y = class { + a; + constructor() { + this.a = x; + } + } + } + let x; } \ No newline at end of file From 0fa89ad99c41ab747511baf2ba6eefb0d48e5a9c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 12 Oct 2015 17:38:55 -0700 Subject: [PATCH 22/34] Fixes #5104. --- src/compiler/core.ts | 8 +++-- .../reference/decoratorCallGeneric.errors.txt | 22 +++++++++++++ .../reference/decoratorCallGeneric.js | 31 +++++++++++++++++++ .../decorators/decoratorCallGeneric.ts | 12 +++++++ 4 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/decoratorCallGeneric.errors.txt create mode 100644 tests/baselines/reference/decoratorCallGeneric.js create mode 100644 tests/cases/conformance/decorators/decoratorCallGeneric.ts diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ce59c3b3bc6..a310ceec457 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -437,8 +437,12 @@ namespace ts { } export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): DiagnosticMessageChain { - Debug.assert(!headChain.next); - headChain.next = tailChain; + let lastChain = headChain; + while (lastChain.next) { + lastChain = lastChain.next; + } + + lastChain.next = tailChain; return headChain; } diff --git a/tests/baselines/reference/decoratorCallGeneric.errors.txt b/tests/baselines/reference/decoratorCallGeneric.errors.txt new file mode 100644 index 00000000000..c8443420740 --- /dev/null +++ b/tests/baselines/reference/decoratorCallGeneric.errors.txt @@ -0,0 +1,22 @@ +tests/cases/conformance/decorators/decoratorCallGeneric.ts(7,2): error TS1238: Unable to resolve signature of class decorator when called as an expression. + The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. + Type argument candidate 'C' is not a valid type argument because it is not a supertype of candidate 'void'. + + +==== tests/cases/conformance/decorators/decoratorCallGeneric.ts (1 errors) ==== + interface I { + prototype: T, + m: () => T + } + function dec(c: I) { } + + @dec + ~~~ +!!! error TS1238: Unable to resolve signature of class decorator when called as an expression. +!!! error TS1238: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. +!!! error TS1238: Type argument candidate 'C' is not a valid type argument because it is not a supertype of candidate 'void'. + class C { + _brand: any; + static m() {} + } + \ No newline at end of file diff --git a/tests/baselines/reference/decoratorCallGeneric.js b/tests/baselines/reference/decoratorCallGeneric.js new file mode 100644 index 00000000000..8c141d5a131 --- /dev/null +++ b/tests/baselines/reference/decoratorCallGeneric.js @@ -0,0 +1,31 @@ +//// [decoratorCallGeneric.ts] +interface I { + prototype: T, + m: () => T +} +function dec(c: I) { } + +@dec +class C { + _brand: any; + static m() {} +} + + +//// [decoratorCallGeneric.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +function dec(c) { } +var C = (function () { + function C() { + } + C.m = function () { }; + C = __decorate([ + dec + ], C); + return C; +})(); diff --git a/tests/cases/conformance/decorators/decoratorCallGeneric.ts b/tests/cases/conformance/decorators/decoratorCallGeneric.ts new file mode 100644 index 00000000000..3eeaae6837c --- /dev/null +++ b/tests/cases/conformance/decorators/decoratorCallGeneric.ts @@ -0,0 +1,12 @@ +// @experimentalDecorators: true +interface I { + prototype: T, + m: () => T +} +function dec(c: I) { } + +@dec +class C { + _brand: any; + static m() {} +} From 48b24343b19769c175796a5fa9f769cf2bf44f1d Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 13 Oct 2015 00:06:59 -0700 Subject: [PATCH 23/34] use isBlockScopedNameDeclaredBeforeUse for block scoped variables and enums --- src/compiler/checker.ts | 159 +++++++----------- ...lockScopedVariablesUseBeforeDef.errors.txt | 45 ++++- .../blockScopedVariablesUseBeforeDef.js | 48 +++++- .../blockScopedVariablesUseBeforeDef.ts | 31 +++- 4 files changed, 183 insertions(+), 100 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0213f594f5b..9db3d49b9af 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -383,42 +383,72 @@ namespace ts { // return undefined if we can't find a symbol. } - const enum RelativeLocation { - Unknown, - SameFileLocatedBefore, - SameFileLocatedAfter, - DifferentFilesLocatedBefore, - DifferentFilesLocatedAfter, - } - - function isLocatedBefore(origin: Node, target: Node): boolean { - switch (getRelativeLocation(origin, target)) { - // unknown is returned with nodes are in different files and order cannot be determined based on compilation settings - // optimistically assume this is ok - case RelativeLocation.Unknown: - case RelativeLocation.SameFileLocatedBefore: - case RelativeLocation.DifferentFilesLocatedBefore: + function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean { + const declarationFile = getSourceFileOfNode(declaration); + const useFile = getSourceFileOfNode(usage); + if (declarationFile !== useFile) { + if (modulekind || (!compilerOptions.outFile && !compilerOptions.out)) { + // nodes are in different files and order cannot be determines return true; - default: - return false; - } - } + } - /** gets relative location of target comparing to origin **/ - function getRelativeLocation(origin: Node, target: Node): RelativeLocation { - let file1 = getSourceFileOfNode(origin); - let file2 = getSourceFileOfNode(target); - if (file1 === file2) { - return origin.pos > target.pos ? RelativeLocation.SameFileLocatedBefore : RelativeLocation.SameFileLocatedAfter; + const sourceFiles = host.getSourceFiles(); + return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile); } - if (modulekind || (!compilerOptions.outFile && !compilerOptions.out)) { - // nodes are in different files and order cannot be determines - return RelativeLocation.Unknown; + if (declaration.pos <= usage.pos) { + // declaration is before usage + // still might be illegal if usage is in the initializer of the variable declaration + return declaration.kind !== SyntaxKind.VariableDeclaration || + !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration, usage); } - let sourceFiles = host.getSourceFiles(); - return sourceFiles.indexOf(file1) > sourceFiles.indexOf(file2) ? RelativeLocation.DifferentFilesLocatedBefore : RelativeLocation.DifferentFilesLocatedAfter; + // declaration is after usage + // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) + return isUsedInFunctionOrNonStaticProperty(declaration, usage); + + function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { + const container = getEnclosingBlockScopeContainer(declaration); + + if (declaration.parent.parent.kind === SyntaxKind.VariableStatement || + declaration.parent.parent.kind === SyntaxKind.ForStatement) { + // variable statement/for statement case, + // use site should not be inside variable declaration (initializer of declaration or binding element) + return isSameScopeDescendentOf(usage, declaration, container); + } + else if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement || + declaration.parent.parent.kind === SyntaxKind.ForInStatement) { + // ForIn/ForOf case - use site should not be used in expression part + let expression = (declaration.parent.parent).expression; + return isSameScopeDescendentOf(usage, expression, container); + } + } + + function isUsedInFunctionOrNonStaticProperty(declaration: Declaration, usage: Node): boolean { + const container = getEnclosingBlockScopeContainer(declaration); + let current = usage; + while (current) { + if (current === container) { + return false; + } + + if (isFunctionLike(current)) { + return true; + } + + const initializerOfNonStaticProperty = current.parent && + current.parent.kind === SyntaxKind.PropertyDeclaration && + (current.parent.flags & NodeFlags.Static) === 0 && + (current.parent).initializer === current; + + if (initializerOfNonStaticProperty) { + return true; + } + + current = current.parent; + } + return false; + } } // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and @@ -649,67 +679,7 @@ namespace ts { Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined"); - // first check if usage is lexically located after the declaration - let isUsedBeforeDeclaration = false; - switch (getRelativeLocation(declaration, errorLocation)) { - case RelativeLocation.DifferentFilesLocatedBefore: - isUsedBeforeDeclaration = true; - break; - case RelativeLocation.SameFileLocatedBefore: - // try to detect if forward reference to block scoped variable is inside function - // such forward references are permitted (they are still technically can be incorrect (i.e. in case of IIFEs) - // but detecting these case is more complicated task) - const declarationContainer = getEnclosingBlockScopeContainer(declaration); - let current = errorLocation; - while (current) { - if (current === declarationContainer) { - isUsedBeforeDeclaration = true; - break; - } - - if (isFunctionLike(current)) { - break; - } - - const isInitializerOfNonStaticProperty = - current.parent && - current.parent.kind === SyntaxKind.PropertyDeclaration && - (current.parent.flags & NodeFlags.Static) === 0 && - (current.parent).initializer === current; - - if (isInitializerOfNonStaticProperty) { - break; - } - current = current.parent; - } - break; - case RelativeLocation.SameFileLocatedAfter: - // lexical check succeeded however code still can be illegal. - // - block scoped variables cannot be used in its initializers - // let x = x; // illegal but usage is lexically after definition - // - in ForIn/ForOf statements variable cannot be contained in expression part - // for (let x in x) - // for (let x of x) - - // climb up to the variable declaration skipping binding patterns - let variableDeclaration = getAncestor(declaration, SyntaxKind.VariableDeclaration); - let container = getEnclosingBlockScopeContainer(variableDeclaration); - - if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) { - // variable statement/for statement case, - // use site should not be inside variable declaration (initializer of declaration or binding element) - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container); - } - else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) { - // ForIn/ForOf case - use site should not be used in expression part - let expression = (variableDeclaration.parent.parent).expression; - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container); - } - break; - } - if (isUsedBeforeDeclaration) { + if (!isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration), errorLocation)) { error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name)); } } @@ -13233,6 +13203,8 @@ namespace ts { let nodeLinks = getNodeLinks(node); if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { + nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; + let enumSymbol = getSymbolOfNode(node); let enumType = getDeclaredTypeOfSymbol(enumSymbol); let autoValue = 0; // set to undefined when enum member is non-constant @@ -13270,8 +13242,6 @@ namespace ts { getNodeLinks(member).enumMemberValue = autoValue++; } } - - nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; } function computeConstantValueForEnumMemberInitializer(initializer: Expression, enumType: Type, enumIsConst: boolean, ambient: boolean): number { @@ -13411,12 +13381,13 @@ namespace ts { } // illegal case: forward reference - if (isLocatedBefore(propertyDecl, member)) { + if (!isBlockScopedNameDeclaredBeforeUse(propertyDecl, member)) { reportError = false; error(e, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums); return undefined; } + computeEnumMemberValues(propertyDecl.parent); return getNodeLinks(propertyDecl).enumMemberValue; } } diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt index a2bae3307a1..15697e9cf61 100644 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt @@ -1,8 +1,18 @@ -tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(53,20): error TS2448: Block-scoped variable 'x' used before its declaration. -tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(60,20): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(2,13): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(58,20): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(65,20): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(100,12): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(105,20): error TS2651: A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums. -==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (2 errors) ==== +==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (5 errors) ==== + function foo0() { + let a = x; + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + let x; + } + function foo1() { let a = () => x; let x; @@ -90,4 +100,31 @@ tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(60,20): error TS2448: B } } let x; - } \ No newline at end of file + } + + function foo13() { + let a = { + get a() { return x } + } + let x + } + + function foo14() { + let a = { + a: x + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + } + let x + } + + const enum A { X = B.Y } + ~~~ +!!! error TS2651: A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums. + + const enum B { Y } + + function foo15() { + const enum A1 { X = B1.Y } + } + const enum B1 { Y } \ No newline at end of file diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js index 212b9d8b7b8..2030b0eed59 100644 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js @@ -1,4 +1,9 @@ //// [blockScopedVariablesUseBeforeDef.ts] +function foo0() { + let a = x; + let x; +} + function foo1() { let a = () => x; let x; @@ -82,9 +87,36 @@ function foo12() { } } let x; -} +} + +function foo13() { + let a = { + get a() { return x } + } + let x +} + +function foo14() { + let a = { + a: x + } + let x +} + +const enum A { X = B.Y } + +const enum B { Y } + +function foo15() { + const enum A1 { X = B1.Y } +} +const enum B1 { Y } //// [blockScopedVariablesUseBeforeDef.js] +function foo0() { + var a = x; + var x; +} function foo1() { var a = function () { return x; }; var x; @@ -179,3 +211,17 @@ function foo12() { } var x; } +function foo13() { + var a = { + get a() { return x; } + }; + var x; +} +function foo14() { + var a = { + a: x + }; + var x; +} +function foo15() { +} diff --git a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts index 287f5ae117e..1cade31d238 100644 --- a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts +++ b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts @@ -1,3 +1,9 @@ +// @target: ES5 +function foo0() { + let a = x; + let x; +} + function foo1() { let a = () => x; let x; @@ -81,4 +87,27 @@ function foo12() { } } let x; -} \ No newline at end of file +} + +function foo13() { + let a = { + get a() { return x } + } + let x +} + +function foo14() { + let a = { + a: x + } + let x +} + +const enum A { X = B.Y } + +const enum B { Y } + +function foo15() { + const enum A1 { X = B1.Y } +} +const enum B1 { Y } \ No newline at end of file From 6bbfe56dca0aa814af957bc46763cf87c3f86f48 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 13 Oct 2015 13:16:44 -0700 Subject: [PATCH 24/34] Use `memberListCount` --- tests/cases/fourslash/tsxCompletion10.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/cases/fourslash/tsxCompletion10.ts b/tests/cases/fourslash/tsxCompletion10.ts index cf8b3068f10..f10e014c282 100644 --- a/tests/cases/fourslash/tsxCompletion10.ts +++ b/tests/cases/fourslash/tsxCompletion10.ts @@ -10,6 +10,5 @@ //// var x1 =
Date: Tue, 13 Oct 2015 13:18:58 -0700 Subject: [PATCH 25/34] revert enum related changes --- src/compiler/checker.ts | 5 ++--- .../blockScopedVariablesUseBeforeDef.errors.txt | 16 ++-------------- .../blockScopedVariablesUseBeforeDef.js | 13 +------------ .../compiler/blockScopedVariablesUseBeforeDef.ts | 11 +---------- 4 files changed, 6 insertions(+), 39 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9db3d49b9af..db9661289bd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13203,8 +13203,6 @@ namespace ts { let nodeLinks = getNodeLinks(node); if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { - nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; - let enumSymbol = getSymbolOfNode(node); let enumType = getDeclaredTypeOfSymbol(enumSymbol); let autoValue = 0; // set to undefined when enum member is non-constant @@ -13242,6 +13240,8 @@ namespace ts { getNodeLinks(member).enumMemberValue = autoValue++; } } + + nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; } function computeConstantValueForEnumMemberInitializer(initializer: Expression, enumType: Type, enumIsConst: boolean, ambient: boolean): number { @@ -13387,7 +13387,6 @@ namespace ts { return undefined; } - computeEnumMemberValues(propertyDecl.parent); return getNodeLinks(propertyDecl).enumMemberValue; } } diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt index 15697e9cf61..94c58bccb04 100644 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt @@ -2,10 +2,9 @@ tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(2,13): error TS2448: Bl tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(58,20): error TS2448: Block-scoped variable 'x' used before its declaration. tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(65,20): error TS2448: Block-scoped variable 'x' used before its declaration. tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(100,12): error TS2448: Block-scoped variable 'x' used before its declaration. -tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(105,20): error TS2651: A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums. -==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (5 errors) ==== +==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (4 errors) ==== function foo0() { let a = x; ~ @@ -116,15 +115,4 @@ tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(105,20): error TS2651: !!! error TS2448: Block-scoped variable 'x' used before its declaration. } let x - } - - const enum A { X = B.Y } - ~~~ -!!! error TS2651: A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums. - - const enum B { Y } - - function foo15() { - const enum A1 { X = B1.Y } - } - const enum B1 { Y } \ No newline at end of file + } \ No newline at end of file diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js index 2030b0eed59..5ed3ac62e6a 100644 --- a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js @@ -101,16 +101,7 @@ function foo14() { a: x } let x -} - -const enum A { X = B.Y } - -const enum B { Y } - -function foo15() { - const enum A1 { X = B1.Y } -} -const enum B1 { Y } +} //// [blockScopedVariablesUseBeforeDef.js] function foo0() { @@ -223,5 +214,3 @@ function foo14() { }; var x; } -function foo15() { -} diff --git a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts index 1cade31d238..956705bd7d3 100644 --- a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts +++ b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts @@ -101,13 +101,4 @@ function foo14() { a: x } let x -} - -const enum A { X = B.Y } - -const enum B { Y } - -function foo15() { - const enum A1 { X = B1.Y } -} -const enum B1 { Y } \ No newline at end of file +} \ No newline at end of file From 002f0c066b3d6f117bf418ffaf3b51397a0d2efc Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 15:10:05 -0700 Subject: [PATCH 26/34] CR feedback --- src/compiler/commandLineParser.ts | 6 +-- src/compiler/core.ts | 15 ++---- src/compiler/sys.ts | 75 ++++++++++++++--------------- src/compiler/tsc.ts | 78 +++++++++++++++++++------------ src/compiler/utilities.ts | 10 ++++ src/server/editorServices.ts | 11 ++--- src/services/shims.ts | 4 +- 7 files changed, 107 insertions(+), 92 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 11062983aaa..6af35ee30cc 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -389,7 +389,7 @@ namespace ts { catch (e) { return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) }; } - return parseConfigFileText(fileName, text); + return parseConfigFileTextToJson(fileName, text); } /** @@ -397,7 +397,7 @@ namespace ts { * @param fileName The path to the config file * @param jsonText The text of the config file */ - export function parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { + export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { try { return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} }; } @@ -412,7 +412,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseConfigFile(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine { + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine { let errors: Diagnostic[] = []; return { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a4c9a987267..0f7c09b4756 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -833,24 +833,15 @@ namespace ts { } } - export function doTwoArraysHaveTheSameElements(array1: Array, array2: Array): Boolean { + export function arrayStructurallyIsEqualTo(array1: Array, array2: Array): boolean { if (!array1 || !array2) { return false; } - if (array1.length != array2.length) { + if (array1.length !== array2.length) { return false; } - array1 = array1.sort(); - array2 = array2.sort(); - - for (let i = 0; i < array1.length; i++) { - if (array1[i] != array2[i]) { - return false; - } - } - - return true; + return arrayIsEqualTo(array1.sort(), array2.sort()); } } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index d191d406c52..4bc9af8d30e 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -199,32 +199,19 @@ namespace ts { const _path = require("path"); const _os = require("os"); - class WatchedFileSet { - private watchedFiles: WatchedFile[] = []; - private nextFileToCheck = 0; - private watchTimer: any; + // average async stat takes about 30 microseconds + // set chunk size to do 30 files in < 1 millisecond + function createWatchedFileSet(interval = 2500, chunkSize = 30) { + let watchedFiles: WatchedFile[] = []; + let nextFileToCheck = 0; + let watchTimer: any; - // average async stat takes about 30 microseconds - // set chunk size to do 30 files in < 1 millisecond - constructor(public interval = 2500, public chunkSize = 30) { - } - - private static copyListRemovingItem(item: T, list: T[]) { - let copiedList: T[] = []; - for (var i = 0, len = list.length; i < len; i++) { - if (list[i] != item) { - copiedList.push(list[i]); - } - } - return copiedList; - } - - private static getModifiedTime(fileName: string): Date { + function getModifiedTime(fileName: string): Date { return _fs.statSync(fileName).mtime; } - private poll(checkedIndex: number) { - let watchedFile = this.watchedFiles[checkedIndex]; + function poll(checkedIndex: number) { + let watchedFile = watchedFiles[checkedIndex]; if (!watchedFile) { return; } @@ -234,7 +221,7 @@ namespace ts { watchedFile.callback(watchedFile.fileName); } else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { - watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName); + watchedFile.mtime = getModifiedTime(watchedFile.fileName); watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0); } }); @@ -243,42 +230,50 @@ namespace ts { // this implementation uses polling and // stat due to inconsistencies of fs.watch // and efficiency of stat on modern filesystems - private startWatchTimer() { - this.watchTimer = setInterval(() => { + function startWatchTimer() { + watchTimer = setInterval(() => { let count = 0; - let nextToCheck = this.nextFileToCheck; + let nextToCheck = nextFileToCheck; let firstCheck = -1; - while ((count < this.chunkSize) && (nextToCheck !== firstCheck)) { - this.poll(nextToCheck); + while ((count < chunkSize) && (nextToCheck !== firstCheck)) { + poll(nextToCheck); if (firstCheck < 0) { firstCheck = nextToCheck; } nextToCheck++; - if (nextToCheck === this.watchedFiles.length) { + if (nextToCheck === watchedFiles.length) { nextToCheck = 0; } count++; } - this.nextFileToCheck = nextToCheck; - }, this.interval); + nextFileToCheck = nextToCheck; + }, interval); } - addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile { + function addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile { let file: WatchedFile = { fileName, callback, - mtime: WatchedFileSet.getModifiedTime(fileName) + mtime: getModifiedTime(fileName) }; - this.watchedFiles.push(file); - if (this.watchedFiles.length === 1) { - this.startWatchTimer(); + watchedFiles.push(file); + if (watchedFiles.length === 1) { + startWatchTimer(); } return file; } - removeFile(file: WatchedFile) { - this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles); + function removeFile(file: WatchedFile) { + watchedFiles = copyListRemovingItem(file, watchedFiles); + } + + return { + getModifiedTime: getModifiedTime, + poll: poll, + startWatchTimer: startWatchTimer, + addFile: addFile, + removeFile: removeFile } } @@ -295,7 +290,7 @@ namespace ts { // changes for large reference sets? If so, do we want // to increase the chunk size or decrease the interval // time dynamically to match the large reference set? - let watchedFileSet = new WatchedFileSet(); + let watchedFileSet = createWatchedFileSet(); function isNode4OrLater(): Boolean { return parseInt(process.version.charAt(1)) >= 4; @@ -417,7 +412,7 @@ namespace ts { // In watchDirectory we only care about adding and removing files (when event name is // "rename"); changes made within files are handled by corresponding fileWatchers (when // event name is "change") - if (eventName == "rename") { + if (eventName === "rename") { // When deleting a file, the passed baseFileName is null callback(!relativeFileName ? relativeFileName : normalizePath(ts.combinePaths(path, relativeFileName))); }; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 9a5e1c15294..9d79d435a04 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -147,15 +147,17 @@ namespace ts { export function executeCommandLine(args: string[]): void { let commandLine = parseCommandLine(args); - let configFileName: string; // Configuration file name (if any) - let configFileWatcher: FileWatcher; // Configuration file watcher - let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal - let cachedProgram: Program; // Program cached from last compilation - let rootFileNames: string[]; // Root fileNames for compilation - let compilerOptions: CompilerOptions; // Compiler options for compilation - let compilerHost: CompilerHost; // Compiler host - let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host - let timerHandle: number; // Handle for 0.25s wait timer + let configFileName: string; // Configuration file name (if any) + let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any) + let configFileWatcher: FileWatcher; // Configuration file watcher + let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal + let cachedProgram: Program; // Program cached from last compilation + let rootFileNames: string[]; // Root fileNames for compilation + let compilerOptions: CompilerOptions; // Compiler options for compilation + let compilerHost: CompilerHost; // Compiler host + let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host + let timerHandleForRecompilation: number; // Handle for 0.25s wait timer to trigger recompilation + let timerHandleForDirectoryChanges: number; // Handle for 0.25s wait timer to trigger directory change handler if (commandLine.options.locale) { if (!isJSONSupported()) { @@ -232,16 +234,22 @@ namespace ts { performCompilation(); - function configFileToParsedCommandLine(configFilename: string): ParsedCommandLine { - let result = readConfigFile(configFileName, sys.readFile); - if (result.error) { - reportWatchDiagnostic(result.error); - sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - return; + function parseConfigFile(): ParsedCommandLine { + if (!cachedConfigFileText) { + try { + cachedConfigFileText = sys.readFile(configFileName); + } + catch (e) { + let error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); + reportWatchDiagnostic(error); + sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return; + } } + let result = parseConfigFileTextToJson(configFileName, cachedConfigFileText); let configObject = result.config; - let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); + let configParseResult = parseJsonConfigFileContent(configObject, sys, getDirectoryPath(configFileName)); if (configParseResult.errors.length > 0) { reportDiagnostics(configParseResult.errors); sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); @@ -255,7 +263,7 @@ namespace ts { if (!cachedProgram) { if (configFileName) { - let configParseResult = configFileToParsedCommandLine(configFileName); + let configParseResult = parseConfigFile(); rootFileNames = configParseResult.fileNames; compilerOptions = extend(commandLine.options, configParseResult.options); } @@ -322,13 +330,14 @@ namespace ts { rootFileNames.splice(index, 1); } } - startTimer(); + startTimerForRecompilation(); } // If the configuration file changes, forget cached program and start the recompilation timer function configFileChanged() { setCachedProgram(undefined); - startTimer(); + cachedConfigFileText = undefined; + startTimerForRecompilation(); } function watchedDirectoryChanged(fileName: string) { @@ -336,28 +345,39 @@ namespace ts { return; } - let parsedCommandLine = configFileToParsedCommandLine(configFileName); - let newFileNames = parsedCommandLine.fileNames.map(compilerHost.getCanonicalFileName); - let canonicalRootFileNames = rootFileNames.map(compilerHost.getCanonicalFileName); + startTimerForHandlingDirectoryChanges(); + } - if (!doTwoArraysHaveTheSameElements(newFileNames, canonicalRootFileNames)) { + function startTimerForHandlingDirectoryChanges() { + if (timerHandleForDirectoryChanges) { + clearTimeout(timerHandleForDirectoryChanges); + } + timerHandleForDirectoryChanges = setTimeout(directoryChangeHandler, 250); + } + + function directoryChangeHandler() { + let parsedCommandLine = parseConfigFile(); + let newFileNames = ts.map(parsedCommandLine.fileNames, compilerHost.getCanonicalFileName); + let canonicalRootFileNames = ts.map(rootFileNames, compilerHost.getCanonicalFileName); + + if (!arrayStructurallyIsEqualTo(newFileNames, canonicalRootFileNames)) { setCachedProgram(undefined); - startTimer(); + startTimerForRecompilation(); } } // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch // operations (such as saving all modified files in an editor) a chance to complete before we kick // off a new compilation. - function startTimer() { - if (timerHandle) { - clearTimeout(timerHandle); + function startTimerForRecompilation() { + if (timerHandleForRecompilation) { + clearTimeout(timerHandleForRecompilation); } - timerHandle = setTimeout(recompile, 250); + timerHandleForRecompilation = setTimeout(recompile, 250); } function recompile() { - timerHandle = undefined; + timerHandleForRecompilation = undefined; reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation)); performCompilation(); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5127b98ab3a..5d0d1240734 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2406,4 +2406,14 @@ namespace ts { } } } + + export function copyListRemovingItem(item: T, list: T[]) { + var copiedList: T[] = []; + for (var i = 0, len = list.length; i < len; i++) { + if (list[i] != item) { + copiedList.push(list[i]); + } + } + return copiedList; + } } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 974f51906f6..0da23261db4 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -556,12 +556,11 @@ namespace ts.server { } this.log("Detected source file changes: " + fileName); - let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename); - let newRootFiles = projectOptions.files.map(f => this.getCanonicalFileName(f)); - let currentRootFiles = project.getRootFiles().map(f => this.getCanonicalFileName(f)); + let newRootFiles = ts.map(projectOptions.files, this.getCanonicalFileName); + let currentRootFiles = ts.map(project.getRootFiles(), this.getCanonicalFileName); - if (!doTwoArraysHaveTheSameElements(currentRootFiles, newRootFiles)) { + if (!arrayStructurallyIsEqualTo(currentRootFiles, newRootFiles)) { // For configured projects, the change is made outside the tsconfig file, and // it is not likely to affect the project for other files opened by the client. We can // just update the current project. @@ -1159,12 +1158,12 @@ namespace ts.server { // file references will be relative to dirPath (or absolute) var dirPath = ts.getDirectoryPath(configFilename); var contents = this.host.readFile(configFilename) - var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileText(configFilename, contents); + var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileTextToJson(configFilename, contents); if (rawConfig.error) { return { succeeded: false, error: rawConfig.error }; } else { - var parsedCommandLine = ts.parseConfigFile(rawConfig.config, this.host, dirPath); + var parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath); if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { return { succeeded: false, error: { errorMsg: "tsconfig option errors" } }; } diff --git a/src/services/shims.ts b/src/services/shims.ts index 351514a1499..e919625395d 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -990,7 +990,7 @@ namespace ts { () => { let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()); - let result = parseConfigFileText(fileName, text); + let result = parseConfigFileTextToJson(fileName, text); if (result.error) { return { @@ -1000,7 +1000,7 @@ namespace ts { }; } - var configFile = parseConfigFile(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName))); + var configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName))); return { options: configFile.options, From def268cccf383c9b26b20186926bd3076c30cb33 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 15:48:25 -0700 Subject: [PATCH 27/34] Fix issues with removing roots --- src/server/editorServices.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 0da23261db4..3cfdadd7785 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -230,6 +230,7 @@ namespace ts.server { if (scriptInfo) { this.filenameToScript[info.fileName] = undefined; this.roots = copyListRemovingItem(info, this.roots); + this.resolvedModuleNames.remove(info.fileName); } } @@ -557,8 +558,8 @@ namespace ts.server { this.log("Detected source file changes: " + fileName); let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename); - let newRootFiles = ts.map(projectOptions.files, this.getCanonicalFileName); - let currentRootFiles = ts.map(project.getRootFiles(), this.getCanonicalFileName); + let newRootFiles = projectOptions.files.map((f => this.getCanonicalFileName(f))); + let currentRootFiles = project.getRootFiles().map((f => this.getCanonicalFileName(f))); if (!arrayStructurallyIsEqualTo(currentRootFiles, newRootFiles)) { // For configured projects, the change is made outside the tsconfig file, and @@ -673,6 +674,9 @@ namespace ts.server { if (!info.isOpen) { this.filenameToScriptInfo[info.fileName] = undefined; var referencingProjects = this.findReferencingProjects(info); + if (info.defaultProject) { + info.defaultProject.removeRoot(info); + } for (var i = 0, len = referencingProjects.length; i < len; i++) { referencingProjects[i].removeReferencedFile(info); } @@ -1227,7 +1231,9 @@ namespace ts.server { for (let fileName of fileNamesToRemove) { let info = this.getScriptInfo(fileName); - project.removeRoot(info); + if (info) { + project.removeRoot(info); + } } for (let fileName of fileNamesToAdd) { From 62664fdedac9267111a976994f549e89e0b4b2f2 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 16:09:41 -0700 Subject: [PATCH 28/34] Add timer for batch processing directory changes --- src/server/editorServices.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3cfdadd7785..535d91aaf98 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -503,6 +503,7 @@ namespace ts.server { // number becomes 0 for a watcher, then we should close it. directoryWatchersRefCount: ts.Map = {}; hostConfiguration: HostConfiguration; + timerForDetectingProjectFilelistChanges: Map = {}; constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) { // ts.disableIncrementalParsing = true; @@ -557,6 +558,20 @@ namespace ts.server { } this.log("Detected source file changes: " + fileName); + this.startTimerForDetectingProjectFilelistChanges(project); + } + + startTimerForDetectingProjectFilelistChanges(project: Project) { + if (this.timerForDetectingProjectFilelistChanges[project.projectFilename]) { + clearTimeout(this.timerForDetectingProjectFilelistChanges[project.projectFilename]); + } + this.timerForDetectingProjectFilelistChanges[project.projectFilename] = setTimeout( + () => this.handleProjectFilelistChanges(project), + 250 + ); + } + + handleProjectFilelistChanges(project: Project) { let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename); let newRootFiles = projectOptions.files.map((f => this.getCanonicalFileName(f))); let currentRootFiles = project.getRootFiles().map((f => this.getCanonicalFileName(f))); From 6013968b1fe3fef37ad2fb79bd8d61c1b3b3e671 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 16:25:27 -0700 Subject: [PATCH 29/34] Address build errors --- src/compiler/core.ts | 16 +++++++--------- src/compiler/utilities.ts | 16 +++++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 0f7c09b4756..b57731d1f76 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -833,15 +833,13 @@ namespace ts { } } - export function arrayStructurallyIsEqualTo(array1: Array, array2: Array): boolean { - if (!array1 || !array2) { - return false; + export function copyListRemovingItem(item: T, list: T[]) { + let copiedList: T[] = []; + for (var i = 0, len = list.length; i < len; i++) { + if (list[i] != item) { + copiedList.push(list[i]); + } } - - if (array1.length !== array2.length) { - return false; - } - - return arrayIsEqualTo(array1.sort(), array2.sort()); + return copiedList; } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5d0d1240734..385e5c3123c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2407,13 +2407,15 @@ namespace ts { } } - export function copyListRemovingItem(item: T, list: T[]) { - var copiedList: T[] = []; - for (var i = 0, len = list.length; i < len; i++) { - if (list[i] != item) { - copiedList.push(list[i]); - } + export function arrayStructurallyIsEqualTo(array1: Array, array2: Array): boolean { + if (!array1 || !array2) { + return false; } - return copiedList; + + if (array1.length !== array2.length) { + return false; + } + + return arrayIsEqualTo(array1.sort(), array2.sort()); } } From c75499974ef5f2aee677907b2d944e3b462aabe8 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 16:31:27 -0700 Subject: [PATCH 30/34] Fix rwcRunner --- src/harness/rwcRunner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 3027afae9dc..19c989bcd6f 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -78,8 +78,8 @@ namespace RWC { let tsconfigFile = ts.forEach(ioLog.filesRead, f => isTsConfigFile(f) ? f : undefined); if (tsconfigFile) { let tsconfigFileContents = getHarnessCompilerInputUnit(tsconfigFile.path); - let parsedTsconfigFileContents = ts.parseConfigFileText(tsconfigFile.path, tsconfigFileContents.content); - let configParseResult = ts.parseConfigFile(parsedTsconfigFileContents.config, Harness.IO, ts.getDirectoryPath(tsconfigFile.path)); + let parsedTsconfigFileContents = ts.parseConfigFileTextToJson(tsconfigFile.path, tsconfigFileContents.content); + let configParseResult = ts.parseJsonConfigFileContent(parsedTsconfigFileContents.config, Harness.IO, ts.getDirectoryPath(tsconfigFile.path)); fileNames = configParseResult.fileNames; opts.options = ts.extend(opts.options, configParseResult.options); } From f91bee0324328a8f3d1b159968ae303a9536b44d Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 16:50:31 -0700 Subject: [PATCH 31/34] Re-read file content upon closing --- src/server/editorServices.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 436b97821cc..64050cd5457 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -683,6 +683,11 @@ namespace ts.server { * @param info The file that has been closed or newly configured */ closeOpenFile(info: ScriptInfo) { + // Closing file should trigger re-reading the file content from disk. This is + // because the user may chose to discard the buffer content before saving + // to the disk, and the server's version of the file can be out of sync. + info.svc.reloadFromFile(info.fileName); + var openFileRoots: ScriptInfo[] = []; var removedProject: Project; for (var i = 0, len = this.openFileRoots.length; i < len; i++) { From fcfc25eeb040390492716626757274c945ebd47f Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 16:57:08 -0700 Subject: [PATCH 32/34] Fix lint errors --- src/compiler/core.ts | 4 ++-- src/compiler/sys.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b57731d1f76..d0f51601b5e 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -833,7 +833,7 @@ namespace ts { } } - export function copyListRemovingItem(item: T, list: T[]) { + export function copyListRemovingItem(item: T, list: T[]) { let copiedList: T[] = []; for (var i = 0, len = list.length; i < len; i++) { if (list[i] != item) { @@ -842,4 +842,4 @@ namespace ts { } return copiedList; } -} +} \ No newline at end of file diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 4bc9af8d30e..0872a2e5ba5 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -274,7 +274,7 @@ namespace ts { startWatchTimer: startWatchTimer, addFile: addFile, removeFile: removeFile - } + }; } // REVIEW: for now this implementation uses polling. From b7c93c012f71dd1fafa6419d1203a6c9a4e79f9a Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 14 Oct 2015 17:50:29 -0700 Subject: [PATCH 33/34] Address CR from 5127 --- src/compiler/core.ts | 13 ++++++------- src/server/editorServices.ts | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a3b5ed1c45f..9a4f5452eed 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -739,13 +739,12 @@ namespace ts { export function isSupportedSourceFileName(fileName: string) { if (!fileName) { return false; } - let dotIndex = fileName.lastIndexOf("."); - if (dotIndex < 0) { - return false; + for (let extension of supportedExtensions) { + if (fileExtensionIs(fileName, extension)) { + return true; + } } - - let extension = fileName.slice(dotIndex, fileName.length); - return supportedExtensions.indexOf(extension) >= 0; + return false; } const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"]; @@ -846,7 +845,7 @@ namespace ts { export function copyListRemovingItem(item: T, list: T[]) { let copiedList: T[] = []; for (var i = 0, len = list.length; i < len; i++) { - if (list[i] != item) { + if (list[i] !== item) { copiedList.push(list[i]); } } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 8d4125f06a6..09f8a372463 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -735,7 +735,7 @@ namespace ts.server { if (!(--project.projectService.directoryWatchersRefCount[directory])) { this.log("Close directory watcher for: " + directory); project.projectService.directoryWatchersForTsconfig[directory].close(); - project.projectService.directoryWatchersForTsconfig[directory] = undefined; + delete project.projectService.directoryWatchersForTsconfig[directory]; } } this.inferredProjects = copyListRemovingItem(project, this.inferredProjects); From 53188d9cb8d00c333f3515525f729a7918b50013 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 15 Oct 2015 10:50:47 -0700 Subject: [PATCH 34/34] Add ES2015 as a synonym to ES6 In ModuleKind, ScriptTarget and associated command line arguments. --- src/compiler/commandLineParser.ts | 8 ++++++- src/compiler/types.ts | 4 +++- tests/baselines/reference/es2015modulekind.js | 23 +++++++++++++++++++ .../reference/es2015modulekind.symbols | 16 +++++++++++++ .../reference/es2015modulekind.types | 17 ++++++++++++++ .../es2015modulekindWithES6Target.js | 23 +++++++++++++++++++ .../es2015modulekindWithES6Target.symbols | 16 +++++++++++++ .../es2015modulekindWithES6Target.types | 17 ++++++++++++++ .../es6modulekindWithES2015Target.js | 23 +++++++++++++++++++ .../es6modulekindWithES2015Target.symbols | 16 +++++++++++++ .../es6modulekindWithES2015Target.types | 17 ++++++++++++++ tests/cases/compiler/es2015modulekind.ts | 17 ++++++++++++++ .../compiler/es2015modulekindWithES6Target.ts | 17 ++++++++++++++ .../compiler/es6modulekindWithES2015Target.ts | 17 ++++++++++++++ 14 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/es2015modulekind.js create mode 100644 tests/baselines/reference/es2015modulekind.symbols create mode 100644 tests/baselines/reference/es2015modulekind.types create mode 100644 tests/baselines/reference/es2015modulekindWithES6Target.js create mode 100644 tests/baselines/reference/es2015modulekindWithES6Target.symbols create mode 100644 tests/baselines/reference/es2015modulekindWithES6Target.types create mode 100644 tests/baselines/reference/es6modulekindWithES2015Target.js create mode 100644 tests/baselines/reference/es6modulekindWithES2015Target.symbols create mode 100644 tests/baselines/reference/es6modulekindWithES2015Target.types create mode 100644 tests/cases/compiler/es2015modulekind.ts create mode 100644 tests/cases/compiler/es2015modulekindWithES6Target.ts create mode 100644 tests/cases/compiler/es6modulekindWithES2015Target.ts diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 37755ee3258..acf0474b7bf 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -77,6 +77,7 @@ namespace ts { "system": ModuleKind.System, "umd": ModuleKind.UMD, "es6": ModuleKind.ES6, + "es2015": ModuleKind.ES2015, }, description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es6, paramType: Diagnostics.KIND, @@ -205,7 +206,12 @@ namespace ts { { name: "target", shortName: "t", - type: { "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6 }, + type: { + "es3": ScriptTarget.ES3, + "es5": ScriptTarget.ES5, + "es6": ScriptTarget.ES6, + "es2015": ScriptTarget.ES2015, + }, description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES6_experimental, paramType: Diagnostics.VERSION, error: Diagnostics.Argument_for_target_option_must_be_ES3_ES5_or_ES6 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6e5528b8217..c44c6ad2cc0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2103,6 +2103,7 @@ namespace ts { UMD = 3, System = 4, ES6 = 5, + ES2015 = ES6, } export const enum JsxEmit { @@ -2128,12 +2129,13 @@ namespace ts { ES3 = 0, ES5 = 1, ES6 = 2, + ES2015 = ES6, Latest = ES6, } export const enum LanguageVariant { Standard, - JSX + JSX, } export interface ParsedCommandLine { diff --git a/tests/baselines/reference/es2015modulekind.js b/tests/baselines/reference/es2015modulekind.js new file mode 100644 index 00000000000..af638557fde --- /dev/null +++ b/tests/baselines/reference/es2015modulekind.js @@ -0,0 +1,23 @@ +//// [es2015modulekind.ts] + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} + +//// [es2015modulekind.js] +export default class A { + constructor() { + } + B() { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekind.symbols b/tests/baselines/reference/es2015modulekind.symbols new file mode 100644 index 00000000000..227443ac06b --- /dev/null +++ b/tests/baselines/reference/es2015modulekind.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/es2015modulekind.ts === + +export default class A +>A : Symbol(A, Decl(es2015modulekind.ts, 0, 0)) +{ + constructor () + { + + } + + public B() +>B : Symbol(B, Decl(es2015modulekind.ts, 6, 5)) + { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekind.types b/tests/baselines/reference/es2015modulekind.types new file mode 100644 index 00000000000..e77cf095aed --- /dev/null +++ b/tests/baselines/reference/es2015modulekind.types @@ -0,0 +1,17 @@ +=== tests/cases/compiler/es2015modulekind.ts === + +export default class A +>A : A +{ + constructor () + { + + } + + public B() +>B : () => number + { + return 42; +>42 : number + } +} diff --git a/tests/baselines/reference/es2015modulekindWithES6Target.js b/tests/baselines/reference/es2015modulekindWithES6Target.js new file mode 100644 index 00000000000..b76ab2a02a2 --- /dev/null +++ b/tests/baselines/reference/es2015modulekindWithES6Target.js @@ -0,0 +1,23 @@ +//// [es2015modulekindWithES6Target.ts] + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} + +//// [es2015modulekindWithES6Target.js] +export default class A { + constructor() { + } + B() { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekindWithES6Target.symbols b/tests/baselines/reference/es2015modulekindWithES6Target.symbols new file mode 100644 index 00000000000..2ff208ac704 --- /dev/null +++ b/tests/baselines/reference/es2015modulekindWithES6Target.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/es2015modulekindWithES6Target.ts === + +export default class A +>A : Symbol(A, Decl(es2015modulekindWithES6Target.ts, 0, 0)) +{ + constructor () + { + + } + + public B() +>B : Symbol(B, Decl(es2015modulekindWithES6Target.ts, 6, 5)) + { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekindWithES6Target.types b/tests/baselines/reference/es2015modulekindWithES6Target.types new file mode 100644 index 00000000000..50b85921b0e --- /dev/null +++ b/tests/baselines/reference/es2015modulekindWithES6Target.types @@ -0,0 +1,17 @@ +=== tests/cases/compiler/es2015modulekindWithES6Target.ts === + +export default class A +>A : A +{ + constructor () + { + + } + + public B() +>B : () => number + { + return 42; +>42 : number + } +} diff --git a/tests/baselines/reference/es6modulekindWithES2015Target.js b/tests/baselines/reference/es6modulekindWithES2015Target.js new file mode 100644 index 00000000000..98808fd1c65 --- /dev/null +++ b/tests/baselines/reference/es6modulekindWithES2015Target.js @@ -0,0 +1,23 @@ +//// [es6modulekindWithES2015Target.ts] + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} + +//// [es6modulekindWithES2015Target.js] +export default class A { + constructor() { + } + B() { + return 42; + } +} diff --git a/tests/baselines/reference/es6modulekindWithES2015Target.symbols b/tests/baselines/reference/es6modulekindWithES2015Target.symbols new file mode 100644 index 00000000000..98b91a0411b --- /dev/null +++ b/tests/baselines/reference/es6modulekindWithES2015Target.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/es6modulekindWithES2015Target.ts === + +export default class A +>A : Symbol(A, Decl(es6modulekindWithES2015Target.ts, 0, 0)) +{ + constructor () + { + + } + + public B() +>B : Symbol(B, Decl(es6modulekindWithES2015Target.ts, 6, 5)) + { + return 42; + } +} diff --git a/tests/baselines/reference/es6modulekindWithES2015Target.types b/tests/baselines/reference/es6modulekindWithES2015Target.types new file mode 100644 index 00000000000..63acdae43d1 --- /dev/null +++ b/tests/baselines/reference/es6modulekindWithES2015Target.types @@ -0,0 +1,17 @@ +=== tests/cases/compiler/es6modulekindWithES2015Target.ts === + +export default class A +>A : A +{ + constructor () + { + + } + + public B() +>B : () => number + { + return 42; +>42 : number + } +} diff --git a/tests/cases/compiler/es2015modulekind.ts b/tests/cases/compiler/es2015modulekind.ts new file mode 100644 index 00000000000..6e1f584844a --- /dev/null +++ b/tests/cases/compiler/es2015modulekind.ts @@ -0,0 +1,17 @@ +// @target: es2015 +// @sourcemap: false +// @declaration: false +// @module: es2015 + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/es2015modulekindWithES6Target.ts b/tests/cases/compiler/es2015modulekindWithES6Target.ts new file mode 100644 index 00000000000..78809d3835f --- /dev/null +++ b/tests/cases/compiler/es2015modulekindWithES6Target.ts @@ -0,0 +1,17 @@ +// @target: es6 +// @sourcemap: false +// @declaration: false +// @module: es2015 + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/es6modulekindWithES2015Target.ts b/tests/cases/compiler/es6modulekindWithES2015Target.ts new file mode 100644 index 00000000000..aaf79e6607d --- /dev/null +++ b/tests/cases/compiler/es6modulekindWithES2015Target.ts @@ -0,0 +1,17 @@ +// @target: es2015 +// @sourcemap: false +// @declaration: false +// @module: es6 + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} \ No newline at end of file