Merge pull request #28450 from Microsoft/libErrors

Remove lib file errors from builder cache when global files are to be emitted
This commit is contained in:
Sheetal Nandi 2018-11-09 17:42:08 -08:00 committed by GitHub
commit b0d85c0270
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 191 additions and 5 deletions

View file

@ -38,6 +38,10 @@ namespace ts {
* Already seen affected files
*/
seenAffectedFiles: Map<true> | undefined;
/**
* whether this program has cleaned semantic diagnostics cache for lib files
*/
cleanedDiagnosticsOfLibFiles?: boolean;
/**
* True if the semantic diagnostics were copied from the old state
*/
@ -64,9 +68,11 @@ namespace ts {
state.semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>();
}
state.changedFilesSet = createMap<true>();
const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState);
const oldCompilerOptions = useOldState ? oldState!.program.getCompilerOptions() : undefined;
const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile &&
!compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldState!.program.getCompilerOptions());
!compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldCompilerOptions!);
if (useOldState) {
// Verify the sanity of old state
if (!oldState!.currentChangedFilePath) {
@ -83,6 +89,8 @@ namespace ts {
// Update changed files and copy semantic diagnostics if we can
const referencedMap = state.referencedMap;
const oldReferencedMap = useOldState ? oldState!.referencedMap : undefined;
const copyDeclarationFileDiagnostics = canCopySemanticDiagnostics && !compilerOptions.skipLibCheck === !oldCompilerOptions!.skipLibCheck;
const copyLibFileDiagnostics = copyDeclarationFileDiagnostics && !compilerOptions.skipDefaultLibCheck === !oldCompilerOptions!.skipDefaultLibCheck;
state.fileInfos.forEach((info, sourceFilePath) => {
let oldInfo: Readonly<BuilderState.FileInfo> | undefined;
let newReferences: BuilderState.ReferencedSet | undefined;
@ -101,6 +109,11 @@ namespace ts {
state.changedFilesSet.set(sourceFilePath, true);
}
else if (canCopySemanticDiagnostics) {
const sourceFile = state.program.getSourceFileByPath(sourceFilePath as Path)!;
if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics) { return; }
if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics) { return; }
// Unchanged file copy diagnostics
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
if (diagnostics) {
@ -193,6 +206,19 @@ namespace ts {
return;
}
// Clean lib file diagnostics if its all files excluding default files to emit
if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles && !state.cleanedDiagnosticsOfLibFiles) {
state.cleanedDiagnosticsOfLibFiles = true;
const options = state.program.getCompilerOptions();
if (forEach(state.program.getSourceFiles(), f =>
state.program.isSourceFileDefaultLibrary(f) &&
!skipTypeChecking(f, options) &&
removeSemanticDiagnosticsOf(state, f.path)
)) {
return;
}
}
// If there was change in signature for the changed file,
// then delete the semantic diagnostics for files that are affected by using exports of this module
@ -268,7 +294,7 @@ namespace ts {
*/
function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) {
if (!state.semanticDiagnosticsFromOldState) {
return false;
return true;
}
state.semanticDiagnosticsFromOldState.delete(path);
state.semanticDiagnosticsPerFile!.delete(path);

View file

@ -368,7 +368,7 @@ namespace ts.BuilderState {
}
// If this is non module emit, or its a global file, it depends on all the source files
if (!state.referencedMap || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) {
if (!state.referencedMap || isFileAffectingGlobalScope(sourceFile)) {
return getAllFileNames(state, programOfThisState);
}
@ -430,6 +430,22 @@ namespace ts.BuilderState {
return true;
}
/**
* Return true if file contains anything that augments to global scope we need to build them as if
* they are global files as well as module
*/
function containsGlobalScopeAugmentation(sourceFile: SourceFile) {
return some(sourceFile.moduleAugmentations, augmentation => isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration));
}
/**
* Return true if the file will invalidate all files because it affectes global scope
*/
function isFileAffectingGlobalScope(sourceFile: SourceFile) {
return containsGlobalScopeAugmentation(sourceFile) ||
!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile);
}
/**
* Gets all files of the program excluding the default library file
*/
@ -473,7 +489,7 @@ namespace ts.BuilderState {
* When program emits modular code, gets the files affected by the sourceFile whose shape has changed
*/
function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) {
if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) {
if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) {
return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
}

View file

@ -25110,6 +25110,11 @@ namespace ts {
checkParameterInitializer(node);
}
}
if (symbol.declarations.length > 1) {
if (some(symbol.declarations, d => d !== node && isVariableLike(d) && !areDeclarationFlagsIdentical(d, node))) {
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name));
}
}
}
else {
// Node is a secondary declaration, check that type is identical to primary declaration and check that
@ -25125,7 +25130,6 @@ namespace ts {
checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(node.initializer), declarationType, node, node.initializer, /*headMessage*/ undefined);
}
if (!areDeclarationFlagsIdentical(node, symbol.valueDeclaration)) {
error(getNameOfDeclaration(symbol.valueDeclaration), Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name));
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name));
}
}

