Skip to content

Structure is reused should be on new program instead of old program #41005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 31 additions & 30 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,9 +824,9 @@ namespace ts {
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
// We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
// `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`.
let structuralIsReused: StructureIsReused | undefined;
structuralIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
if (structuralIsReused !== StructureIsReused.Completely) {
let structureIsReused: StructureIsReused;
structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
if (structureIsReused !== StructureIsReused.Completely) {
processingDefaultLibFiles = [];
processingOtherFiles = [];

Expand Down Expand Up @@ -979,6 +979,7 @@ namespace ts {
getSymlinkCache,
realpath: host.realpath?.bind(host),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
structureIsReused,
};

onProgramCreateComplete();
Expand Down Expand Up @@ -1107,7 +1108,7 @@ namespace ts {
}

function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly ResolvedModuleFull[] {
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined);
Expand Down Expand Up @@ -1278,22 +1279,22 @@ namespace ts {
// if any of these properties has changed - structure cannot be reused
const oldOptions = oldProgram.getCompilerOptions();
if (changesAffectModuleResolution(oldOptions, options)) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

// there is an old program, check if we can reuse its structure
const oldRootNames = oldProgram.getRootFileNames();
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

if (!arrayIsEqualTo(options.types, oldOptions.types)) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

// Check if any referenced project tsconfig files are different
if (!canReuseProjectReferences()) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
if (projectReferences) {
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
Expand All @@ -1302,13 +1303,13 @@ namespace ts {
// check if program source files has changed in the way that can affect structure of the program
const newSourceFiles: SourceFile[] = [];
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
oldProgram.structureIsReused = StructureIsReused.Completely;
structureIsReused = StructureIsReused.Completely;

// If the missing file paths are now present, it can change the progam structure,
// and hence cant reuse the structure.
// This is same as how we dont reuse the structure if one of the file from old program is now missing
if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

const oldSourceFiles = oldProgram.getSourceFiles();
Expand All @@ -1321,7 +1322,7 @@ namespace ts {
: host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217

if (!newSourceFile) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
Expand All @@ -1332,15 +1333,15 @@ namespace ts {
// This lets us know if the unredirected file has changed. If it has we should break the redirect.
if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
// Underlying file has changed. Might not redirect anymore. Must rebuild program.
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
fileChanged = false;
newSourceFile = oldSourceFile; // Use the redirect.
}
else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) {
// If a redirected-to source file changes, the redirect may be broken.
if (newSourceFile !== oldSourceFile) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
fileChanged = false;
}
Expand All @@ -1361,7 +1362,7 @@ namespace ts {
const prevKind = seenPackageNames.get(packageName);
const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
seenPackageNames.set(packageName, newKind);
}
Expand All @@ -1371,47 +1372,47 @@ namespace ts {

if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) {
// 'lib' references has changed. Matches behavior in changesAffectModuleResolution
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
// value of no-default-lib has changed
// this will affect if default library is injected into the list of files
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

// check tripleslash references
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
// tripleslash references has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

// check imports and module augmentations
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
// imports has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
// moduleAugmentations has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}
if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) {
// dynamicImport has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
// 'types' references has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

// tentatively approve the file
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
}
else if (hasInvalidatedResolution(oldSourceFile.path)) {
// 'module/types' references could have changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;

// add file to the modified list so that we will resolve it later
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
Expand All @@ -1421,8 +1422,8 @@ namespace ts {
newSourceFiles.push(newSourceFile);
}

if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
if (structureIsReused !== StructureIsReused.Completely) {
return structureIsReused;
}

const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
Expand All @@ -1440,7 +1441,7 @@ namespace ts {
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
}
else {
Expand All @@ -1452,20 +1453,20 @@ namespace ts {
// ensure that types resolutions are still correct
const typeReferenceEesolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
if (typeReferenceEesolutionsChanged) {
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, typeReferenceResolutions);
}
else {
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
}
}

if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
if (structureIsReused !== StructureIsReused.Completely) {
return structureIsReused;
}

if (host.hasChangedAutomaticTypeDirectiveNames?.()) {
return oldProgram.structureIsReused = StructureIsReused.SafeModules;
return StructureIsReused.SafeModules;
}

missingFilePaths = oldProgram.getMissingFilePaths();
Expand Down Expand Up @@ -1503,7 +1504,7 @@ namespace ts {
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
redirectTargetsMap = oldProgram.redirectTargetsMap;

return oldProgram.structureIsReused = StructureIsReused.Completely;
return StructureIsReused.Completely;
}

function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3779,7 +3779,8 @@ namespace ts {
isSourceFileDefaultLibrary(file: SourceFile): boolean;

// For testing purposes only.
/* @internal */ structureIsReused?: StructureIsReused;
// This is set on created program to let us know how the program was created using old program
/* @internal */ readonly structureIsReused: StructureIsReused;

/* @internal */ getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined;
/* @internal */ getLibFileFromReference(ref: FileReference): SourceFile | undefined;
Expand Down
6 changes: 3 additions & 3 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ namespace ts.server {
// bump up the version if
// - oldProgram is not set - this is a first time updateGraph is called
// - newProgram is different from the old program and structure of the old program was not reused.
const hasNewProgram = this.program && (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused! & StructureIsReused.Completely)));
const hasNewProgram = this.program && (!oldProgram || (this.program !== oldProgram && !(this.program.structureIsReused & StructureIsReused.Completely)));
if (hasNewProgram) {
if (oldProgram) {
for (const f of oldProgram.getSourceFiles()) {
Expand Down Expand Up @@ -1153,7 +1153,7 @@ namespace ts.server {
}

if (!this.importSuggestionsCache.isEmpty()) {
if (this.hasAddedorRemovedFiles || oldProgram && !oldProgram.structureIsReused) {
if (this.hasAddedorRemovedFiles || oldProgram && !this.program.structureIsReused) {
this.importSuggestionsCache.clear();
}
else if (this.dirtyFilesForSuggestions && oldProgram && this.program) {
Expand Down Expand Up @@ -1194,7 +1194,7 @@ namespace ts.server {
this.print(/*writeProjectFileNames*/ true);
}
else if (this.program !== oldProgram) {
this.writeLog(`Different program with same set of files:: oldProgram.structureIsReused:: ${oldProgram && oldProgram.structureIsReused}`);
this.writeLog(`Different program with same set of files:: structureIsReused:: ${this.program.structureIsReused}`);
}
return hasNewProgram;
}
Expand Down
4 changes: 2 additions & 2 deletions src/testRunner/unittests/moduleResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,8 @@ import b = require("./moduleB");
const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics();
assert.equal(diagnostics1.length, 1, "expected one diagnostic");

createProgram(names, {}, compilerHost, program1);
assert.isTrue(program1.structureIsReused === StructureIsReused.Completely);
const program2 = createProgram(names, {}, compilerHost, program1);
assert.isTrue(program2.structureIsReused === StructureIsReused.Completely);
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
Expand Down
Loading