Skip to content
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

Lazy calculation of expensive file explaining diagnsotics and some caching to be used to share the diagnostic data #58398

Merged
merged 9 commits into from
May 6, 2024
251 changes: 197 additions & 54 deletions src/compiler/program.ts

Large diffs are not rendered by default.

16 changes: 7 additions & 9 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4608,26 +4608,24 @@ export type FileIncludeReason =

/** @internal */
export const enum FilePreprocessingDiagnosticsKind {
FilePreprocessingReferencedDiagnostic,
FilePreprocessingLibReferenceDiagnostic,
FilePreprocessingFileExplainingDiagnostic,
ResolutionDiagnostics,
}

/** @internal */
export interface FilePreprocessingReferencedDiagnostic {
kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic;
reason: ReferencedFile;
diagnostic: DiagnosticMessage;
args?: DiagnosticArguments;
export interface FilePreprocessingLibReferenceDiagnostic {
kind: FilePreprocessingDiagnosticsKind.FilePreprocessingLibReferenceDiagnostic;
reason: ReferencedFile & { kind: FileIncludeKind.LibReferenceDirective; };
}

/** @internal */
export interface FilePreprocessingFileExplainingDiagnostic {
kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic;
file?: Path;
file: Path | undefined;
fileProcessingReason: FileIncludeReason;
diagnostic: DiagnosticMessage;
args?: DiagnosticArguments;
args: DiagnosticArguments;
}

/** @internal */
Expand All @@ -4637,7 +4635,7 @@ export interface ResolutionDiagnostics {
}

/** @internal */
export type FilePreprocessingDiagnostics = FilePreprocessingReferencedDiagnostic | FilePreprocessingFileExplainingDiagnostic | ResolutionDiagnostics;
export type FilePreprocessingDiagnostics = FilePreprocessingLibReferenceDiagnostic | FilePreprocessingFileExplainingDiagnostic | ResolutionDiagnostics;

/** @internal */
export const enum EmitOnly {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
});
parsedConfigs = undefined;
}
builderProgram = undefined!;
}

