Do not write files directly from builder when noEmitOnError is true (#34832)

* Add tests for noEmitOnError

* Do not write files directly from builder when noEmitOnError is true
Fixes #34823

* make linter happy

* Instead of generating output in memory, check errors before doing the emit in case of noEmitOnError
This commit is contained in:
Sheetal Nandi
2019-12-12 19:51:18 -08:00
committed by GitHub
parent ac5e10bc72
commit c3b2aea9f9
16 changed files with 271 additions and 53 deletions

View File

@@ -882,36 +882,36 @@ namespace ts {
oldProgram = undefined;
oldState = undefined;
const result = createRedirectedBuilderProgram(state, configFileParsingDiagnostics);
result.getState = () => state;
result.backupState = () => {
const builderProgram = createRedirectedBuilderProgram(state, configFileParsingDiagnostics);
builderProgram.getState = () => state;
builderProgram.backupState = () => {
Debug.assert(backupState === undefined);
backupState = cloneBuilderProgramState(state);
};
result.restoreState = () => {
builderProgram.restoreState = () => {
state = Debug.assertDefined(backupState);
backupState = undefined;
};
result.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.assertDefined(state.program), sourceFile);
result.getSemanticDiagnostics = getSemanticDiagnostics;
result.emit = emit;
result.releaseProgram = () => {
builderProgram.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.assertDefined(state.program), sourceFile);
builderProgram.getSemanticDiagnostics = getSemanticDiagnostics;
builderProgram.emit = emit;
builderProgram.releaseProgram = () => {
releaseCache(state);
backupState = undefined;
};
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
(result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
(builderProgram as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
}
else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
(result as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
(result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
(builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
(builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
}
else {
notImplemented();
}
return result;
return builderProgram;
/**
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
@@ -987,6 +987,8 @@ namespace ts {
function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
const result = handleNoEmitOptions(builderProgram, targetSourceFile, cancellationToken);
if (result) return result;
if (!targetSourceFile) {
// Emit and report any errors we ran into.
let sourceMaps: SourceMapEmitResult[] = [];

View File

@@ -1570,37 +1570,9 @@ namespace ts {
}
function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
let declarationDiagnostics: readonly Diagnostic[] = [];
if (!forceDtsEmit) {
if (options.noEmit) {
return { diagnostics: declarationDiagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
}
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
// get any preEmit diagnostics, not just the ones
if (options.noEmitOnError) {
const diagnostics = [
...program.getOptionsDiagnostics(cancellationToken),
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
...program.getGlobalDiagnostics(cancellationToken),
...program.getSemanticDiagnostics(sourceFile, cancellationToken)
];
if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) {
declarationDiagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
}
if (diagnostics.length > 0 || declarationDiagnostics.length > 0) {
return {
diagnostics: concatenate(diagnostics, declarationDiagnostics),
sourceMaps: undefined,
emittedFiles: undefined,
emitSkipped: true
};
}
}
const result = handleNoEmitOptions(program, sourceFile, cancellationToken);
if (result) return result;
}
// Create the emit resolver outside of the "emitTime" tracking code below. That way
@@ -3442,6 +3414,33 @@ namespace ts {
}
}
/*@internal*/
export function handleNoEmitOptions(program: ProgramToEmitFilesAndReportErrors, sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): EmitResult | undefined {
const options = program.getCompilerOptions();
if (options.noEmit) {
return { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
}
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
// get any preEmit diagnostics, not just the ones
if (!options.noEmitOnError) return undefined;
let diagnostics: readonly Diagnostic[] = [
...program.getOptionsDiagnostics(cancellationToken),
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
...program.getGlobalDiagnostics(cancellationToken),
...program.getSemanticDiagnostics(sourceFile, cancellationToken)
];
if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) {
diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
}
return diagnostics.length > 0 ?
{ diagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true } :
undefined;
}
/*@internal*/
interface CompilerHostLike {
useCaseSensitiveFileNames(): boolean;

View File

@@ -124,6 +124,7 @@ namespace ts {
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
getConfigFileParsingDiagnostics(): readonly Diagnostic[];
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
}
@@ -152,20 +153,20 @@ namespace ts {
const isListFilesOnly = !!program.getCompilerOptions().listFilesOnly;
// First get and report any syntactic errors.
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
const configFileParsingDiagnosticsLength = diagnostics.length;
addRange(diagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
const allDiagnostics = program.getConfigFileParsingDiagnostics().slice();
const configFileParsingDiagnosticsLength = allDiagnostics.length;
addRange(allDiagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getOptionsDiagnostics(cancellationToken));
if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
addRange(allDiagnostics, program.getOptionsDiagnostics(cancellationToken));
if (!isListFilesOnly) {
addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
addRange(allDiagnostics, program.getGlobalDiagnostics(cancellationToken));
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
addRange(allDiagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
}
}
}
@@ -175,9 +176,10 @@ namespace ts {
? { emitSkipped: true, diagnostics: emptyArray }
: program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
const { emittedFiles, diagnostics: emitDiagnostics } = emitResult;
addRange(diagnostics, emitDiagnostics);
addRange(allDiagnostics, emitDiagnostics);
sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic);
const diagnostics = sortAndDeduplicateDiagnostics(allDiagnostics);
diagnostics.forEach(reportDiagnostic);
if (writeFileName) {
const currentDir = program.getCurrentDirectory();
forEach(emittedFiles, file => {