From b8f33f6a35e6659912f39055c4844e786d581c86 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 12 Sep 2018 16:09:22 -0700 Subject: [PATCH] Report all project errors on incremental compile --- src/compiler/tsbuild.ts | 65 ++++++++++---------- src/testRunner/unittests/tsbuildWatchMode.ts | 27 ++++++++ src/testRunner/unittests/tscWatchMode.ts | 18 +++--- 3 files changed, 72 insertions(+), 38 deletions(-) diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index d7eb648bca2..2c064bea137 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -394,9 +394,9 @@ namespace ts { let globalDependencyGraph: DependencyGraph | undefined; // Watch state - // TODO(shkamat): this should be really be diagnostics but thats for later time - const diagnostics = createFileMap(toPath); + const diagnostics = createFileMap>(toPath); const projectPendingBuild = createFileMap(toPath); + const projectErrorsReported = createFileMap(toPath); const invalidatedProjectQueue = [] as ResolvedConfigFileName[]; let nextProjectToBuild = 0; let timerToBuildInvalidatedProject: any; @@ -438,6 +438,7 @@ namespace ts { diagnostics.clear(); projectPendingBuild.clear(); + projectErrorsReported.clear(); invalidatedProjectQueue.length = 0; nextProjectToBuild = 0; if (timerToBuildInvalidatedProject) { @@ -472,18 +473,6 @@ namespace ts { host.reportSolutionBuilderStatus(createCompilerDiagnostic(message, ...args)); } - function storeErrors(proj: ResolvedConfigFileName, diagnostics: ReadonlyArray) { - if (options.watch) { - storeErrorSummary(proj, diagnostics.filter(diagnostic => diagnostic.category === DiagnosticCategory.Error).length); - } - } - - function storeErrorSummary(proj: ResolvedConfigFileName, errorCount: number) { - if (options.watch) { - diagnostics.setValue(proj, errorCount); - } - } - function reportWatchStatus(message: DiagnosticMessage, ...args: (string | number | undefined)[]) { if (hostWithWatch.onWatchStatusChange) { hostWithWatch.onWatchStatusChange(createCompilerDiagnostic(message, ...args), host.getNewLine(), { preserveWatchOutput: options.preserveWatchOutput }); @@ -509,7 +498,7 @@ namespace ts { } function watchConfigFile(resolved: ResolvedConfigFileName) { - if (!allWatchedConfigFiles.hasKey(resolved)) { + if (options.watch && !allWatchedConfigFiles.hasKey(resolved)) { allWatchedConfigFiles.setValue(resolved, hostWithWatch.watchFile(resolved, () => { invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.Full); })); @@ -517,6 +506,7 @@ namespace ts { } function watchWildCardDirectories(resolved: ResolvedConfigFileName, parsed: ParsedCommandLine) { + if (!options.watch) return; updateWatchingWildcardDirectories( getOrCreateValueMapFromConfigFileMap(allWatchedWildcardDirectories, resolved), createMapFromTemplate(parsed.configFileSpecs!.wildcardDirectories), @@ -540,6 +530,7 @@ namespace ts { } function watchInputFiles(resolved: ResolvedConfigFileName, parsed: ParsedCommandLine) { + if (!options.watch) return; mutateMap( getOrCreateValueMapFromConfigFileMap(allWatchedInputFiles, resolved), arrayToMap(parsed.fileNames, toPath), @@ -848,6 +839,7 @@ namespace ts { timerToBuildInvalidatedProject = undefined; if (reportFileChangeDetected) { reportFileChangeDetected = false; + projectErrorsReported.clear(); reportWatchStatus(Diagnostics.File_change_detected_Starting_incremental_compilation); } const buildProject = getNextInvalidatedProject(); @@ -866,15 +858,19 @@ namespace ts { function reportErrorSummary() { if (options.watch) { + // Report errors from the other projects + getGlobalDependencyGraph().buildQueue.forEach(project => { + if (!projectErrorsReported.hasKey(project)) { + reportErrors(diagnostics.getValue(project) || emptyArray); + } + }); let totalErrors = 0; - diagnostics.forEach(singleProjectErrors => totalErrors += singleProjectErrors); + diagnostics.forEach(singleProjectErrors => totalErrors += singleProjectErrors.filter(diagnostic => diagnostic.category === DiagnosticCategory.Error).length); reportWatchStatus(totalErrors === 1 ? Diagnostics.Found_1_error_Watching_for_file_changes : Diagnostics.Found_0_errors_Watching_for_file_changes, totalErrors); } } function buildSingleInvalidatedProject(resolved: ResolvedConfigFileName, reloadLevel: ConfigFileProgramReloadLevel) { - // TODO:: handle this in better way later - const proj = parseConfigFile(resolved); if (!proj) { reportParseConfigFileDiagnostic(resolved); @@ -968,10 +964,6 @@ namespace ts { } } - function reportParseConfigFileDiagnostic(proj: ResolvedConfigFileName) { - host.reportDiagnostic(configFileCache.getValue(proj) as Diagnostic); - storeErrorSummary(proj, 1); - } function buildSingleProject(proj: ResolvedConfigFileName): BuildResultFlags { if (options.dry) { @@ -1013,7 +1005,7 @@ namespace ts { ...program.getSyntacticDiagnostics()]; if (syntaxDiagnostics.length) { resultFlags |= BuildResultFlags.SyntaxErrors; - reportErrors(proj, syntaxDiagnostics); + reportAndStoreErrors(proj, syntaxDiagnostics); projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Syntactic errors" }); return resultFlags; } @@ -1023,7 +1015,7 @@ namespace ts { const declDiagnostics = program.getDeclarationDiagnostics(); if (declDiagnostics.length) { resultFlags |= BuildResultFlags.DeclarationEmitErrors; - reportErrors(proj, declDiagnostics); + reportAndStoreErrors(proj, declDiagnostics); projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Declaration file errors" }); return resultFlags; } @@ -1033,7 +1025,7 @@ namespace ts { const semanticDiagnostics = program.getSemanticDiagnostics(); if (semanticDiagnostics.length) { resultFlags |= BuildResultFlags.TypeErrors; - reportErrors(proj, semanticDiagnostics); + reportAndStoreErrors(proj, semanticDiagnostics); projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Semantic errors" }); return resultFlags; } @@ -1154,7 +1146,7 @@ namespace ts { const projName = proj.options.configFilePath!; if (status.type === UpToDateStatusType.UpToDate && !options.force) { - reportErrors(next, errors); + reportAndStoreErrors(next, errors); // Up to date, skip if (defaultOptions.dry) { // In a dry build, inform the user of this fact @@ -1164,20 +1156,20 @@ namespace ts { } if (status.type === UpToDateStatusType.UpToDateWithUpstreamTypes && !options.force) { - reportErrors(next, errors); + reportAndStoreErrors(next, errors); // Fake build updateOutputTimestamps(proj); continue; } if (status.type === UpToDateStatusType.UpstreamBlocked) { - reportErrors(next, errors); + reportAndStoreErrors(next, errors); if (options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, projName, status.upstreamProjectName); continue; } if (status.type === UpToDateStatusType.ContainerOnly) { - reportErrors(next, errors); + reportAndStoreErrors(next, errors); // Do nothing continue; } @@ -1189,9 +1181,20 @@ namespace ts { return anyFailed ? ExitStatus.DiagnosticsPresent_OutputsSkipped : ExitStatus.Success; } - function reportErrors(proj: ResolvedConfigFileName, errors: ReadonlyArray) { + function reportParseConfigFileDiagnostic(proj: ResolvedConfigFileName) { + reportAndStoreErrors(proj, [configFileCache.getValue(proj) as Diagnostic]); + } + + function reportAndStoreErrors(proj: ResolvedConfigFileName, errors: ReadonlyArray) { + reportErrors(errors); + if (options.watch) { + projectErrorsReported.setValue(proj, true); + diagnostics.setValue(proj, errors); + } + } + + function reportErrors(errors: ReadonlyArray) { errors.forEach(err => host.reportDiagnostic(err)); - storeErrors(proj, errors); } /** diff --git a/src/testRunner/unittests/tsbuildWatchMode.ts b/src/testRunner/unittests/tsbuildWatchMode.ts index 697d14f72ba..d9dcda94bff 100644 --- a/src/testRunner/unittests/tsbuildWatchMode.ts +++ b/src/testRunner/unittests/tsbuildWatchMode.ts @@ -351,6 +351,33 @@ function myFunc() { return 100; }`); } }); + it("reports errors in all projects on incremental compile", () => { + const host = createSolutionInWatchMode(allFiles); + const outputFileStamps = getOutputFileStamps(host); + + host.writeFile(logic[1].path, `${logic[1].content} +let y: string = 10;`); + + host.checkTimeoutQueueLengthAndRun(1); // Builds logic + const changedLogic = getOutputFileStamps(host); + verifyChangedFiles(changedLogic, outputFileStamps, emptyArray); + host.checkTimeoutQueueLength(0); + checkOutputErrorsIncremental(host, [ + `sample1/logic/index.ts(8,5): error TS2322: Type '10' is not assignable to type 'string'.\n` + ]); + + host.writeFile(core[1].path, `${core[1].content} +let x: string = 10;`); + + host.checkTimeoutQueueLengthAndRun(1); // Builds core + const changedCore = getOutputFileStamps(host); + verifyChangedFiles(changedCore, changedLogic, emptyArray); + host.checkTimeoutQueueLength(0); + checkOutputErrorsIncremental(host, [ + `sample1/core/index.ts(5,5): error TS2322: Type '10' is not assignable to type 'string'.\n`, + `sample1/logic/index.ts(8,5): error TS2322: Type '10' is not assignable to type 'string'.\n` + ]); + }); // TODO: write tests reporting errors but that will have more involved work since file }); } diff --git a/src/testRunner/unittests/tscWatchMode.ts b/src/testRunner/unittests/tscWatchMode.ts index da1c4fd0d70..b750e4c556c 100644 --- a/src/testRunner/unittests/tscWatchMode.ts +++ b/src/testRunner/unittests/tscWatchMode.ts @@ -77,7 +77,7 @@ namespace ts.tscWatch { logsBeforeWatchDiagnostic: string[] | undefined, preErrorsWatchDiagnostic: Diagnostic, logsBeforeErrors: string[] | undefined, - errors: ReadonlyArray, + errors: ReadonlyArray | ReadonlyArray, disableConsoleClears?: boolean | undefined, ...postErrorsWatchDiagnostics: Diagnostic[] ) { @@ -96,8 +96,12 @@ namespace ts.tscWatch { assert.equal(host.screenClears.length, screenClears, "Expected number of screen clears"); host.clearOutput(); - function assertDiagnostic(diagnostic: Diagnostic) { - const expected = formatDiagnostic(diagnostic, host); + function isDiagnostic(diagnostic: Diagnostic | string): diagnostic is Diagnostic { + return !!(diagnostic as Diagnostic).messageText; + } + + function assertDiagnostic(diagnostic: Diagnostic | string) { + const expected = isDiagnostic(diagnostic) ? formatDiagnostic(diagnostic, host) : diagnostic; assert.equal(outputs[index], expected, getOutputAtFailedMessage("Diagnostic", expected)); index++; } @@ -130,13 +134,13 @@ namespace ts.tscWatch { } } - function createErrorsFoundCompilerDiagnostic(errors: ReadonlyArray) { + function createErrorsFoundCompilerDiagnostic(errors: ReadonlyArray | ReadonlyArray) { return errors.length === 1 ? createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes) : createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length); } - export function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) { + export function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray | ReadonlyArray, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) { checkOutputErrors( host, /*logsBeforeWatchDiagnostic*/ undefined, @@ -147,7 +151,7 @@ namespace ts.tscWatch { createErrorsFoundCompilerDiagnostic(errors)); } - export function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) { + export function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray | ReadonlyArray, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) { checkOutputErrors( host, logsBeforeWatchDiagnostic, @@ -158,7 +162,7 @@ namespace ts.tscWatch { createErrorsFoundCompilerDiagnostic(errors)); } - function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) { + function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray | ReadonlyArray, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) { checkOutputErrors( host, logsBeforeWatchDiagnostic,