Skip to content
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
267 changes: 215 additions & 52 deletions src/compiler/builder.ts

Large diffs are not rendered by default.

73 changes: 24 additions & 49 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1165,11 +1165,6 @@ function getLibFileNameFromLibReference(libReference: FileReference) {
return libMap.get(libName);
}

interface DiagnosticCache<T extends Diagnostic> {
perFile?: Map<Path, readonly T[]>;
allDiagnostics?: readonly T[];
}

/** @internal */
export function isReferencedFile(reason: FileIncludeReason | undefined): reason is ReferencedFile {
switch (reason?.kind) {
Expand Down Expand Up @@ -1555,8 +1550,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
let filesWithReferencesProcessed: Set<Path> | undefined;
let fileReasonsToChain: Map<Path, FileReasonToChainCache> | undefined;
let reasonToRelatedInfo: Map<FileIncludeReason, DiagnosticWithLocation | false> | undefined;
const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
let cachedBindAndCheckDiagnosticsForFile: Map<Path, readonly Diagnostic[]> | undefined;
let cachedDeclarationDiagnosticsForFile: Map<Path, readonly DiagnosticWithLocation[]> | undefined;

let fileProcessingDiagnostics: FilePreprocessingDiagnostics[] | undefined;
let automaticTypeDirectiveNames: string[] | undefined;
Expand Down Expand Up @@ -2921,10 +2916,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
);
}

function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined {
return sourceFile
? cachedBindAndCheckDiagnosticsForFile.perFile?.get(sourceFile.path)
: cachedBindAndCheckDiagnosticsForFile.allDiagnostics;
function getCachedSemanticDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] | undefined {
return cachedBindAndCheckDiagnosticsForFile?.get(sourceFile.path);
}

function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
Expand All @@ -2945,14 +2938,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}

function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
const options = program.getCompilerOptions();
// collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
if (!sourceFile || options.outFile) {
return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
}
else {
return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken);
}
return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken);
}

function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): readonly DiagnosticWithLocation[] {
Expand Down Expand Up @@ -3001,7 +2987,14 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
if (nodesToCheck) {
return getBindAndCheckDiagnosticsForFileNoCache(sourceFile, cancellationToken, nodesToCheck);
}
return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache);
let result = cachedBindAndCheckDiagnosticsForFile?.get(sourceFile.path);
if (!result) {
(cachedBindAndCheckDiagnosticsForFile ??= new Map()).set(
sourceFile.path,
result = getBindAndCheckDiagnosticsForFileNoCache(sourceFile, cancellationToken),
);
}
return result;
}

function getBindAndCheckDiagnosticsForFileNoCache(
Expand Down Expand Up @@ -3361,43 +3354,27 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
});
}

function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache);
function getDeclarationDiagnosticsWorker(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
let result = cachedDeclarationDiagnosticsForFile?.get(sourceFile.path);
if (!result) {
(cachedDeclarationDiagnosticsForFile ??= new Map()).set(
sourceFile.path,
result = getDeclarationDiagnosticsForFileNoCache(sourceFile, cancellationToken),
);
}
return result;
}

function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
return runWithCancellationToken(() => {
const resolver = getTypeChecker().getEmitResolver(sourceFile, cancellationToken);
// Don't actually write any files since we're just getting diagnostics.
return ts_getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray;
});
}

function getAndCacheDiagnostics<T extends SourceFile | undefined, U extends Diagnostic>(
sourceFile: T,
cancellationToken: CancellationToken | undefined,
cache: DiagnosticCache<U>,
getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined) => readonly U[],
): readonly U[] {
const cachedResult = sourceFile
? cache.perFile?.get(sourceFile.path)
: cache.allDiagnostics;

if (cachedResult) {
return cachedResult;
}
const result = getDiagnostics(sourceFile, cancellationToken);
if (sourceFile) {
(cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result);
}
else {
cache.allDiagnostics = result;
}
return result;
}

function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
return sourceFile.isDeclarationFile ? emptyArray : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
}