function getResolutionCache() {
Expand Down
49 changes: 31 additions & 18 deletions src/testRunner/unittests/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface NamedSourceText {
export interface ProgramWithSourceTexts extends ts.Program {
sourceTexts?: readonly NamedSourceText[];
host: TestCompilerHost;
version: number;
}

export interface TestCompilerHost extends ts.CompilerHost {
Expand Down Expand Up @@ -102,7 +103,7 @@ function createSourceFileWithText(fileName: string, sourceText: SourceText, targ
return file;
}

export function createTestCompilerHost(texts: readonly NamedSourceText[], target: ts.ScriptTarget, oldProgram?: ProgramWithSourceTexts, useGetSourceFileByPath?: boolean) {
export function createTestCompilerHost(texts: readonly NamedSourceText[], target: ts.ScriptTarget, oldProgram?: ProgramWithSourceTexts, useGetSourceFileByPath?: boolean, useCaseSensitiveFileNames?: boolean) {
const files = ts.arrayToMap(texts, t => t.name, t => {
if (oldProgram) {
let oldFile = oldProgram.getSourceFile(t.name) as SourceFileWithText;
Expand All @@ -115,52 +116,64 @@ export function createTestCompilerHost(texts: readonly NamedSourceText[], target
}
return createSourceFileWithText(t.name, t.text, target);
});
const useCaseSensitiveFileNames = ts.sys && ts.sys.useCaseSensitiveFileNames;
if (useCaseSensitiveFileNames === undefined) useCaseSensitiveFileNames = ts.sys && ts.sys.useCaseSensitiveFileNames;
const getCanonicalFileName = ts.createGetCanonicalFileName(useCaseSensitiveFileNames);
const filesByPath = ts.mapEntries(files, (fileName, file) => [ts.toPath(fileName, "", getCanonicalFileName), file]);
const trace: string[] = [];
const result: TestCompilerHost = {
trace: s => trace.push(s),
getTrace: () => trace,
clearTrace: () => trace.length = 0,
getSourceFile: fileName => files.get(fileName),
getSourceFile: fileName => filesByPath.get(ts.toPath(fileName, "", getCanonicalFileName)),
getDefaultLibFileName: () => "lib.d.ts",
writeFile: ts.notImplemented,
getCurrentDirectory: () => "",
getDirectories: () => [],
getCanonicalFileName,
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
getNewLine: () => ts.sys ? ts.sys.newLine : newLine,
fileExists: fileName => files.has(fileName),
fileExists: fileName => filesByPath.has(ts.toPath(fileName, "", getCanonicalFileName)),
readFile: fileName => {
const file = files.get(fileName);
const file = filesByPath.get(ts.toPath(fileName, "", getCanonicalFileName));
return file && file.text;
},
};
if (useGetSourceFileByPath) {
const filesByPath = ts.mapEntries(files, (fileName, file) => [ts.toPath(fileName, "", getCanonicalFileName), file]);
result.getSourceFileByPath = (_fileName, path) => filesByPath.get(path);
}
return result;
}

export function newProgram(texts: NamedSourceText[], rootNames: string[], options: ts.CompilerOptions, useGetSourceFileByPath?: boolean): ProgramWithSourceTexts {
const host = createTestCompilerHost(texts, options.target!, /*oldProgram*/ undefined, useGetSourceFileByPath);
const program = ts.createProgram(rootNames, options, host) as ProgramWithSourceTexts;
program.sourceTexts = texts;
program.host = host;
return program;
export function newProgram(texts: NamedSourceText[], rootNames: string[], options: ts.CompilerOptions, useGetSourceFileByPath?: boolean, useCaseSensitiveFileNames?: boolean): ProgramWithSourceTexts {
const host = createTestCompilerHost(texts, options.target!, /*oldProgram*/ undefined, useGetSourceFileByPath, useCaseSensitiveFileNames);
return programToProgramWithSourceTexts(
ts.createProgram(rootNames, options, host),
texts,
host,
1,
);
}

export function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: readonly string[], options: ts.CompilerOptions, updater: (files: NamedSourceText[]) => void, newTexts?: NamedSourceText[], useGetSourceFileByPath?: boolean) {
function programToProgramWithSourceTexts(program: ts.Program, texts: NamedSourceText[], host: TestCompilerHost, version: number): ProgramWithSourceTexts {
const result = program as ProgramWithSourceTexts;
result.sourceTexts = texts;
result.host = host;
result.version = version;
return result;
}

export function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: readonly string[], options: ts.CompilerOptions, updater: (files: NamedSourceText[]) => void, newTexts?: NamedSourceText[], useGetSourceFileByPath?: boolean, useCaseSensitiveFileNames?: boolean) {
if (!newTexts) {
newTexts = oldProgram.sourceTexts!.slice(0);
}
updater(newTexts);
const host = createTestCompilerHost(newTexts, options.target!, oldProgram, useGetSourceFileByPath);
const program = ts.createProgram(rootNames, options, host, oldProgram) as ProgramWithSourceTexts;
program.sourceTexts = newTexts;
program.host = host;
return program;
const host = createTestCompilerHost(newTexts, options.target!, oldProgram, useGetSourceFileByPath, useCaseSensitiveFileNames);
return programToProgramWithSourceTexts(
ts.createProgram(rootNames, options, host, oldProgram),
newTexts,
host,
oldProgram.version + 1,
);
}

export function updateProgramText(files: readonly NamedSourceText[], fileName: string, newProgramText: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { dedent } from "../../_namespaces/Utils";
import { jsonToReadableText } from "../helpers";
import {
FsContents,
libContent,
} from "./contents";
import { libFile } from "./virtualFileSystemWithWatch";

export function getFsContentsForMultipleErrorsForceConsistentCasingInFileNames(): FsContents {
return {
"/home/src/projects/project/src/struct.d.ts": dedent`
import * as xs1 from "fp-ts/lib/Struct";
import * as xs2 from "fp-ts/lib/struct";
import * as xs3 from "./Struct";
import * as xs4 from "./struct";
`,
"/home/src/projects/project/src/anotherFile.ts": dedent`
import * as xs1 from "fp-ts/lib/Struct";
import * as xs2 from "fp-ts/lib/struct";
import * as xs3 from "./Struct";
import * as xs4 from "./struct";
`,
"/home/src/projects/project/src/oneMore.ts": dedent`
import * as xs1 from "fp-ts/lib/Struct";
import * as xs2 from "fp-ts/lib/struct";
import * as xs3 from "./Struct";
import * as xs4 from "./struct";
`,
"/home/src/projects/project/tsconfig.json": jsonToReadableText({}),
"/home/src/projects/project/node_modules/fp-ts/lib/struct.d.ts": `export function foo(): void`,
[libFile.path]: libContent,
};
}
43 changes: 43 additions & 0 deletions src/testRunner/unittests/helpers/libraryResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,46 @@ export function getCommandLineArgsForLibResolution(withoutConfig: true | undefin
["project1/core.d.ts", "project1/utils.d.ts", "project1/file.ts", "project1/index.ts", "project1/file2.ts", "--lib", "es5,dom", "--traceResolution", "--explainFiles"] :
["-p", "project1", "--explainFiles"];
}

function getFsContentsForLibResolutionUnknown(): FsContents {
return {
"/home/src/projects/project1/utils.d.ts": `export const y = 10;`,
"/home/src/projects/project1/file.ts": `export const file = 10;`,
"/home/src/projects/project1/core.d.ts": `export const core = 10;`,
"/home/src/projects/project1/index.ts": `export const x = "type1";`,
"/home/src/projects/project1/file2.ts": dedent`
/// <reference lib="webworker2"/>
/// <reference lib="unknownlib"/>
/// <reference lib="scripthost"/>
`,
"/home/src/projects/project1/tsconfig.json": jsonToReadableText({
compilerOptions: {
composite: true,
traceResolution: true,
},
}),
"/home/src/lib/lib.d.ts": libContent,
"/home/src/lib/lib.webworker.d.ts": "interface WebWorkerInterface { }",
"/home/src/lib/lib.scripthost.d.ts": "interface ScriptHostInterface { }",
};
}

export function getFsForLibResolutionUnknown() {
return loadProjectFromFiles(
getFsContentsForLibResolutionUnknown(),
{
cwd: "/home/src/projects",
executingFilePath: "/home/src/lib/tsc.js",
},
);
}

export function getSysForLibResolutionUnknown() {
return createWatchedSystem(
getFsContentsForLibResolutionUnknown(),
{
currentDirectory: "/home/src/projects",
executingFilePath: "/home/src/lib/tsc.js",
},
);
}