From bd97e12f760773aac87d62159fa26ea1474a2337 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 22 Jun 2018 14:05:36 -0700 Subject: [PATCH] Multifaceted approach to performantly enabling fileExists outside of the synchronize step in the emit host (#25107) * Multifaceted approach to performantly enabling fileExists outside of the synchronize step in the emit host * make cache undefinable and handle correctly * Remove unneeded cast * Readd assert * More useful failure messager --- src/compiler/program.ts | 9 ++++++++- src/services/services.ts | 14 +++++++------- ...portTypesDeclarationDiagnosticsNoServerError.ts | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 tests/cases/fourslash/importTypesDeclarationDiagnosticsNoServerError.ts diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 853609da31e..b1a94e8e203 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1239,7 +1239,14 @@ namespace ts { (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, readFile: f => host.readFile(f), - fileExists: f => host.fileExists(f), + fileExists: f => { + // Use local caches + const path = toPath(f); + if (getSourceFileByPath(path)) return true; + if (contains(missingFilePaths, path)) return false; + // Before falling back to the host + return host.fileExists(f); + }, ...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}), }; } diff --git a/src/services/services.ts b/src/services/services.ts index 2e275813585..67241270e9e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1171,13 +1171,13 @@ namespace ts { } // Get a fresh cache of the host information - let hostCache = new HostCache(host, getCanonicalFileName); + let hostCache: HostCache | undefined = new HostCache(host, getCanonicalFileName); const rootFileNames = hostCache.getRootFileNames(); const hasInvalidatedResolution: HasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse; // If the program is already up-to-date, we can reuse it - if (isProgramUptoDate(program, rootFileNames, hostCache.compilationSettings(), path => hostCache.getVersion(path), fileExists, hasInvalidatedResolution, !!host.hasChangedAutomaticTypeDirectiveNames)) { + if (isProgramUptoDate(program, rootFileNames, hostCache.compilationSettings(), path => hostCache!.getVersion(path), fileExists, hasInvalidatedResolution, !!host.hasChangedAutomaticTypeDirectiveNames)) { return; } @@ -1204,7 +1204,7 @@ namespace ts { readFile(fileName) { // stub missing host functionality const path = toPath(fileName, currentDirectory, getCanonicalFileName); - const entry = hostCache.getEntryByPath(path); + const entry = hostCache && hostCache.getEntryByPath(path); if (entry) { return isString(entry) ? undefined : getSnapshotText(entry.scriptSnapshot); } @@ -1246,7 +1246,7 @@ namespace ts { // hostCache is captured in the closure for 'getOrCreateSourceFile' but it should not be used past this point. // It needs to be cleared to allow all collected snapshots to be released - hostCache = undefined!; + hostCache = undefined; // We reset this cache on structure invalidation so we don't hold on to outdated files for long; however we can't use the `compilerHost` above, // Because it only functions until `hostCache` is cleared, while we'll potentially need the functionality to lazily read sourcemap files during @@ -1260,7 +1260,7 @@ namespace ts { function fileExists(fileName: string): boolean { const path = toPath(fileName, currentDirectory, getCanonicalFileName); - const entry = hostCache.getEntryByPath(path); + const entry = hostCache && hostCache.getEntryByPath(path); return entry ? !isString(entry) : (!!host.fileExists && host.fileExists(fileName)); @@ -1278,11 +1278,11 @@ namespace ts { } function getOrCreateSourceFileByPath(fileName: string, path: Path, _languageVersion: ScriptTarget, _onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined { - Debug.assert(hostCache !== undefined); + Debug.assert(hostCache !== undefined, "getOrCreateSourceFileByPath called after typical CompilerHost lifetime, check the callstack something with a reference to an old host."); // The program is asking for this file, check first if the host can locate it. // If the host can not locate the file, then it does not exist. return undefined // to the program to allow reporting of errors for missing files. - const hostFileInformation = hostCache.getOrCreateEntryByPath(fileName, path); + const hostFileInformation = hostCache && hostCache.getOrCreateEntryByPath(fileName, path); if (!hostFileInformation) { return undefined; } diff --git a/tests/cases/fourslash/importTypesDeclarationDiagnosticsNoServerError.ts b/tests/cases/fourslash/importTypesDeclarationDiagnosticsNoServerError.ts new file mode 100644 index 00000000000..78ad677a57c --- /dev/null +++ b/tests/cases/fourslash/importTypesDeclarationDiagnosticsNoServerError.ts @@ -0,0 +1,14 @@ +/// + +// @declaration: true +// @Filename: node_modules/foo/index.d.ts +////export function f(): I; +////export interface I { +//// x: number; +////} +// @Filename: a.ts +////import { f } from "foo"; +////export const x = f(); + +goTo.file(1); +verify.getSemanticDiagnostics([]);