mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-18 07:29:16 -05:00
Get semantic diagnostics for the program from builder so that it caches the errors of unchanged files
This commit is contained in:
@@ -26,6 +26,7 @@ namespace ts {
|
||||
getFilesAffectedBy(program: Program, path: Path): string[];
|
||||
emitFile(program: Program, path: Path): EmitOutput;
|
||||
emitChangedFiles(program: Program): EmitOutputDetailed[];
|
||||
getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[];
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
@@ -74,6 +75,7 @@ namespace ts {
|
||||
// Last checked shape signature for the file info
|
||||
type FileInfo = { version: string; signature: string; };
|
||||
let fileInfos: Map<FileInfo>;
|
||||
const semanticDiagnosticsPerFile = createMap<Diagnostic[]>();
|
||||
let changedFilesSinceLastEmit: Map<true>;
|
||||
let emitHandler: EmitHandler;
|
||||
return {
|
||||
@@ -81,6 +83,7 @@ namespace ts {
|
||||
getFilesAffectedBy,
|
||||
emitFile,
|
||||
emitChangedFiles,
|
||||
getSemanticDiagnostics,
|
||||
clear
|
||||
};
|
||||
|
||||
@@ -90,6 +93,7 @@ namespace ts {
|
||||
isModuleEmit = currentIsModuleEmit;
|
||||
emitHandler = isModuleEmit ? getModuleEmitHandler() : getNonModuleEmitHandler();
|
||||
fileInfos = undefined;
|
||||
semanticDiagnosticsPerFile.clear();
|
||||
}
|
||||
|
||||
changedFilesSinceLastEmit = changedFilesSinceLastEmit || createMap<true>();
|
||||
@@ -104,20 +108,27 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
function registerChangedFile(path: Path) {
|
||||
changedFilesSinceLastEmit.set(path, true);
|
||||
// All changed files need to re-evaluate its semantic diagnostics
|
||||
semanticDiagnosticsPerFile.delete(path);
|
||||
}
|
||||
|
||||
function addNewFileInfo(program: Program, sourceFile: SourceFile): FileInfo {
|
||||
changedFilesSinceLastEmit.set(sourceFile.path, true);
|
||||
registerChangedFile(sourceFile.path);
|
||||
emitHandler.addScriptInfo(program, sourceFile);
|
||||
return { version: sourceFile.version, signature: undefined };
|
||||
}
|
||||
|
||||
function removeExistingFileInfo(path: Path, _existingFileInfo: FileInfo) {
|
||||
changedFilesSinceLastEmit.set(path, true);
|
||||
registerChangedFile(path);
|
||||
emitHandler.removeScriptInfo(path);
|
||||
}
|
||||
|
||||
function updateExistingFileInfo(program: Program, existingInfo: FileInfo, sourceFile: SourceFile, hasInvalidatedResolution: HasInvalidatedResolution) {
|
||||
if (existingInfo.version !== sourceFile.version || hasInvalidatedResolution(sourceFile.path)) {
|
||||
changedFilesSinceLastEmit.set(sourceFile.path, true);
|
||||
registerChangedFile(sourceFile.path);
|
||||
semanticDiagnosticsPerFile.delete(sourceFile.path);
|
||||
existingInfo.version = sourceFile.version;
|
||||
emitHandler.updateScriptInfo(program, sourceFile);
|
||||
}
|
||||
@@ -170,6 +181,9 @@ namespace ts {
|
||||
const sourceFile = program.getSourceFile(file);
|
||||
seenFiles.set(file, sourceFile);
|
||||
if (sourceFile) {
|
||||
// Any affected file shouldnt have the cached diagnostics
|
||||
semanticDiagnosticsPerFile.delete(sourceFile.path);
|
||||
|
||||
const emitOutput = getEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ false, /*isDetailed*/ true) as EmitOutputDetailed;
|
||||
result.push(emitOutput);
|
||||
|
||||
@@ -189,10 +203,45 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[] {
|
||||
ensureProgramGraph(program);
|
||||
|
||||
// Ensure that changed files have cleared their respective
|
||||
if (changedFilesSinceLastEmit) {
|
||||
changedFilesSinceLastEmit.forEach((__value, path: Path) => {
|
||||
const affectedFiles = getFilesAffectedBy(program, path);
|
||||
for (const file of affectedFiles) {
|
||||
const sourceFile = program.getSourceFile(file);
|
||||
if (sourceFile) {
|
||||
semanticDiagnosticsPerFile.delete(sourceFile.path);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let diagnostics: Diagnostic[];
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
const path = sourceFile.path;
|
||||
const cachedDiagnostics = semanticDiagnosticsPerFile.get(path);
|
||||
// Report the semantic diagnostics from the cache if we already have those diagnostics present
|
||||
if (cachedDiagnostics) {
|
||||
diagnostics = concatenate(diagnostics, cachedDiagnostics);
|
||||
}
|
||||
else {
|
||||
// Diagnostics werent cached, get them from program, and cache the result
|
||||
const cachedDiagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
semanticDiagnosticsPerFile.set(path, cachedDiagnostics);
|
||||
diagnostics = concatenate(diagnostics, cachedDiagnostics);
|
||||
}
|
||||
}
|
||||
return diagnostics || emptyArray;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
isModuleEmit = undefined;
|
||||
emitHandler = undefined;
|
||||
fileInfos = undefined;
|
||||
semanticDiagnosticsPerFile.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +405,7 @@ namespace ts {
|
||||
const referencedByPaths = referencedBy.get(path);
|
||||
if (referencedByPaths) {
|
||||
for (const path of referencedByPaths) {
|
||||
changedFilesSinceLastEmit.set(path, true);
|
||||
registerChangedFile(path);
|
||||
}
|
||||
referencedBy.delete(path);
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ namespace ts {
|
||||
if (resolution && !resolution.isInvalidated) {
|
||||
const result = getResult(resolution);
|
||||
if (result) {
|
||||
if (getResultFileName(result) === deletedFilePath) {
|
||||
if (toPath(getResultFileName(result)) === deletedFilePath) {
|
||||
resolution.isInvalidated = true;
|
||||
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(path, true);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace ts {
|
||||
enableStatistics(compilerOptions);
|
||||
|
||||
const program = createProgram(rootFileNames, compilerOptions, compilerHost);
|
||||
const exitStatus = compileProgram(sys, program, () => program.emit(), reportDiagnostic);
|
||||
const exitStatus = compileProgram(program);
|
||||
|
||||
reportStatistics(program);
|
||||
return sys.exit(exitStatus);
|
||||
@@ -174,6 +174,29 @@ namespace ts {
|
||||
return watchingHost;
|
||||
}
|
||||
|
||||
function compileProgram(program: Program): ExitStatus {
|
||||
let diagnostics: Diagnostic[];
|
||||
|
||||
// First get and report any syntactic errors.
|
||||
diagnostics = program.getSyntacticDiagnostics();
|
||||
|
||||
// If we didn't have any syntactic errors, then also try getting the global and
|
||||
// semantic errors.
|
||||
if (diagnostics.length === 0) {
|
||||
diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
diagnostics = program.getSemanticDiagnostics();
|
||||
}
|
||||
}
|
||||
|
||||
// Emit and report any errors we ran into.
|
||||
const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit();
|
||||
diagnostics = diagnostics.concat(emitDiagnostics);
|
||||
|
||||
return handleEmitOutputAndReportErrors(sys, program, emittedFiles, emitSkipped, diagnostics, reportDiagnostic);
|
||||
}
|
||||
|
||||
function enableStatistics(compilerOptions: CompilerOptions) {
|
||||
if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) {
|
||||
performance.enable();
|
||||
|
||||
@@ -101,29 +101,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function compileProgram(system: System, program: Program, emitProgram: () => EmitResult,
|
||||
reportDiagnostic: DiagnosticReporter): ExitStatus {
|
||||
let diagnostics: Diagnostic[];
|
||||
|
||||
// First get and report any syntactic errors.
|
||||
diagnostics = program.getSyntacticDiagnostics();
|
||||
|
||||
// If we didn't have any syntactic errors, then also try getting the global and
|
||||
// semantic errors.
|
||||
if (diagnostics.length === 0) {
|
||||
diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
diagnostics = program.getSemanticDiagnostics();
|
||||
}
|
||||
}
|
||||
|
||||
// Emit and report any errors we ran into.
|
||||
const emitOutput = emitProgram();
|
||||
diagnostics = diagnostics.concat(emitOutput.diagnostics);
|
||||
|
||||
export function handleEmitOutputAndReportErrors(system: System, program: Program,
|
||||
emittedFiles: string[], emitSkipped: boolean,
|
||||
diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter
|
||||
): ExitStatus {
|
||||
reportDiagnostics(sortAndDeduplicateDiagnostics(diagnostics), reportDiagnostic);
|
||||
reportEmittedFiles(emitOutput.emittedFiles, system);
|
||||
reportEmittedFiles(emittedFiles, system);
|
||||
|
||||
if (program.getCompilerOptions().listFiles) {
|
||||
forEach(program.getSourceFiles(), file => {
|
||||
@@ -131,7 +114,7 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
if (emitOutput.emitSkipped && diagnostics.length > 0) {
|
||||
if (emitSkipped && diagnostics.length > 0) {
|
||||
// If the emitter didn't emit anything, then pass that value along.
|
||||
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
|
||||
}
|
||||
@@ -143,74 +126,6 @@ namespace ts {
|
||||
return ExitStatus.Success;
|
||||
}
|
||||
|
||||
function emitWatchedProgram(host: System, program: Program, builder: Builder) {
|
||||
const emittedFiles: string[] = program.getCompilerOptions().listEmittedFiles ? [] : undefined;
|
||||
let sourceMaps: SourceMapData[];
|
||||
let emitSkipped: boolean;
|
||||
let diagnostics: Diagnostic[];
|
||||
|
||||
const result = builder.emitChangedFiles(program);
|
||||
switch (result.length) {
|
||||
case 0:
|
||||
emitSkipped = true;
|
||||
break;
|
||||
case 1:
|
||||
const emitOutput = result[0];
|
||||
({ diagnostics, sourceMaps, emitSkipped } = emitOutput);
|
||||
writeOutputFiles(emitOutput.outputFiles);
|
||||
break;
|
||||
default:
|
||||
for (const emitOutput of result) {
|
||||
if (emitOutput.emitSkipped) {
|
||||
emitSkipped = true;
|
||||
}
|
||||
diagnostics = concatenate(diagnostics, emitOutput.diagnostics);
|
||||
sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps);
|
||||
writeOutputFiles(emitOutput.outputFiles);
|
||||
}
|
||||
}
|
||||
|
||||
return { emitSkipped, diagnostics: diagnostics || [], emittedFiles, sourceMaps };
|
||||
|
||||
|
||||
function ensureDirectoriesExist(directoryPath: string) {
|
||||
if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) {
|
||||
const parentDirectory = getDirectoryPath(directoryPath);
|
||||
ensureDirectoriesExist(parentDirectory);
|
||||
host.createDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
|
||||
try {
|
||||
performance.mark("beforeIOWrite");
|
||||
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
|
||||
|
||||
host.writeFile(fileName, data, writeByteOrderMark);
|
||||
|
||||
performance.mark("afterIOWrite");
|
||||
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
|
||||
}
|
||||
catch (e) {
|
||||
return createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, e);
|
||||
}
|
||||
}
|
||||
|
||||
function writeOutputFiles(outputFiles: OutputFile[]) {
|
||||
if (outputFiles) {
|
||||
for (const outputFile of outputFiles) {
|
||||
const error = writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark);
|
||||
if (error) {
|
||||
diagnostics.push(error);
|
||||
}
|
||||
if (emittedFiles) {
|
||||
emittedFiles.push(outputFile.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createWatchingSystemHost(pretty?: DiagnosticStyle, system = sys,
|
||||
parseConfigFile?: ParseConfigFile, reportDiagnostic?: DiagnosticReporter,
|
||||
reportWatchDiagnostic?: DiagnosticReporter
|
||||
@@ -228,7 +143,82 @@ namespace ts {
|
||||
};
|
||||
|
||||
function compileWatchedProgram(host: System, program: Program, builder: Builder) {
|
||||
return compileProgram(system, program, () => emitWatchedProgram(host, program, builder), reportDiagnostic);
|
||||
// First get and report any syntactic errors.
|
||||
let diagnostics = program.getSyntacticDiagnostics();
|
||||
let reportSemanticDiagnostics = false;
|
||||
|
||||
// If we didn't have any syntactic errors, then also try getting the global and
|
||||
// semantic errors.
|
||||
if (diagnostics.length === 0) {
|
||||
diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
reportSemanticDiagnostics = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Emit and report any errors we ran into.
|
||||
const emittedFiles: string[] = program.getCompilerOptions().listEmittedFiles ? [] : undefined;
|
||||
let sourceMaps: SourceMapData[];
|
||||
let emitSkipped: boolean;
|
||||
|
||||
const result = builder.emitChangedFiles(program);
|
||||
if (result.length === 0) {
|
||||
emitSkipped = true;
|
||||
}
|
||||
else {
|
||||
for (const emitOutput of result) {
|
||||
if (emitOutput.emitSkipped) {
|
||||
emitSkipped = true;
|
||||
}
|
||||
diagnostics = concatenate(diagnostics, emitOutput.diagnostics);
|
||||
sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps);
|
||||
writeOutputFiles(emitOutput.outputFiles);
|
||||
}
|
||||
}
|
||||
|
||||
if (reportSemanticDiagnostics) {
|
||||
diagnostics = diagnostics.concat(builder.getSemanticDiagnostics(program));
|
||||
}
|
||||
return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped,
|
||||
diagnostics, reportDiagnostic);
|
||||
|
||||
function ensureDirectoriesExist(directoryPath: string) {
|
||||
if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) {
|
||||
const parentDirectory = getDirectoryPath(directoryPath);
|
||||
ensureDirectoriesExist(parentDirectory);
|
||||
host.createDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
|
||||
try {
|
||||
performance.mark("beforeIOWrite");
|
||||
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
|
||||
|
||||
host.writeFile(fileName, data, writeByteOrderMark);
|
||||
|
||||
performance.mark("afterIOWrite");
|
||||
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
|
||||
}
|
||||
catch (e) {
|
||||
return createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, e);
|
||||
}
|
||||
}
|
||||
|
||||
function writeOutputFiles(outputFiles: OutputFile[]) {
|
||||
if (outputFiles) {
|
||||
for (const outputFile of outputFiles) {
|
||||
const error = writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark);
|
||||
if (error) {
|
||||
diagnostics.push(error);
|
||||
}
|
||||
if (emittedFiles) {
|
||||
emittedFiles.push(outputFile.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user