Skip to content

Commit b8f33f6

Browse files
committed
Report all project errors on incremental compile
1 parent 5696384 commit b8f33f6

File tree

3 files changed

+72
-38
lines changed

3 files changed

+72
-38
lines changed

src/compiler/tsbuild.ts

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -394,9 +394,9 @@ namespace ts {
394394
let globalDependencyGraph: DependencyGraph | undefined;
395395

396396
// Watch state
397-
// TODO(shkamat): this should be really be diagnostics but thats for later time
398-
const diagnostics = createFileMap<number>(toPath);
397+
const diagnostics = createFileMap<ReadonlyArray<Diagnostic>>(toPath);
399398
const projectPendingBuild = createFileMap<ConfigFileProgramReloadLevel>(toPath);
399+
const projectErrorsReported = createFileMap<true>(toPath);
400400
const invalidatedProjectQueue = [] as ResolvedConfigFileName[];
401401
let nextProjectToBuild = 0;
402402
let timerToBuildInvalidatedProject: any;
@@ -438,6 +438,7 @@ namespace ts {
438438

439439
diagnostics.clear();
440440
projectPendingBuild.clear();
441+
projectErrorsReported.clear();
441442
invalidatedProjectQueue.length = 0;
442443
nextProjectToBuild = 0;
443444
if (timerToBuildInvalidatedProject) {
@@ -472,18 +473,6 @@ namespace ts {
472473
host.reportSolutionBuilderStatus(createCompilerDiagnostic(message, ...args));
473474
}
474475

475-
function storeErrors(proj: ResolvedConfigFileName, diagnostics: ReadonlyArray<Diagnostic>) {
476-
if (options.watch) {
477-
storeErrorSummary(proj, diagnostics.filter(diagnostic => diagnostic.category === DiagnosticCategory.Error).length);
478-
}
479-
}
480-
481-
function storeErrorSummary(proj: ResolvedConfigFileName, errorCount: number) {
482-
if (options.watch) {
483-
diagnostics.setValue(proj, errorCount);
484-
}
485-
}
486-
487476
function reportWatchStatus(message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
488477
if (hostWithWatch.onWatchStatusChange) {
489478
hostWithWatch.onWatchStatusChange(createCompilerDiagnostic(message, ...args), host.getNewLine(), { preserveWatchOutput: options.preserveWatchOutput });
@@ -509,14 +498,15 @@ namespace ts {
509498
}
510499

511500
function watchConfigFile(resolved: ResolvedConfigFileName) {
512-
if (!allWatchedConfigFiles.hasKey(resolved)) {
501+
if (options.watch && !allWatchedConfigFiles.hasKey(resolved)) {
513502
allWatchedConfigFiles.setValue(resolved, hostWithWatch.watchFile(resolved, () => {
514503
invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.Full);
515504
}));
516505
}
517506
}
518507

519508
function watchWildCardDirectories(resolved: ResolvedConfigFileName, parsed: ParsedCommandLine) {
509+
if (!options.watch) return;
520510
updateWatchingWildcardDirectories(
521511
getOrCreateValueMapFromConfigFileMap(allWatchedWildcardDirectories, resolved),
522512
createMapFromTemplate(parsed.configFileSpecs!.wildcardDirectories),
@@ -540,6 +530,7 @@ namespace ts {
540530
}
541531

542532
function watchInputFiles(resolved: ResolvedConfigFileName, parsed: ParsedCommandLine) {
533+
if (!options.watch) return;
543534
mutateMap(
544535
getOrCreateValueMapFromConfigFileMap(allWatchedInputFiles, resolved),
545536
arrayToMap(parsed.fileNames, toPath),
@@ -848,6 +839,7 @@ namespace ts {
848839
timerToBuildInvalidatedProject = undefined;
849840
if (reportFileChangeDetected) {
850841
reportFileChangeDetected = false;
842+
projectErrorsReported.clear();
851843
reportWatchStatus(Diagnostics.File_change_detected_Starting_incremental_compilation);
852844
}
853845
const buildProject = getNextInvalidatedProject();
@@ -866,15 +858,19 @@ namespace ts {
866858

867859
function reportErrorSummary() {
868860
if (options.watch) {
861+
// Report errors from the other projects
862+
getGlobalDependencyGraph().buildQueue.forEach(project => {
863+
if (!projectErrorsReported.hasKey(project)) {
864+
reportErrors(diagnostics.getValue(project) || emptyArray);
865+
}
866+
});
869867
let totalErrors = 0;
870-
diagnostics.forEach(singleProjectErrors => totalErrors += singleProjectErrors);
868+
diagnostics.forEach(singleProjectErrors => totalErrors += singleProjectErrors.filter(diagnostic => diagnostic.category === DiagnosticCategory.Error).length);
871869
reportWatchStatus(totalErrors === 1 ? Diagnostics.Found_1_error_Watching_for_file_changes : Diagnostics.Found_0_errors_Watching_for_file_changes, totalErrors);
872870
}
873871
}
874872

875873
function buildSingleInvalidatedProject(resolved: ResolvedConfigFileName, reloadLevel: ConfigFileProgramReloadLevel) {
876-
// TODO:: handle this in better way later
877-
878874
const proj = parseConfigFile(resolved);
879875
if (!proj) {
880876
reportParseConfigFileDiagnostic(resolved);
@@ -968,10 +964,6 @@ namespace ts {
968964
}
969965
}
970966

971-
function reportParseConfigFileDiagnostic(proj: ResolvedConfigFileName) {
972-
host.reportDiagnostic(configFileCache.getValue(proj) as Diagnostic);
973-
storeErrorSummary(proj, 1);
974-
}
975967

976968
function buildSingleProject(proj: ResolvedConfigFileName): BuildResultFlags {
977969
if (options.dry) {
@@ -1013,7 +1005,7 @@ namespace ts {
10131005
...program.getSyntacticDiagnostics()];
10141006
if (syntaxDiagnostics.length) {
10151007
resultFlags |= BuildResultFlags.SyntaxErrors;
1016-
reportErrors(proj, syntaxDiagnostics);
1008+
reportAndStoreErrors(proj, syntaxDiagnostics);
10171009
projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Syntactic errors" });
10181010
return resultFlags;
10191011
}
@@ -1023,7 +1015,7 @@ namespace ts {
10231015
const declDiagnostics = program.getDeclarationDiagnostics();
10241016
if (declDiagnostics.length) {
10251017
resultFlags |= BuildResultFlags.DeclarationEmitErrors;
1026-
reportErrors(proj, declDiagnostics);
1018+
reportAndStoreErrors(proj, declDiagnostics);
10271019
projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Declaration file errors" });
10281020
return resultFlags;
10291021
}
@@ -1033,7 +1025,7 @@ namespace ts {
10331025
const semanticDiagnostics = program.getSemanticDiagnostics();
10341026
if (semanticDiagnostics.length) {
10351027
resultFlags |= BuildResultFlags.TypeErrors;
1036-
reportErrors(proj, semanticDiagnostics);
1028+
reportAndStoreErrors(proj, semanticDiagnostics);
10371029
projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Semantic errors" });
10381030
return resultFlags;
10391031
}
@@ -1154,7 +1146,7 @@ namespace ts {
11541146

11551147
const projName = proj.options.configFilePath!;
11561148
if (status.type === UpToDateStatusType.UpToDate && !options.force) {
1157-
reportErrors(next, errors);
1149+
reportAndStoreErrors(next, errors);
11581150
// Up to date, skip
11591151
if (defaultOptions.dry) {
11601152
// In a dry build, inform the user of this fact
@@ -1164,20 +1156,20 @@ namespace ts {
11641156
}
11651157

11661158
if (status.type === UpToDateStatusType.UpToDateWithUpstreamTypes && !options.force) {
1167-
reportErrors(next, errors);
1159+
reportAndStoreErrors(next, errors);
11681160
// Fake build
11691161
updateOutputTimestamps(proj);
11701162
continue;
11711163
}
11721164

11731165
if (status.type === UpToDateStatusType.UpstreamBlocked) {
1174-
reportErrors(next, errors);
1166+
reportAndStoreErrors(next, errors);
11751167
if (options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, projName, status.upstreamProjectName);
11761168
continue;
11771169
}
11781170

11791171
if (status.type === UpToDateStatusType.ContainerOnly) {
1180-
reportErrors(next, errors);
1172+
reportAndStoreErrors(next, errors);
11811173
// Do nothing
11821174
continue;
11831175
}
@@ -1189,9 +1181,20 @@ namespace ts {
11891181
return anyFailed ? ExitStatus.DiagnosticsPresent_OutputsSkipped : ExitStatus.Success;
11901182
}
11911183

1192-
function reportErrors(proj: ResolvedConfigFileName, errors: ReadonlyArray<Diagnostic>) {
1184+
function reportParseConfigFileDiagnostic(proj: ResolvedConfigFileName) {
1185+
reportAndStoreErrors(proj, [configFileCache.getValue(proj) as Diagnostic]);
1186+
}
1187+
1188+
function reportAndStoreErrors(proj: ResolvedConfigFileName, errors: ReadonlyArray<Diagnostic>) {
1189+
reportErrors(errors);
1190+
if (options.watch) {
1191+
projectErrorsReported.setValue(proj, true);
1192+
diagnostics.setValue(proj, errors);
1193+
}
1194+
}
1195+
1196+
function reportErrors(errors: ReadonlyArray<Diagnostic>) {
11931197
errors.forEach(err => host.reportDiagnostic(err));
1194-
storeErrors(proj, errors);
11951198
}
11961199

11971200
/**

src/testRunner/unittests/tsbuildWatchMode.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,33 @@ function myFunc() { return 100; }`);
351351
}
352352
});
353353

354+
it("reports errors in all projects on incremental compile", () => {
355+
const host = createSolutionInWatchMode(allFiles);
356+
const outputFileStamps = getOutputFileStamps(host);
357+
358+
host.writeFile(logic[1].path, `${logic[1].content}
359+
let y: string = 10;`);
360+
361+
host.checkTimeoutQueueLengthAndRun(1); // Builds logic
362+
const changedLogic = getOutputFileStamps(host);
363+
verifyChangedFiles(changedLogic, outputFileStamps, emptyArray);
364+
host.checkTimeoutQueueLength(0);
365+
checkOutputErrorsIncremental(host, [
366+
`sample1/logic/index.ts(8,5): error TS2322: Type '10' is not assignable to type 'string'.\n`
367+
]);
368+
369+
host.writeFile(core[1].path, `${core[1].content}
370+
let x: string = 10;`);
371+
372+
host.checkTimeoutQueueLengthAndRun(1); // Builds core
373+
const changedCore = getOutputFileStamps(host);
374+
verifyChangedFiles(changedCore, changedLogic, emptyArray);
375+
host.checkTimeoutQueueLength(0);
376+
checkOutputErrorsIncremental(host, [
377+
`sample1/core/index.ts(5,5): error TS2322: Type '10' is not assignable to type 'string'.\n`,
378+
`sample1/logic/index.ts(8,5): error TS2322: Type '10' is not assignable to type 'string'.\n`
379+
]);
380+
});
354381
// TODO: write tests reporting errors but that will have more involved work since file
355382
});
356383
}

src/testRunner/unittests/tscWatchMode.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ namespace ts.tscWatch {
7777
logsBeforeWatchDiagnostic: string[] | undefined,
7878
preErrorsWatchDiagnostic: Diagnostic,
7979
logsBeforeErrors: string[] | undefined,
80-
errors: ReadonlyArray<Diagnostic>,
80+
errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>,
8181
disableConsoleClears?: boolean | undefined,
8282
...postErrorsWatchDiagnostics: Diagnostic[]
8383
) {
@@ -96,8 +96,12 @@ namespace ts.tscWatch {
9696
assert.equal(host.screenClears.length, screenClears, "Expected number of screen clears");
9797
host.clearOutput();
9898

99-
function assertDiagnostic(diagnostic: Diagnostic) {
100-
const expected = formatDiagnostic(diagnostic, host);
99+
function isDiagnostic(diagnostic: Diagnostic | string): diagnostic is Diagnostic {
100+
return !!(diagnostic as Diagnostic).messageText;
101+
}
102+
103+
function assertDiagnostic(diagnostic: Diagnostic | string) {
104+
const expected = isDiagnostic(diagnostic) ? formatDiagnostic(diagnostic, host) : diagnostic;
101105
assert.equal(outputs[index], expected, getOutputAtFailedMessage("Diagnostic", expected));
102106
index++;
103107
}
@@ -130,13 +134,13 @@ namespace ts.tscWatch {
130134
}
131135
}
132136

133-
function createErrorsFoundCompilerDiagnostic(errors: ReadonlyArray<Diagnostic>) {
137+
function createErrorsFoundCompilerDiagnostic(errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>) {
134138
return errors.length === 1
135139
? createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes)
136140
: createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length);
137141
}
138142

139-
export function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
143+
export function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
140144
checkOutputErrors(
141145
host,
142146
/*logsBeforeWatchDiagnostic*/ undefined,
@@ -147,7 +151,7 @@ namespace ts.tscWatch {
147151
createErrorsFoundCompilerDiagnostic(errors));
148152
}
149153

150-
export function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
154+
export function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
151155
checkOutputErrors(
152156
host,
153157
logsBeforeWatchDiagnostic,
@@ -158,7 +162,7 @@ namespace ts.tscWatch {
158162
createErrorsFoundCompilerDiagnostic(errors));
159163
}
160164

161-
function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
165+
function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
162166
checkOutputErrors(
163167
host,
164168
logsBeforeWatchDiagnostic,

0 commit comments

Comments
 (0)