View file

@ -1564,6 +1564,146 @@ export class Data2 {
verifyTransitiveExports([libFile, app, lib2Public, lib2Data, lib2Data2, lib1Public, lib1ToolsPublic, lib1ToolsInterface]);
});
});
describe("updates errors in lib file", () => {
const currentDirectory = "/user/username/projects/myproject";
const field = "fullscreen";
const fieldWithoutReadonly = `interface Document {
${field}: boolean;
}`;
const libFileWithDocument: File = {
path: libFile.path,
content: `${libFile.content}
interface Document {
readonly ${field}: boolean;
}`
};
function getDiagnostic(program: Program, file: File) {
return getDiagnosticOfFileFromProgram(program, file.path, file.content.indexOf(field), field.length, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, field);
}
function verifyLibFileErrorsWith(aFile: File) {
const files = [aFile, libFileWithDocument];
function verifyLibErrors(options: CompilerOptions) {
const host = createWatchedSystem(files, { currentDirectory });
const watch = createWatchOfFilesAndCompilerOptions([aFile.path], host, options);
checkProgramActualFiles(watch(), [aFile.path, libFile.path]);
checkOutputErrorsInitial(host, getErrors());
host.writeFile(aFile.path, aFile.content.replace(fieldWithoutReadonly, "var x: string;"));
host.runQueuedTimeoutCallbacks();
checkProgramActualFiles(watch(), [aFile.path, libFile.path]);
checkOutputErrorsIncremental(host, emptyArray);
host.writeFile(aFile.path, aFile.content);
host.runQueuedTimeoutCallbacks();
checkProgramActualFiles(watch(), [aFile.path, libFile.path]);
checkOutputErrorsIncremental(host, getErrors());
function getErrors() {
return [
...(options.skipLibCheck || options.skipDefaultLibCheck ? [] : [getDiagnostic(watch(), libFileWithDocument)]),
getDiagnostic(watch(), aFile)
];
}
}
it("with default options", () => {
verifyLibErrors({});
});
it("with skipLibCheck", () => {
verifyLibErrors({ skipLibCheck: true });
});
it("with skipDefaultLibCheck", () => {
verifyLibErrors({ skipDefaultLibCheck: true });
});
}
describe("when non module file changes", () => {
const aFile: File = {
path: `${currentDirectory}/a.ts`,
content: `${fieldWithoutReadonly}
var y: number;`
};
verifyLibFileErrorsWith(aFile);
});
describe("when module file with global definitions changes", () => {
const aFile: File = {
path: `${currentDirectory}/a.ts`,
content: `export {}
declare global {
${fieldWithoutReadonly}
var y: number;
}`
};
verifyLibFileErrorsWith(aFile);
});
});
it("when skipLibCheck and skipDefaultLibCheck changes", () => {
const currentDirectory = "/user/username/projects/myproject";
const field = "fullscreen";
const aFile: File = {
path: `${currentDirectory}/a.ts`,
content: `interface Document {
${field}: boolean;
}`
};
const bFile: File = {
path: `${currentDirectory}/b.d.ts`,
content: `interface Document {
${field}: boolean;
}`
};
const libFileWithDocument: File = {
path: libFile.path,
content: `${libFile.content}
interface Document {
readonly ${field}: boolean;
}`
};
const configFile: File = {
path: `${currentDirectory}/tsconfig.json`,
content: "{}"
};
const files = [aFile, bFile, configFile, libFileWithDocument];
const host = createWatchedSystem(files, { currentDirectory });
const watch = createWatchOfConfigFile("tsconfig.json", host);
verifyProgramFiles();
checkOutputErrorsInitial(host, [
getDiagnostic(libFileWithDocument),
getDiagnostic(aFile),
getDiagnostic(bFile)
]);
verifyConfigChange({ skipLibCheck: true }, [aFile]);
verifyConfigChange({ skipDefaultLibCheck: true }, [aFile, bFile]);
verifyConfigChange({}, [libFileWithDocument, aFile, bFile]);
verifyConfigChange({ skipDefaultLibCheck: true }, [aFile, bFile]);
verifyConfigChange({ skipLibCheck: true }, [aFile]);
verifyConfigChange({}, [libFileWithDocument, aFile, bFile]);
function verifyConfigChange(compilerOptions: CompilerOptions, errorInFiles: ReadonlyArray<File>) {
host.writeFile(configFile.path, JSON.stringify({ compilerOptions }));
host.runQueuedTimeoutCallbacks();
verifyProgramFiles();
checkOutputErrorsIncremental(host, errorInFiles.map(getDiagnostic));
}
function getDiagnostic(file: File) {
return getDiagnosticOfFileFromProgram(watch(), file.path, file.content.indexOf(field), field.length, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, field);
}
function verifyProgramFiles() {
checkProgramActualFiles(watch(), [aFile.path, bFile.path, libFile.path]);
}
});
});
describe("tsc-watch emit with outFile or out setting", () => {