From a27b29f025c1cd37b3c971f851dcc53b5fbdf70f Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 9 Nov 2018 12:23:54 -0800 Subject: [PATCH] Remove lib file errors when all files are to be emitted. Fixes #26389 --- src/compiler/builder.ts | 26 ++++- src/testRunner/unittests/tscWatchMode.ts | 119 +++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 2de77fa554..b5f0339c05 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -38,6 +38,10 @@ namespace ts { * Already seen affected files */ seenAffectedFiles: Map | undefined; + /** + * program corresponding to this state + */ + cleanedDiagnosticsOfLibFiles?: boolean; /** * True if the semantic diagnostics were copied from the old state */ @@ -83,6 +87,8 @@ namespace ts { // Update changed files and copy semantic diagnostics if we can const referencedMap = state.referencedMap; const oldReferencedMap = useOldState ? oldState!.referencedMap : undefined; + const copyDeclarationFileDiagnostics = canCopySemanticDiagnostics && !compilerOptions.skipLibCheck === !oldState!.program.getCompilerOptions().skipLibCheck; + const copyLibFileDiagnostics = copyDeclarationFileDiagnostics && !compilerOptions.skipDefaultLibCheck === !oldState!.program.getCompilerOptions().skipDefaultLibCheck; state.fileInfos.forEach((info, sourceFilePath) => { let oldInfo: Readonly | undefined; let newReferences: BuilderState.ReferencedSet | undefined; @@ -101,6 +107,11 @@ namespace ts { state.changedFilesSet.set(sourceFilePath, true); } else if (canCopySemanticDiagnostics) { + const sourceFile = state.program.getSourceFileByPath(sourceFilePath as Path)!; + + if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics) { return; } + if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics) { return; } + // Unchanged file copy diagnostics const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath); if (diagnostics) { @@ -193,6 +204,19 @@ namespace ts { return; } + // Clean lib file diagnostics if its all files excluding default files to emit + if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles && !state.cleanedDiagnosticsOfLibFiles) { + state.cleanedDiagnosticsOfLibFiles = true; + const options = state.program.getCompilerOptions(); + if (forEach(state.program.getSourceFiles(), f => + !contains(state.allFilesExcludingDefaultLibraryFile, f) && + !skipTypeChecking(f, options) && + removeSemanticDiagnosticsOf(state, f.path) + )) { + return; + } + } + // If there was change in signature for the changed file, // then delete the semantic diagnostics for files that are affected by using exports of this module @@ -268,7 +292,7 @@ namespace ts { */ function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) { if (!state.semanticDiagnosticsFromOldState) { - return false; + return true; } state.semanticDiagnosticsFromOldState.delete(path); state.semanticDiagnosticsPerFile!.delete(path); diff --git a/src/testRunner/unittests/tscWatchMode.ts b/src/testRunner/unittests/tscWatchMode.ts index f77b4bce0e..1e65e7b9e9 100644 --- a/src/testRunner/unittests/tscWatchMode.ts +++ b/src/testRunner/unittests/tscWatchMode.ts @@ -1564,6 +1564,125 @@ export class Data2 { verifyTransitiveExports([libFile, app, lib2Public, lib2Data, lib2Data2, lib1Public, lib1ToolsPublic, lib1ToolsInterface]); }); }); + + describe("updates errors in lib file when non module file changes", () => { + const currentDirectory = "/user/username/projects/myproject"; + const field = "fullscreen"; + const aFile: File = { + path: `${currentDirectory}/a.ts`, + content: `interface Document { + ${field}: boolean; +}` + }; + const libFileWithDocument: File = { + path: libFile.path, + content: `${libFile.content} +interface Document { + readonly ${field}: boolean; +}` + }; + + function getDiagnostic(program: Program, file: File) { + return getDiagnosticOfFileFromProgram(program, file.path, file.content.indexOf(field), field.length, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, field); + } + + const files = [aFile, libFileWithDocument]; + + function verifyLibErrors(options: CompilerOptions) { + const host = createWatchedSystem(files, { currentDirectory }); + const watch = createWatchOfFilesAndCompilerOptions([aFile.path], host, options); + checkProgramActualFiles(watch(), [aFile.path, libFile.path]); + checkOutputErrorsInitial(host, getErrors()); + + host.writeFile(aFile.path, "var x = 10;"); + host.runQueuedTimeoutCallbacks(); + checkProgramActualFiles(watch(), [aFile.path, libFile.path]); + checkOutputErrorsIncremental(host, emptyArray); + + host.writeFile(aFile.path, aFile.content); + host.runQueuedTimeoutCallbacks(); + checkProgramActualFiles(watch(), [aFile.path, libFile.path]); + checkOutputErrorsIncremental(host, getErrors()); + + function getErrors() { + return [ + ...(options.skipLibCheck || options.skipDefaultLibCheck ? [] : [getDiagnostic(watch(), libFileWithDocument)]), + getDiagnostic(watch(), aFile) + ]; + } + } + + it("with default options", () => { + verifyLibErrors({}); + }); + it("with skipLibCheck", () => { + verifyLibErrors({ skipLibCheck: true }); + }); + it("with skipDefaultLibCheck", () => { + verifyLibErrors({ skipDefaultLibCheck: true }); + }); + }); + + it("when skipLibCheck and skipDefaultLibCheck changes", () => { + const currentDirectory = "/user/username/projects/myproject"; + const field = "fullscreen"; + const aFile: File = { + path: `${currentDirectory}/a.ts`, + content: `interface Document { + ${field}: boolean; +}` + }; + const bFile: File = { + path: `${currentDirectory}/b.d.ts`, + content: `interface Document { + ${field}: boolean; +}` + }; + const libFileWithDocument: File = { + path: libFile.path, + content: `${libFile.content} +interface Document { + readonly ${field}: boolean; +}` + }; + const configFile: File = { + path: `${currentDirectory}/tsconfig.json`, + content: "{}" + }; + + const files = [aFile, bFile, configFile, libFileWithDocument]; + + const host = createWatchedSystem(files, { currentDirectory }); + const watch = createWatchOfConfigFile("tsconfig.json", host); + verifyProgramFiles(); + checkOutputErrorsInitial(host, [ + getDiagnostic(libFileWithDocument), + getDiagnostic(aFile), + getDiagnostic(bFile) + ]); + + verifyConfigChange({ skipLibCheck: true }, [aFile]); + verifyConfigChange({ skipDefaultLibCheck: true }, [aFile, bFile]); + verifyConfigChange({}, [libFileWithDocument, aFile, bFile]); + verifyConfigChange({ skipDefaultLibCheck: true }, [aFile, bFile]); + verifyConfigChange({ skipLibCheck: true }, [aFile]); + verifyConfigChange({}, [libFileWithDocument, aFile, bFile]); + + function verifyConfigChange(compilerOptions: CompilerOptions, errorInFiles: ReadonlyArray) { + host.writeFile(configFile.path, JSON.stringify({ compilerOptions })); + host.runQueuedTimeoutCallbacks(); + verifyProgramFiles(); + checkOutputErrorsIncremental(host, errorInFiles.map(getDiagnostic)); + } + + function getDiagnostic(file: File) { + return getDiagnosticOfFileFromProgram(watch(), file.path, file.content.indexOf(field), field.length, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, field); + } + + function verifyProgramFiles() { + checkProgramActualFiles(watch(), [aFile.path, bFile.path, libFile.path]); + } + }); }); describe("tsc-watch emit with outFile or out setting", () => {