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:
commit
b0d85c0270
4 changed files with 191 additions and 5 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
Loading…
Reference in a new issue