function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> {
Expand Down Expand Up @@ -5360,8 +5337,6 @@ export function handleNoEmitOptions<T extends BuilderProgram>(
): EmitResult | undefined {
const options = program.getCompilerOptions();
if (options.noEmit) {
// Cache the semantic diagnostics
program.getSemanticDiagnostics(sourceFile, cancellationToken);
return sourceFile ?
emitSkippedWithNoDiagnostics :
program.emitBuildInfo(writeFile, cancellationToken);
Expand Down
27 changes: 16 additions & 11 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,19 +214,24 @@ import {
} from "../_namespaces/ts.js";

/** @internal */
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, file: SourceFile | undefined): DiagnosticWithLocation[] | undefined {
export function getDeclarationDiagnostics(
host: EmitHost,
resolver: EmitResolver,
file: SourceFile,
): DiagnosticWithLocation[] | undefined {
const compilerOptions = host.getCompilerOptions();
const files = filter(getSourceFilesToEmit(host, file), isSourceFileNotJson);
const result = transformNodes(
resolver,
host,
factory,
compilerOptions,
file ? contains(files, file) ? [file] : emptyArray : files,
[transformDeclarations],
/*allowDtsFiles*/ false,
);
return result.diagnostics;
return contains(files, file) ?
transformNodes(
resolver,
host,
factory,
compilerOptions,
[file],
[transformDeclarations],
/*allowDtsFiles*/ false,
).diagnostics :
undefined;
}

const declarationEmitNodeBuilderFlags = NodeBuilderFlags.MultilineObjectLiterals |
Expand Down
32 changes: 26 additions & 6 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import {
getNonIncrementalBuildInfoRoots,
getNormalizedAbsolutePath,
getParsedCommandLineOfConfigFile,
getPendingEmitKind,
getPendingEmitKindWithSeen,
getSourceFileVersionAsHashFromText,
getTsBuildInfoEmitOutputFilePath,
getWatchErrorSummaryDiagnosticMessage,
Expand Down Expand Up @@ -1511,8 +1511,14 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
// If there are errors, we need to build project again to report it
if (
!project.options.noCheck &&
(incrementalBuildInfo.semanticDiagnosticsPerFile?.length ||
(!project.options.noEmit && getEmitDeclarations(project.options) && incrementalBuildInfo.emitDiagnosticsPerFile?.length))
(
incrementalBuildInfo.changeFileSet?.length ||
incrementalBuildInfo.semanticDiagnosticsPerFile?.length ||
(
getEmitDeclarations(project.options) &&
incrementalBuildInfo.emitDiagnosticsPerFile?.length
)
)
) {
return {
type: UpToDateStatusType.OutOfDateBuildInfoWithErrors,
Expand All @@ -1523,8 +1529,11 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
// If there are pending changes that are not emitted, project is out of date
if (
!project.options.noEmit &&
((incrementalBuildInfo as IncrementalMultiFileEmitBuildInfo).affectedFilesPendingEmit?.length ||
(incrementalBuildInfo as IncrementalBundleEmitBuildInfo).pendingEmit !== undefined)
(
incrementalBuildInfo.changeFileSet?.length ||
(incrementalBuildInfo as IncrementalMultiFileEmitBuildInfo).affectedFilesPendingEmit?.length ||
(incrementalBuildInfo as IncrementalBundleEmitBuildInfo).pendingEmit !== undefined
)
) {
return {
type: UpToDateStatusType.OutOfDateBuildInfoWithPendingEmit,
Expand All @@ -1533,7 +1542,18 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
}

// Has not emitted some of the files, project is out of date
if (!project.options.noEmit && getPendingEmitKind(project.options, incrementalBuildInfo.options || {})) {
if (
(
!project.options.noEmit ||
(project.options.noEmit && getEmitDeclarations(project.options))
) &&
getPendingEmitKindWithSeen(
project.options,
incrementalBuildInfo.options || {},
/*emitOnlyDtsFiles*/ undefined,
!!project.options.noEmit,
)
) {
return {
type: UpToDateStatusType.OutOfDateOptions,
buildInfoFile: buildInfoPath,
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4733,7 +4733,7 @@ export interface Program extends ScriptReferenceHost {

/** @internal */ getCommonSourceDirectory(): string;

/** @internal */ getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined;
/** @internal */ getCachedSemanticDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] | undefined;

/** @internal */ getClassifiableNames(): Set<__String>;

Expand Down
15 changes: 12 additions & 3 deletions src/compiler/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
generateDjb2Hash,
getDefaultLibFileName,
getDirectoryPath,
getEmitDeclarations,
getEmitScriptTarget,
getLineAndCharacterOfPosition,
getNameOfScriptTarget,
Expand Down Expand Up @@ -570,7 +571,7 @@ export function emitFilesAndReportErrors<T extends BuilderProgram>(
emitResult: EmitResult;
diagnostics: SortedReadonlyArray<Diagnostic>;
} {
const isListFilesOnly = !!program.getCompilerOptions().listFilesOnly;
const options = program.getCompilerOptions();

// First get and report any syntactic errors.
const allDiagnostics = program.getConfigFileParsingDiagnostics().slice();
Expand All @@ -582,17 +583,25 @@ export function emitFilesAndReportErrors<T extends BuilderProgram>(
if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
addRange(allDiagnostics, program.getOptionsDiagnostics(cancellationToken));

if (!isListFilesOnly) {
if (!options.listFilesOnly) {
addRange(allDiagnostics, program.getGlobalDiagnostics(cancellationToken));

if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
addRange(allDiagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
}

if (
options.noEmit &&
getEmitDeclarations(options) &&
allDiagnostics.length === configFileParsingDiagnosticsLength
) {
addRange(allDiagnostics, program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken));
}
}
}

// Emit and report any errors we ran into.
const emitResult = isListFilesOnly
const emitResult = options.listFilesOnly
? { emitSkipped: true, diagnostics: emptyArray }
: program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
addRange(allDiagnostics, emitResult.diagnostics);
Expand Down
1 change: 1 addition & 0 deletions src/testRunner/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export * from "./unittests/tscWatch/incremental.js";
export * from "./unittests/tscWatch/libraryResolution.js";
export * from "./unittests/tscWatch/moduleResolution.js";
export * from "./unittests/tscWatch/nodeNextWatch.js";
export * from "./unittests/tscWatch/noEmit.js";
export * from "./unittests/tscWatch/noEmitOnError.js";
export * from "./unittests/tscWatch/programUpdates.js";
export * from "./unittests/tscWatch/projectsWithReferences.js";
Expand Down
24 changes: 19 additions & 5 deletions src/testRunner/unittests/helpers/baseline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ export function generateSourceMapBaselineFiles(sys: ts.System & { writtenFiles:
}

export type ReadableIncrementalBuildInfoDiagnosticOfFile = [file: string, diagnostics: readonly ts.ReusableDiagnostic[]];
export type ReadableIncrementalBuildInfoDiagnostic = [file: string, "not cached"] | ReadableIncrementalBuildInfoDiagnosticOfFile;
export type ReadableIncrementalBuildInfoDiagnostic = [file: string, "not cached or not changed"] | ReadableIncrementalBuildInfoDiagnosticOfFile;
export type ReadableIncrementalBuildInfoEmitDiagnostic = ReadableIncrementalBuildInfoDiagnosticOfFile;
export type ReadableBuilderFileEmit = string & { __readableBuilderFileEmit: any; };
export type ReadableIncrementalBuilderInfoFilePendingEmit = [original: string | [string], emitKind: ReadableBuilderFileEmit];
export type ReadableIncrementalBuilderInfoFilePendingEmit = [original: string | [file: string] | [file: string, emitKind: ts.BuilderFileEmit], emitKind: ReadableBuilderFileEmit];
export type ReadableIncrementalBuildInfoEmitSignature = string | [file: string, signature: ts.EmitSignature | []];
export type ReadableIncrementalBuildInfoFileInfo<T> = Omit<ts.BuilderState.FileInfo, "impliedFormat"> & {
impliedFormat: string | undefined;
Expand All @@ -161,12 +161,14 @@ export type ReadableIncrementalBuildInfoBase =
| "resolvedRoot"
| "semanticDiagnosticsPerFile"
| "emitDiagnosticsPerFile"
| "changeFileSet"
>
& {
root: readonly ReadableIncrementalBuildInfoRoot[];
resolvedRoot: readonly ReadableIncrementalBuildInfoResolvedRoot[] | undefined;
semanticDiagnosticsPerFile: readonly ReadableIncrementalBuildInfoDiagnostic[] | undefined;
emitDiagnosticsPerFile: readonly ReadableIncrementalBuildInfoEmitDiagnostic[] | undefined;
changeFileSet: readonly string[] | undefined;
}
& ReadableBuildInfo;
export type ReadableIncrementalMultiFileEmitBuildInfo =
Expand All @@ -179,6 +181,7 @@ export type ReadableIncrementalMultiFileEmitBuildInfo =
| "referencedMap"
| "semanticDiagnosticsPerFile"
| "emitDiagnosticsPerFile"
| "changeFileSet"
| "affectedFilesPendingEmit"
| "emitSignatures"
>
Expand All @@ -199,6 +202,7 @@ export type ReadableIncrementalBundleEmitBuildInfo =
| "resolvedRoot"
| "semanticDiagnosticsPerFile"
| "emitDiagnosticsPerFile"
| "changeFileSet"
| "pendingEmit"
>
& ReadableIncrementalBuildInfoBase
Expand Down Expand Up @@ -247,6 +251,7 @@ function generateBuildInfoBaseline(sys: ts.System, buildInfoPath: string, buildI
resolvedRoot: buildInfo.resolvedRoot?.map(toReadableIncrementalBuildInfoResolvedRoot),
semanticDiagnosticsPerFile: toReadableIncrementalBuildInfoDiagnostic(buildInfo.semanticDiagnosticsPerFile),
emitDiagnosticsPerFile: toReadableIncrementalBuildInfoEmitDiagnostic(buildInfo.emitDiagnosticsPerFile),
changeFileSet: buildInfo.changeFileSet?.map(toFileName),
pendingEmit: pendingEmit === undefined ?
undefined :
[
Expand All @@ -271,6 +276,7 @@ function generateBuildInfoBaseline(sys: ts.System, buildInfoPath: string, buildI
referencedMap: toMapOfReferencedSet(buildInfo.referencedMap),
semanticDiagnosticsPerFile: toReadableIncrementalBuildInfoDiagnostic(buildInfo.semanticDiagnosticsPerFile),
emitDiagnosticsPerFile: toReadableIncrementalBuildInfoEmitDiagnostic(buildInfo.emitDiagnosticsPerFile),
changeFileSet: buildInfo.changeFileSet?.map(toFileName),
affectedFilesPendingEmit: buildInfo.affectedFilesPendingEmit?.map(value => toReadableIncrementalBuilderInfoFilePendingEmit(value, fullEmitForOptions!)),
emitSignatures: buildInfo.emitSignatures?.map(s =>
ts.isNumber(s) ?
Expand Down Expand Up @@ -339,7 +345,11 @@ function generateBuildInfoBaseline(sys: ts.System, buildInfoPath: string, buildI
fullEmitForOptions: ts.BuilderFileEmit,
): ReadableIncrementalBuilderInfoFilePendingEmit {
return [
ts.isNumber(value) ? toFileName(value) : [toFileName(value[0])],
ts.isNumber(value) ?
toFileName(value) :
!value[1] ?
[toFileName(value[0])] :
[toFileName(value[0]), value[1]],
toReadableBuilderFileEmit(ts.toBuilderFileEmit(value, fullEmitForOptions)),
];
}
Expand All @@ -350,7 +360,11 @@ function generateBuildInfoBaseline(sys: ts.System, buildInfoPath: string, buildI
if (emit & ts.BuilderFileEmit.Js) addFlags("Js");
if (emit & ts.BuilderFileEmit.JsMap) addFlags("JsMap");
if (emit & ts.BuilderFileEmit.JsInlineMap) addFlags("JsInlineMap");
if (emit & ts.BuilderFileEmit.Dts) addFlags("Dts");
if ((emit & ts.BuilderFileEmit.Dts) === ts.BuilderFileEmit.Dts) addFlags("Dts");
else {
if (emit & ts.BuilderFileEmit.DtsEmit) addFlags("DtsEmit");
if (emit & ts.BuilderFileEmit.DtsErrors) addFlags("DtsErrors");
}
if (emit & ts.BuilderFileEmit.DtsMap) addFlags("DtsMap");
}
return (result || "None") as ReadableBuilderFileEmit;
Expand All @@ -364,7 +378,7 @@ function generateBuildInfoBaseline(sys: ts.System, buildInfoPath: string, buildI
): readonly ReadableIncrementalBuildInfoDiagnostic[] | undefined {
return diagnostics?.map(d =>
ts.isNumber(d) ?
[toFileName(d), "not cached"] :
[toFileName(d), "not cached or not changed"] :
[toFileName(d[0]), d[1]]
);
}
Expand Down
Loading