diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index e2d59975d04..f4a61267144 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -65,7 +65,8 @@ namespace ts { } state.changedFilesSet = createMap(); const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState); - const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile; + const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile && + !compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldState!.program.getCompilerOptions()); if (useOldState) { // Verify the sanity of old state if (!oldState!.currentChangedFilePath) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 56ea3cace0c..dcc4f968f26 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6967,6 +6967,49 @@ namespace ts { return compilerOptions[flag] === undefined ? !!compilerOptions.strict : !!compilerOptions[flag]; } + export function compilerOptionsAffectSemanticDiagnostics(newOptions: CompilerOptions, oldOptions: CompilerOptions) { + if (oldOptions === newOptions) { + return false; + } + + return changedCompileOptionValueOf(newOptions, oldOptions, [ + "noImplicitReturns", + "strict", + "suppressExcessPropertyErrors", + "suppressImplicitAnyIndexErrors", + "noFallthroughCasesInSwitch", + "noStrictGenericChecks", + "noUnusedLocals", + "noUnusedParameters", + "noImplicitUseStrict" + ]) || changedStrictOptionValueOf(newOptions, oldOptions, [ + "noImplicitAny", + "noImplicitThis", + "strictNullChecks", + "strictFunctionTypes", + "strictPropertyInitialization", + "alwaysStrict" + ]); + } + + function changedStrictOptionValueOf(newOptions: CompilerOptions, oldOptions: CompilerOptions, flags: StrictOptionName[]) { + for (const flag of flags) { + if (getStrictOptionValue(newOptions, flag) !== getStrictOptionValue(oldOptions, flag)) { + return true; + } + } + return false; + } + + function changedCompileOptionValueOf(newOptions: CompilerOptions, oldOptions: CompilerOptions, optionKeys: (keyof CompilerOptions)[]) { + for (const optionKey of optionKeys) { + if (newOptions[optionKey] !== oldOptions[optionKey]) { + return true; + } + } + return false; + } + export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { diff --git a/src/testRunner/unittests/tscWatchMode.ts b/src/testRunner/unittests/tscWatchMode.ts index 1456a621019..d91559c72fc 100644 --- a/src/testRunner/unittests/tscWatchMode.ts +++ b/src/testRunner/unittests/tscWatchMode.ts @@ -1312,6 +1312,49 @@ export class B // File a need not be rewritten assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); }); + + it("updates errors when strictNullChecks changes", () => { + const currentDirectory = "/user/username/projects/myproject"; + const aFile: File = { + path: `${currentDirectory}/a.ts`, + content: `declare function foo(): null | { hello: any }; +foo().hello` + }; + const compilerOptions: CompilerOptions = { + }; + const config: File = { + path: `${currentDirectory}/tsconfig.json`, + content: JSON.stringify({ compilerOptions }) + }; + const files = [aFile, config, libFile]; + const host = createWatchedSystem(files, { currentDirectory }); + const watch = createWatchOfConfigFile("tsconfig.json", host); + checkProgramActualFiles(watch(), [aFile.path, libFile.path]); + checkOutputErrorsInitial(host, emptyArray); + const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`); + compilerOptions.strictNullChecks = true; + host.writeFile(config.path, JSON.stringify({ compilerOptions })); + host.runQueuedTimeoutCallbacks(); + const expectedStrictNullErrors = [ + getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("foo()"), 5, Diagnostics.Object_is_possibly_null) + ]; + checkOutputErrorsIncremental(host, expectedStrictNullErrors); + // File a need not be rewritten + assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); + compilerOptions.strict = true; + delete (compilerOptions.strictNullChecks); + host.writeFile(config.path, JSON.stringify({ compilerOptions })); + host.runQueuedTimeoutCallbacks(); + checkOutputErrorsIncremental(host, expectedStrictNullErrors); + // File a need not be rewritten + assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); + delete (compilerOptions.strict); + host.writeFile(config.path, JSON.stringify({ compilerOptions })); + host.runQueuedTimeoutCallbacks(); + checkOutputErrorsIncremental(host, emptyArray); + // File a need not be rewritten + assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); + }); }); describe("tsc-watch emit with outFile or out setting", () => {