diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index e2d59975d042a..f4a6126714438 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/commandLineParser.ts b/src/compiler/commandLineParser.ts index 55f224caec490..00920325721a0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -319,13 +319,15 @@ namespace ts { { name: "noImplicitAny", type: "boolean", + strictFlag: true, showInSimplifiedHelpView: true, category: Diagnostics.Strict_Type_Checking_Options, - description: Diagnostics.Raise_error_on_expressions_and_declarations_with_an_implied_any_type, + description: Diagnostics.Raise_error_on_expressions_and_declarations_with_an_implied_any_type }, { name: "strictNullChecks", type: "boolean", + strictFlag: true, showInSimplifiedHelpView: true, category: Diagnostics.Strict_Type_Checking_Options, description: Diagnostics.Enable_strict_null_checks @@ -333,6 +335,7 @@ namespace ts { { name: "strictFunctionTypes", type: "boolean", + strictFlag: true, showInSimplifiedHelpView: true, category: Diagnostics.Strict_Type_Checking_Options, description: Diagnostics.Enable_strict_checking_of_function_types @@ -340,6 +343,7 @@ namespace ts { { name: "strictPropertyInitialization", type: "boolean", + strictFlag: true, showInSimplifiedHelpView: true, category: Diagnostics.Strict_Type_Checking_Options, description: Diagnostics.Enable_strict_checking_of_property_initialization_in_classes @@ -347,6 +351,7 @@ namespace ts { { name: "noImplicitThis", type: "boolean", + strictFlag: true, showInSimplifiedHelpView: true, category: Diagnostics.Strict_Type_Checking_Options, description: Diagnostics.Raise_error_on_this_expressions_with_an_implied_any_type, @@ -354,6 +359,7 @@ namespace ts { { name: "alwaysStrict", type: "boolean", + strictFlag: true, showInSimplifiedHelpView: true, category: Diagnostics.Strict_Type_Checking_Options, description: Diagnostics.Parse_in_strict_mode_and_emit_use_strict_for_each_source_file @@ -363,6 +369,7 @@ namespace ts { { name: "noUnusedLocals", type: "boolean", + affectsSemanticDiagnostics: true, showInSimplifiedHelpView: true, category: Diagnostics.Additional_Checks, description: Diagnostics.Report_errors_on_unused_locals, @@ -370,6 +377,7 @@ namespace ts { { name: "noUnusedParameters", type: "boolean", + affectsSemanticDiagnostics: true, showInSimplifiedHelpView: true, category: Diagnostics.Additional_Checks, description: Diagnostics.Report_errors_on_unused_parameters, @@ -377,6 +385,7 @@ namespace ts { { name: "noImplicitReturns", type: "boolean", + affectsSemanticDiagnostics: true, showInSimplifiedHelpView: true, category: Diagnostics.Additional_Checks, description: Diagnostics.Report_error_when_not_all_code_paths_in_function_return_a_value @@ -384,6 +393,7 @@ namespace ts { { name: "noFallthroughCasesInSwitch", type: "boolean", + affectsSemanticDiagnostics: true, showInSimplifiedHelpView: true, category: Diagnostics.Additional_Checks, description: Diagnostics.Report_errors_for_fallthrough_cases_in_switch_statement @@ -455,12 +465,14 @@ namespace ts { { name: "allowSyntheticDefaultImports", type: "boolean", + affectsSemanticDiagnostics: true, category: Diagnostics.Module_Resolution_Options, description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking }, { name: "esModuleInterop", type: "boolean", + affectsSemanticDiagnostics: true, showInSimplifiedHelpView: true, category: Diagnostics.Module_Resolution_Options, description: Diagnostics.Enables_emit_interoperability_between_CommonJS_and_ES_Modules_via_creation_of_namespace_objects_for_all_imports_Implies_allowSyntheticDefaultImports @@ -640,6 +652,7 @@ namespace ts { { name: "noImplicitUseStrict", type: "boolean", + affectsSemanticDiagnostics: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Do_not_emit_use_strict_directives_in_module_output }, @@ -678,24 +691,28 @@ namespace ts { { name: "allowUnusedLabels", type: "boolean", + affectsSemanticDiagnostics: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Do_not_report_errors_on_unused_labels }, { name: "allowUnreachableCode", type: "boolean", + affectsSemanticDiagnostics: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Do_not_report_errors_on_unreachable_code }, { name: "suppressExcessPropertyErrors", type: "boolean", + affectsSemanticDiagnostics: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Suppress_excess_property_checks_for_object_literals, }, { name: "suppressImplicitAnyIndexErrors", type: "boolean", + affectsSemanticDiagnostics: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Suppress_noImplicitAny_errors_for_indexing_objects_lacking_index_signatures, }, @@ -714,6 +731,7 @@ namespace ts { { name: "noStrictGenericChecks", type: "boolean", + affectsSemanticDiagnostics: true, category: Diagnostics.Advanced_Options, description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types, }, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f66c0c02e3946..2fb504fef2729 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4540,6 +4540,8 @@ namespace ts { isCommandLineOnly?: boolean; showInSimplifiedHelpView?: boolean; category?: DiagnosticMessage; + strictFlag?: true; // true if the option is one of the flag under strict + affectsSemanticDiagnostics?: true; // true if option affects semantic diagnostics } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 56ea3cace0c39..97a71897f1f8b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6967,6 +6967,15 @@ namespace ts { return compilerOptions[flag] === undefined ? !!compilerOptions.strict : !!compilerOptions[flag]; } + export function compilerOptionsAffectSemanticDiagnostics(newOptions: CompilerOptions, oldOptions: CompilerOptions) { + if (oldOptions === newOptions) { + return false; + } + + return optionDeclarations.some(option => (!!option.strictFlag && getStrictOptionValue(newOptions, option.name as StrictOptionName) !== getStrictOptionValue(oldOptions, option.name as StrictOptionName)) || + (!!option.affectsSemanticDiagnostics && !newOptions[option.name] !== !oldOptions[option.name])); + } + 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 8a3b732ad979f..d91559c72fc45 100644 --- a/src/testRunner/unittests/tscWatchMode.ts +++ b/src/testRunner/unittests/tscWatchMode.ts @@ -1141,7 +1141,6 @@ namespace ts.tscWatch { } it("without outDir or outFile is specified", () => { - debugger; verifyWithOptions({ module: ModuleKind.AMD }, ["file1.js", "src/file2.js"]); }); @@ -1313,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", () => { diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index c89d29ffc5b51..04e34d5a89851 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -9333,7 +9333,7 @@ export function Test2() { textSpan: protocolTextSpanFromSubstring(userTs.content, "fnA", { index: 1 }), definitions: [protocolFileSpanFromSubstring(aTs, "fnA")], }); - checkNumberOfProjects(session.getProjectService(), { configuredProjects: 1 }); debugger; + checkNumberOfProjects(session.getProjectService(), { configuredProjects: 1 }); verifyUserTsConfigProject(session); // Navigate to the definition