Skip to content

Commit eebd932

Browse files
committed
Add incremental test where cache should have same resolutions as whats in the program
This shows cache is holding onto resolutions that are no longer needed by program because either those modules arent present in file or is determined to be ambient resolution
1 parent b771223 commit eebd932

File tree

4 files changed

+127
-27
lines changed

4 files changed

+127
-27
lines changed

src/compiler/program.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,8 @@ export function getResolutionModeOverride(node: ImportAttributes | undefined, gr
966966
return elem.value.text === "import" ? ModuleKind.ESNext : ModuleKind.CommonJS;
967967
}
968968

969-
const emptyResolution: ResolvedModuleWithFailedLookupLocations & ResolvedTypeReferenceDirectiveWithFailedLookupLocations = {
969+
/** @internal */
970+
export const emptyResolution: ResolvedModuleWithFailedLookupLocations & ResolvedTypeReferenceDirectiveWithFailedLookupLocations = {
970971
resolvedModule: undefined,
971972
resolvedTypeReferenceDirective: undefined,
972973
};
@@ -1135,6 +1136,13 @@ function forEachProjectReference<T>(
11351136
/** @internal */
11361137
export const inferredTypesContainingFile = "__inferred type names__.ts";
11371138

1139+
/** @internal */
1140+
export function getAutomaticTypeDirectiveContainingFile(options: CompilerOptions, currentDirectory: string) {
1141+
// This containingFilename needs to match with the one used in managed-side
1142+
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
1143+
return combinePaths(containingDirectory, inferredTypesContainingFile);
1144+
}
1145+
11381146
/** @internal */
11391147
export function getInferredLibraryNameResolveFrom(options: CompilerOptions, currentDirectory: string, libFileName: string) {
11401148
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
@@ -1804,10 +1812,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18041812
automaticTypeDirectiveResolutions = createModeAwareCache();
18051813
if (automaticTypeDirectiveNames.length) {
18061814
tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: automaticTypeDirectiveNames.length });
1807-
// This containingFilename needs to match with the one used in managed-side
1808-
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : currentDirectory;
1809-
const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
1810-
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(automaticTypeDirectiveNames, containingFilename);
1815+
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(
1816+
automaticTypeDirectiveNames,
1817+
getAutomaticTypeDirectiveContainingFile(options, currentDirectory),
1818+
);
18111819
for (let i = 0; i < automaticTypeDirectiveNames.length; i++) {
18121820
// under node16/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode
18131821
automaticTypeDirectiveResolutions.set(automaticTypeDirectiveNames[i], /*mode*/ undefined, resolutions[i]);

src/harness/incrementalUtils.ts

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ export function verifyResolutionCache(
204204
actualProgram: ts.Program,
205205
resolutionHostCacheHost: ts.ResolutionCacheHost,
206206
projectName: string,
207+
userResolvedModuleNames?: true,
207208
) {
208209
const currentDirectory = resolutionHostCacheHost.getCurrentDirectory!();
209210
const expected = ts.createResolutionCache(resolutionHostCacheHost, actual.rootDirForResolution);
@@ -214,6 +215,12 @@ export function verifyResolutionCache(
214215
const expectedToResolution = new Map<ExpectedResolution, ts.ResolutionWithFailedLookupLocations>();
215216
const resolutionToExpected = new Map<ts.ResolutionWithFailedLookupLocations, ExpectedResolution>();
216217
const resolutionToRefs = new Map<ts.ResolutionWithFailedLookupLocations, ResolutionInfo[]>();
218+
const inferredTypesPath = resolutionHostCacheHost.toPath(
219+
ts.getAutomaticTypeDirectiveContainingFile(
220+
actualProgram.getCompilerOptions(),
221+
currentDirectory,
222+
),
223+
);
217224
actual.resolvedModuleNames.forEach((resolutions, path) =>
218225
collectResolutionToRefFromCache(
219226
"Modules",
@@ -222,6 +229,7 @@ export function verifyResolutionCache(
222229
getResolvedModuleFileName,
223230
/*deferWatchingNonRelativeResolution*/ true,
224231
expected.resolvedModuleNames,
232+
(name, mode) => actualProgram.getResolvedModule(actualProgram.getSourceFileByPath(path)!, name, mode),
225233
)
226234
);
227235
actual.resolvedTypeReferenceDirectives.forEach((resolutions, path) =>
@@ -232,6 +240,10 @@ export function verifyResolutionCache(
232240
getResolvedTypeRefFileName,
233241
/*deferWatchingNonRelativeResolution*/ false,
234242
expected.resolvedTypeReferenceDirectives,
243+
(name, mode) =>
244+
path !== inferredTypesPath ?
245+
actualProgram.getResolvedTypeReferenceDirective(actualProgram.getSourceFileByPath(path)!, name, mode) :
246+
actualProgram.getAutomaticTypeDirectiveResolutions().get(name, mode),
235247
)
236248
);
237249
actual.resolvedLibraries.forEach((resolved, libFileName) => {
@@ -248,6 +260,39 @@ export function verifyResolutionCache(
248260
);
249261
expected.resolvedLibraries.set(libFileName, expectedResolution);
250262
});
263+
// Check for resolutions in program but not in cache to empty resolutions
264+
if (!userResolvedModuleNames) {
265+
actualProgram.forEachResolvedModule((resolution, name, mode, filePath) =>
266+
verifyResolutionIsInCache(
267+
"Modules",
268+
actual.resolvedModuleNames.get(filePath),
269+
resolution,
270+
name,
271+
mode,
272+
filePath,
273+
)
274+
);
275+
}
276+
actualProgram.forEachResolvedTypeReferenceDirective((resolution, name, mode, filePath) =>
277+
verifyResolutionIsInCache(
278+
"TypeRefs",
279+
actual.resolvedTypeReferenceDirectives.get(filePath),
280+
resolution,
281+
name,
282+
mode,
283+
filePath,
284+
)
285+
);
286+
actualProgram.getAutomaticTypeDirectiveResolutions().forEach((resolution, name, mode) =>
287+
verifyResolutionIsInCache(
288+
"AutoTypeRefs",
289+
actual.resolvedTypeReferenceDirectives.get(inferredTypesPath),
290+
resolution,
291+
name,
292+
mode,
293+
inferredTypesPath,
294+
)
295+
);
251296

252297
expected.finishCachingPerDirectoryResolution(actualProgram, /*oldProgram*/ undefined);
253298

@@ -260,6 +305,10 @@ export function verifyResolutionCache(
260305
`Expected from:: ${JSON.stringify(info, undefined, " ")}` +
261306
`Actual from: ${resolution.files?.size}`,
262307
);
308+
ts.Debug.assert(
309+
!resolution.isInvalidated,
310+
`${projectName}:: Resolution should not be invalidated`,
311+
);
263312
verifySet(resolutionToExpected.get(resolution)!.files, resolution.files, `${projectName}:: Resolution files`);
264313
});
265314
verifyMapOfResolutionSet(expected.resolvedFileToResolution, actual.resolvedFileToResolution, `resolvedFileToResolution`);
@@ -295,24 +344,54 @@ export function verifyResolutionCache(
295344
ts.Debug.assert(expected.countResolutionsResolvedWithGlobalCache() === 0, `${projectName}:: ResolutionsResolvedWithGlobalCache should be cleared`);
296345
ts.Debug.assert(expected.countResolutionsResolvedWithoutGlobalCache() === 0, `${projectName}:: ResolutionsResolvedWithoutGlobalCache should be cleared`);
297346

347+
function verifyResolutionIsInCache<T extends ts.ResolutionWithFailedLookupLocations>(
348+
cacheType: string,
349+
cache: ts.ModeAwareCache<T> | undefined,
350+
resolution: T,
351+
name: string,
352+
mode: ts.ResolutionMode,
353+
fileName: string,
354+
) {
355+
if (resolution as unknown !== ts.emptyResolution) {
356+
// Resolutions should match
357+
ts.Debug.assert(
358+
cache?.get(name, mode) === resolution,
359+
`${projectName}:: ${cacheType}:: ${name}:: ${mode} Expected resolution in program to be in cache ${fileName}`,
360+
);
361+
}
362+
else {
363+
// EmptyResolution is place holder and shouldnt be in the cache
364+
ts.Debug.assert(
365+
!cache?.has(name, mode),
366+
`${projectName}:: ${cacheType}:: ${name}:: ${mode} Ambient moduleResolution, should not be watched ${fileName}`,
367+
);
368+
}
369+
}
370+
298371
function collectResolutionToRefFromCache<T extends ts.ResolutionWithFailedLookupLocations>(
299372
cacheType: string,
300373
fileName: ts.Path,
301374
cache: ts.ModeAwareCache<T> | undefined,
302375
getResolvedFileName: (resolution: T) => string | undefined,
303376
deferWatchingNonRelativeResolution: boolean,
304-
storeExpcted: Map<ts.Path, ts.ModeAwareCache<ts.ResolutionWithFailedLookupLocations>>,
377+
storeExpected: Map<ts.Path, ts.ModeAwareCache<ts.ResolutionWithFailedLookupLocations>>,
378+
getProgramResolutions: (name: string, mode: ts.ResolutionMode) => T | undefined,
305379
) {
306380
ts.Debug.assert(
307-
actualProgram.getSourceFileByPath(fileName) || ts.endsWith(fileName, ts.inferredTypesContainingFile),
381+
actualProgram.getSourceFileByPath(fileName) || inferredTypesPath === fileName,
308382
`${projectName}:: ${cacheType} ${fileName} Expect cache for file in program or auto type ref`,
309383
);
310384
let expectedCache: ts.ModeAwareCache<ts.ResolutionWithFailedLookupLocations> | undefined;
311385
cache?.forEach((resolved, name, mode) => {
312386
const resolvedFileName = getResolvedFileName(resolved);
313387
const expected = collectResolution(cacheType, fileName, resolved, resolvedFileName, name, mode, deferWatchingNonRelativeResolution);
314-
if (!expectedCache) storeExpcted.set(fileName, expectedCache = ts.createModeAwareCache());
388+
if (!expectedCache) storeExpected.set(fileName, expectedCache = ts.createModeAwareCache());
315389
expectedCache.set(name, mode, expected);
390+
// Resolution in cache should be same as that is in program
391+
ts.Debug.assert(
392+
resolved === getProgramResolutions(name, mode),
393+
`${projectName}:: ${cacheType} ${fileName} ${name} ${mode} Expected resolution in cache to be matched to that in the program`,
394+
);
316395
});
317396
}
318397

src/testRunner/unittests/helpers/tscWatch.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export interface RunWatchBaseline<T extends ts.BuilderProgram> extends BaselineB
187187
getPrograms: () => readonly CommandLineProgram[];
188188
watchOrSolution: WatchOrSolution<T>;
189189
useSourceOfProjectReferenceRedirect?: () => boolean;
190+
userResolvedModuleNames?: true;
190191
}
191192
export function runWatchBaseline<T extends ts.BuilderProgram = ts.EmitAndSemanticDiagnosticsBuilderProgram>({
192193
scenario,
@@ -200,6 +201,7 @@ export function runWatchBaseline<T extends ts.BuilderProgram = ts.EmitAndSemanti
200201
edits,
201202
watchOrSolution,
202203
useSourceOfProjectReferenceRedirect,
204+
userResolvedModuleNames,
203205
}: RunWatchBaseline<T>) {
204206
baseline.push(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}`);
205207
let programs = watchBaseline({
@@ -225,6 +227,7 @@ export function runWatchBaseline<T extends ts.BuilderProgram = ts.EmitAndSemanti
225227
caption,
226228
resolutionCache: (watchOrSolution as ts.WatchOfConfigFile<T> | undefined)?.getResolutionCache?.(),
227229
useSourceOfProjectReferenceRedirect,
230+
userResolvedModuleNames,
228231
});
229232
}
230233
}
@@ -245,6 +248,7 @@ export interface WatchBaseline extends BaselineBase, TscWatchCheckOptions {
245248
caption?: string;
246249
resolutionCache?: ts.ResolutionCache;
247250
useSourceOfProjectReferenceRedirect?: () => boolean;
251+
userResolvedModuleNames?: true;
248252
}
249253
export function watchBaseline({
250254
baseline,
@@ -256,6 +260,7 @@ export function watchBaseline({
256260
caption,
257261
resolutionCache,
258262
useSourceOfProjectReferenceRedirect,
263+
userResolvedModuleNames,
259264
}: WatchBaseline) {
260265
if (baselineSourceMap) generateSourceMapBaselineFiles(sys);
261266
const programs = getPrograms();
@@ -274,6 +279,7 @@ export function watchBaseline({
274279
programs[0][0],
275280
resolutionCache,
276281
useSourceOfProjectReferenceRedirect,
282+
userResolvedModuleNames,
277283
);
278284
}
279285
return programs;
@@ -283,7 +289,8 @@ function verifyProgramStructureAndResolutionCache(
283289
sys: TscWatchSystem,
284290
program: ts.Program,
285291
resolutionCache: ts.ResolutionCache,
286-
useSourceOfProjectReferenceRedirect?: () => boolean,
292+
useSourceOfProjectReferenceRedirect: (() => boolean) | undefined,
293+
userResolvedModuleNames: true | undefined,
287294
) {
288295
const options = program.getCompilerOptions();
289296
const compilerHost = ts.createCompilerHostWorker(options, /*setParentNodes*/ undefined, sys);
@@ -300,25 +307,30 @@ function verifyProgramStructureAndResolutionCache(
300307
program,
301308
caption,
302309
);
303-
verifyResolutionCache(resolutionCache, program, {
304-
...compilerHost,
305-
306-
getCompilerHost: () => compilerHost,
307-
toPath: fileName => sys.toPath(fileName),
308-
getCompilationSettings: () => options,
309-
fileIsOpen: ts.returnFalse,
310-
getCurrentProgram: () => program,
311-
preferNonRecursiveWatch: sys.preferNonRecursiveWatch,
310+
verifyResolutionCache(
311+
resolutionCache,
312+
program,
313+
{
314+
...compilerHost,
315+
getCompilerHost: () => compilerHost,
316+
toPath: fileName => sys.toPath(fileName),
317+
getCompilationSettings: () => options,
318+
fileIsOpen: ts.returnFalse,
319+
getCurrentProgram: () => program,
320+
preferNonRecursiveWatch: sys.preferNonRecursiveWatch,
312321

313-
watchDirectoryOfFailedLookupLocation: ts.returnNoopFileWatcher,
314-
watchAffectingFileLocation: ts.returnNoopFileWatcher,
315-
onInvalidatedResolution: ts.noop,
316-
watchTypeRootsDirectory: ts.returnNoopFileWatcher,
317-
onChangedAutomaticTypeDirectiveNames: ts.noop,
318-
scheduleInvalidateResolutionsOfFailedLookupLocations: ts.noop,
319-
getCachedDirectoryStructureHost: ts.returnUndefined,
320-
writeLog: ts.noop,
321-
}, caption);
322+
watchDirectoryOfFailedLookupLocation: ts.returnNoopFileWatcher,
323+
watchAffectingFileLocation: ts.returnNoopFileWatcher,
324+
onInvalidatedResolution: ts.noop,
325+
watchTypeRootsDirectory: ts.returnNoopFileWatcher,
326+
onChangedAutomaticTypeDirectiveNames: ts.noop,
327+
scheduleInvalidateResolutionsOfFailedLookupLocations: ts.noop,
328+
getCachedDirectoryStructureHost: ts.returnUndefined,
329+
writeLog: ts.noop,
330+
},
331+
caption,
332+
userResolvedModuleNames,
333+
);
322334
}
323335
export interface VerifyTscWatch extends TscWatchCompile {
324336
baselineIncremental?: boolean;

src/testRunner/unittests/tscWatch/watchApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolu
121121
},
122122
],
123123
watchOrSolution: watch,
124+
userResolvedModuleNames: true,
124125
});
125126
});
126127
}

0 commit comments

Comments
 (0)