Skip to content

Do not check for ambient module to determine if resolution should be watched. #54316

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

Closed
wants to merge 10 commits into from
Closed
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
153 changes: 81 additions & 72 deletions src/compiler/program.ts

Large diffs are not rendered by default.

463 changes: 310 additions & 153 deletions src/compiler/resolutionCache.ts

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7800,6 +7800,7 @@ export interface CompilerHost extends ModuleResolutionHost {
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
ambientModuleNames: readonly StringLiteralLike[] | undefined,
): readonly ResolvedModuleWithFailedLookupLocations[];
resolveTypeReferenceDirectiveReferences?<T extends FileReference | string>(
typeDirectiveReferences: readonly T[],
Expand Down Expand Up @@ -7840,6 +7841,22 @@ export interface CompilerHost extends ModuleResolutionHost {
/** @internal */ getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
}

/** @internal */
export interface CompilerHostSupportingResolutionCache {
reusedModuleResolutions?(
reusedNames: readonly StringLiteralLike[] | undefined,
containingSourceFile: SourceFile,
ambientModuleNames: readonly StringLiteralLike[] | undefined,
): void;
reusedTypeReferenceDirectiveResolutions?<T extends FileReference | string>(
reusedNames: readonly T[] | undefined,
containingSourceFile: SourceFile | undefined,
): void;
}
/** @internal */
export interface CompilerHost extends CompilerHostSupportingResolutionCache {
}

/** true if --out otherwise source file name *
* @internal
*/
Expand Down
16 changes: 13 additions & 3 deletions src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
perfLogger,
PollingInterval,
ProjectReference,
ResolutionCache,
ResolutionCacheHost,
ResolutionMode,
ResolvedModule,
Expand Down Expand Up @@ -221,6 +222,7 @@ export interface ProgramHost<T extends BuilderProgram> {
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
ambientModuleNames: readonly StringLiteralLike[] | undefined,
): readonly ResolvedModuleWithFailedLookupLocations[];
resolveTypeReferenceDirectiveReferences?<T extends FileReference | string>(
typeDirectiveReferences: readonly T[],
Expand Down Expand Up @@ -333,6 +335,8 @@ export interface Watch<T> {
getCurrentProgram(): T;
/** Closes the watch */
close(): void;
/** @internal */
getResolutionCache(): ResolutionCache;
}

/**
Expand Down Expand Up @@ -511,11 +515,13 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
if (!compilerHost.resolveModuleNameLiterals && !compilerHost.resolveModuleNames) {
compilerHost.resolveModuleNameLiterals = resolutionCache.resolveModuleNameLiterals.bind(resolutionCache);
compilerHost.reusedModuleResolutions = resolutionCache.reusedModuleResolutions.bind(resolutionCache);
}
compilerHost.resolveTypeReferenceDirectiveReferences = maybeBind(host, host.resolveTypeReferenceDirectiveReferences);
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
if (!compilerHost.resolveTypeReferenceDirectiveReferences && !compilerHost.resolveTypeReferenceDirectives) {
compilerHost.resolveTypeReferenceDirectiveReferences = resolutionCache.resolveTypeReferenceDirectiveReferences.bind(resolutionCache);
compilerHost.resolveTypeReferenceDirectiveReferences = resolutionCache.resolveTypeReferenceDirectiveReferences.bind(resolutionCache);
compilerHost.reusedTypeReferenceDirectiveResolutions = resolutionCache.reusedTypeReferenceDirectiveResolutions.bind(resolutionCache);
}
compilerHost.resolveLibrary = !host.resolveLibrary ?
resolutionCache.resolveLibrary.bind(resolutionCache) :
Expand Down Expand Up @@ -543,8 +549,8 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
if (configFileName) updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile);

return configFileName ?
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, close } :
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, updateRootFileNames, close };
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, close, getResolutionCache } :
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, updateRootFileNames, close, getResolutionCache };

function close() {
clearInvalidateResolutionsOfFailedLookupLocations();
Expand Down Expand Up @@ -584,6 +590,10 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
}
}

function getResolutionCache() {
return resolutionCache;
}

function getCurrentBuilderProgram() {
return builderProgram;
}
Expand Down
85 changes: 69 additions & 16 deletions src/harness/fourslashImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as vfs from "./_namespaces/vfs";
import * as vpath from "./_namespaces/vpath";

import ArrayOrSingle = FourSlashInterface.ArrayOrSingle;
import { Logger } from "./tsserverLogger";

export const enum FourSlashTestType {
Native,
Expand Down Expand Up @@ -202,6 +203,12 @@ const enum CallHierarchyItemDirection {
Outgoing
}

interface BaselineTest {
command: string;
baselineFile: string;
actual: string;
}

export class TestState {
// Language service instance
private languageServiceAdapterHost: Harness.LanguageService.LanguageServiceAdapterHost;
Expand All @@ -225,6 +232,7 @@ export class TestState {
public formatCodeSettings: ts.FormatCodeSettings;

private inputFiles = new Map<string, string>(); // Map between inputFile's fileName and its content for easily looking up when resolving references
logger: Logger | undefined;

private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[] | undefined) {
let result = "";
Expand Down Expand Up @@ -338,6 +346,7 @@ export class TestState {
}

const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
this.logger = languageServiceAdapter.getLogger();
this.languageServiceAdapterHost = languageServiceAdapter.getHost();
this.languageService = memoWrap(languageServiceAdapter.getLanguageService(), this); // Wrap the LS to cache some expensive operations certain tests call repeatedly
if (this.testType === FourSlashTestType.Server) {
Expand Down Expand Up @@ -426,6 +435,11 @@ export class TestState {

this.formatCodeSettings = ts.testFormatSettings;

if (this.logger?.loggingEnabled()) {
const patch = this.languageServiceAdapterHost.vfs.diff();
this.logger.log(vfs.formatPatch(patch) || "");
}

// Open the first file by default
this.openFile(0);

Expand Down Expand Up @@ -462,6 +476,39 @@ export class TestState {
}
}

private baselineFromTest: BaselineTest | BaselineTest[] | undefined;

private baseline(command: string, baselineFile: string, actual: string) {
if (!this.baselineFromTest) this.baselineFromTest = { command, baselineFile, actual };
else if (ts.isArray(this.baselineFromTest)) this.baselineFromTest.push({ command, baselineFile, actual });
else this.baselineFromTest = [this.baselineFromTest, { command, baselineFile, actual }];
}

baselineTest() {
if (this.baselineFromTest) {
if (ts.isArray(this.baselineFromTest)) {
Harness.Baseline.runBaseline(
this.baselineFromTest[0].baselineFile,
this.baselineFromTest.map(({ command, actual }) =>
`/// ${command}::\n${actual}`
).join("\n\n\n"),
);
}
else {
Harness.Baseline.runBaseline(this.baselineFromTest.baselineFile, this.baselineFromTest.actual);
}
}
}

baselineTsserverLog() {
if (this.logger) {
Harness.Baseline.runBaseline(
`tsserver/fourslashServer/${ts.getBaseFileName(this.originalInputFileName).replace(".ts", ".js")}`,
this.logger.logs!.join("\n")
);
}
}

private getFileContent(fileName: string): string {
return ts.Debug.checkDefined(this.tryGetFileContent(fileName));
}
Expand Down Expand Up @@ -1273,7 +1320,7 @@ export class TestState {
ts.Debug.assertNever(command);
}
});
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent);
this.baseline(commands.length > 1 ? "Commands" : commands[0].type, this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent);

function baselineArrayOrSingle<T>(
command: FourSlashInterface.BaselineCommand,
Expand Down Expand Up @@ -2168,7 +2215,7 @@ export class TestState {

public baselineCurrentFileBreakpointLocations() {
const baselineFile = this.getBaselineFileNameForInternalFourslashFile().replace("breakpointValidation", "bpSpan");
Harness.Baseline.runBaseline(baselineFile, this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
this.baseline("breakpoints", baselineFile, this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
}

private getEmitFiles(): readonly FourSlashFile[] {
Expand Down Expand Up @@ -2232,7 +2279,7 @@ export class TestState {
resultString += Harness.IO.newLine();
}

Harness.Baseline.runBaseline(ts.Debug.checkDefined(this.testData.globalOptions[MetadataOptionNames.baselineFile]), resultString);
this.baseline("EmitOutput", ts.Debug.checkDefined(this.testData.globalOptions[MetadataOptionNames.baselineFile]), resultString);
}

private flattenChainedMessage(diag: ts.DiagnosticMessageChain, indent = " ") {
Expand All @@ -2249,7 +2296,7 @@ export class TestState {
public baselineSyntacticDiagnostics() {
const files = this.getCompilerTestFiles();
const result = this.getSyntacticDiagnosticBaselineText(files);
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(), result);
this.baseline("Syntax Diagnostics", this.getBaselineFileNameForContainingTestFile(), result);
}

private getCompilerTestFiles() {
Expand All @@ -2264,7 +2311,7 @@ export class TestState {
+ Harness.IO.newLine()
+ Harness.IO.newLine()
+ this.getSemanticDiagnosticBaselineText(files);
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(), result);
this.baseline("Syntax and Semantic Diagnostics", this.getBaselineFileNameForContainingTestFile(), result);
}

private getSyntacticDiagnosticBaselineText(files: Harness.Compiler.TestFile[]) {
Expand Down Expand Up @@ -2302,7 +2349,7 @@ export class TestState {
...(documentation?.length ? documentation.map(p => p.text).join("").split("\n") : []),
...(tags?.length ? tags.map(p => `@${p.name} ${p.text?.map(dp => dp.text).join("") ?? ""}`).join("\n").split("\n") : [])
]);
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result));
this.baseline("QuickInfo", baselineFile, annotations + "\n\n" + stringify(result));
}

public baselineSignatureHelp() {
Expand Down Expand Up @@ -2337,7 +2384,7 @@ export class TestState {
return tooltip;
}
);
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result));
this.baseline("SignatureHelp", baselineFile, annotations + "\n\n" + stringify(result));
}

public baselineCompletions(preferences?: ts.UserPreferences) {
Expand Down Expand Up @@ -2381,7 +2428,7 @@ export class TestState {
}
}
}
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result, (key, value) => {
this.baseline("Completions", baselineFile, annotations + "\n\n" + stringify(result, (key, value) => {
return key === "exportMapKey"
? value.replace(/\|[0-9]+/g, "|*")
: value;
Expand Down Expand Up @@ -2462,7 +2509,7 @@ export class TestState {
return baselineContent.join(fileContent.includes("\n") ? n + n : n);
}).join(n.repeat(2) + "=".repeat(80) + n.repeat(2));

Harness.Baseline.runBaseline(baselineFile, text);
this.baseline("Smart Selection", baselineFile, text);
}

public printBreakpointLocation(pos: number) {
Expand Down Expand Up @@ -2931,7 +2978,8 @@ export class TestState {
}

public baselineCurrentFileNameOrDottedNameSpans() {
Harness.Baseline.runBaseline(
this.baseline(
"NameOrDottedNameSpans",
this.testData.globalOptions[MetadataOptionNames.baselineFile],
this.baselineCurrentFileLocations(pos => this.getNameOrDottedNameSpan(pos)!));
}
Expand Down Expand Up @@ -3576,7 +3624,7 @@ export class TestState {
}
}

Harness.Baseline.runBaseline(baselineFile, baselineText);
this.baseline("Auto Imports", baselineFile, baselineText);
}

public verifyJsxClosingTag(map: { [markerName: string]: ts.JsxClosingTagInfo | undefined }): void {
Expand Down Expand Up @@ -3607,7 +3655,7 @@ export class TestState {
offset = result.offset;
}

Harness.Baseline.runBaseline(baselineFile, baselineContent);
this.baseline("Linked Editing", baselineFile, baselineContent);

function getLinkedEditingBaselineWorker(activeFile: FourSlashFile, offset: number, languageService: ts.LanguageService) {
const fileName = activeFile.fileName;
Expand Down Expand Up @@ -4190,7 +4238,7 @@ export class TestState {
const baselineFile = this.getBaselineFileNameForContainingTestFile(".callHierarchy.txt");
const callHierarchyItem = this.languageService.prepareCallHierarchy(this.activeFile.fileName, this.currentCaretPosition);
const text = callHierarchyItem ? ts.mapOneOrMany(callHierarchyItem, item => this.formatCallHierarchy(item), result => result.join("")) : "none";
Harness.Baseline.runBaseline(baselineFile, text);
this.baseline("Call Hierarchy", baselineFile, text);
}

private getLineContent(index: number) {
Expand Down Expand Up @@ -4413,25 +4461,30 @@ function renameKeys<T>(obj: { readonly [key: string]: T }, renameKey: (key: stri
return res;
}

export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) {
export interface FourSlashServerLogBaseliner {
baseline?: () => void;
}
export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string, serverLogBaseliner?: FourSlashServerLogBaseliner) {
const content = Harness.IO.readFile(fileName)!;
runFourSlashTestContent(basePath, testType, content, fileName);
runFourSlashTestContent(basePath, testType, content, fileName, serverLogBaseliner);
}

export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): void {
export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string, serverLogBaseliner?: FourSlashServerLogBaseliner): void {
// Give file paths an absolute path for the virtual file system
const absoluteBasePath = ts.combinePaths(Harness.virtualFileSystemRoot, basePath);
const absoluteFileName = ts.combinePaths(Harness.virtualFileSystemRoot, fileName);

// Parse out the files and their metadata
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
const state = new TestState(absoluteFileName, absoluteBasePath, testType, testData);
if (serverLogBaseliner) serverLogBaseliner.baseline = () => state.baselineTsserverLog();
const actualFileName = Harness.IO.resolvePath(fileName) || absoluteFileName;
const output = ts.transpileModule(content, { reportDiagnostics: true, fileName: actualFileName, compilerOptions: { target: ts.ScriptTarget.ES2015, inlineSourceMap: true, inlineSources: true } });
if (output.diagnostics!.length > 0) {
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`);
}
runCode(output.outputText, state, actualFileName);
state.baselineTest();
}

function runCode(code: string, state: TestState, fileName: string): void {
Expand Down
2 changes: 1 addition & 1 deletion src/harness/harnessIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export namespace Compiler {
export const es2015DefaultLibFileName = "lib.es2015.d.ts";

// Cache of lib files from "built/local"
let libFileNameSourceFileMap: Map<string, ts.SourceFile> | undefined;
export let libFileNameSourceFileMap: Map<string, ts.SourceFile> | undefined;

export function getDefaultLibrarySourceFile(fileName = defaultLibFileName): ts.SourceFile | undefined {
if (!isDefaultLibraryFile(fileName)) {
Expand Down
Loading