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

Merge diagnosticsProducing and nonDiagnosticsProducing checkers into a single checker supporting lazy diagnostics #36747

Merged
merged 5 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
915 changes: 497 additions & 418 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

47 changes: 14 additions & 33 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,7 @@ namespace ts {
let files: SourceFile[];
let symlinks: SymlinkCache | undefined;
let commonSourceDirectory: string;
let diagnosticsProducingTypeChecker: TypeChecker;
let noDiagnosticsTypeChecker: TypeChecker;
let typeChecker: TypeChecker;
let classifiableNames: Set<__String>;
const ambientModuleNameToUnmodifiedFileName = new Map<string, string>();
let fileReasons = createMultiMap<Path, FileIncludeReason>();
Expand Down Expand Up @@ -1304,21 +1303,19 @@ namespace ts {
getProgramDiagnostics,
getTypeChecker,
getClassifiableNames,
getDiagnosticsProducingTypeChecker,
getCommonSourceDirectory,
emit,
getCurrentDirectory: () => currentDirectory,
getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
getInstantiationCount: () => getDiagnosticsProducingTypeChecker().getInstantiationCount(),
getRelationCacheSizes: () => getDiagnosticsProducingTypeChecker().getRelationCacheSizes(),
getNodeCount: () => getTypeChecker().getNodeCount(),
getIdentifierCount: () => getTypeChecker().getIdentifierCount(),
getSymbolCount: () => getTypeChecker().getSymbolCount(),
getTypeCount: () => getTypeChecker().getTypeCount(),
getInstantiationCount: () => getTypeChecker().getInstantiationCount(),
getRelationCacheSizes: () => getTypeChecker().getRelationCacheSizes(),
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
isSourceFileFromExternalLibrary,
isSourceFileDefaultLibrary,
dropDiagnosticsProducingTypeChecker,
getSourceFileFromReference,
getLibFileFromReference,
sourceFileToPackageName,
Expand Down Expand Up @@ -1981,16 +1978,8 @@ namespace ts {
}
}

function getDiagnosticsProducingTypeChecker() {
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
}

function dropDiagnosticsProducingTypeChecker() {
diagnosticsProducingTypeChecker = undefined!;
}

function getTypeChecker() {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
return typeChecker || (typeChecker = createTypeChecker(program));
}

function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
Expand Down Expand Up @@ -2018,7 +2007,7 @@ namespace ts {
// This is because in the -out scenario all files need to be emitted, and therefore all
// files need to be type checked. And the way to specify that all files need to be type
// checked is to not pass the file to getEmitResolver.
const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken);
const emitResolver = getTypeChecker().getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken);

performance.mark("beforeEmit");

Expand Down Expand Up @@ -2122,15 +2111,7 @@ namespace ts {
if (e instanceof OperationCanceledException) {
// We were canceled while performing the operation. Because our type checker
// might be a bad state, we need to throw it away.
//
// Note: we are overly aggressive here. We do not actually *have* to throw away
// the "noDiagnosticsTypeChecker". However, for simplicity, i'd like to keep
// the lifetimes of these two TypeCheckers the same. Also, we generally only
// cancel when the user has made a change anyways. And, in that case, we (the
// program instance) will get thrown away anyways. So trying to keep one of
// these type checkers alive doesn't serve much purpose.
noDiagnosticsTypeChecker = undefined!;
diagnosticsProducingTypeChecker = undefined!;
typeChecker = undefined!;
}

throw e;
Expand All @@ -2154,7 +2135,7 @@ namespace ts {
return emptyArray;
}

const typeChecker = getDiagnosticsProducingTypeChecker();
const typeChecker = getTypeChecker();

Debug.assert(!!sourceFile.bindDiagnostics);

Expand Down Expand Up @@ -2210,7 +2191,7 @@ namespace ts {

function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
return runWithCancellationToken(() => {
return getDiagnosticsProducingTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
return getTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
});
}

Expand Down Expand Up @@ -2445,7 +2426,7 @@ namespace ts {

function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
return runWithCancellationToken(() => {
const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
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;
});
Expand Down Expand Up @@ -2496,7 +2477,7 @@ namespace ts {
}

function getGlobalDiagnostics(): SortedReadonlyArray<Diagnostic> {
return rootNames.length ? sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>;
return rootNames.length ? sortAndDeduplicateDiagnostics(getTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>;
}

function getConfigFileParsingDiagnostics(): readonly Diagnostic[] {
Expand Down
5 changes: 0 additions & 5 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4000,11 +4000,6 @@ namespace ts {

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

// For testing purposes only. Should not be used by any other consumers (including the
// language service).
/* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker;
/* @internal */ dropDiagnosticsProducingTypeChecker(): void;

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

/* @internal */ getClassifiableNames(): Set<__String>;
Expand Down
2 changes: 1 addition & 1 deletion src/harness/harnessIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ namespace Harness {
// These types are equivalent, but depend on what order the compiler observed
// certain parts of the program.

const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true, !!hasErrorBaseline);
const fullWalker = new TypeWriterWalker(program, !!hasErrorBaseline);

// Produce baselines. The first gives the types for all expressions.
// The second gives symbols for all identifiers.
Expand Down
6 changes: 2 additions & 4 deletions src/harness/typeWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,10 @@ namespace Harness {

private checker: ts.TypeChecker;

constructor(private program: ts.Program, fullTypeCheck: boolean, private hadErrorBaseline: boolean) {
constructor(private program: ts.Program, private hadErrorBaseline: boolean) {
// Consider getting both the diagnostics checker and the non-diagnostics checker to verify
// they are consistent.
this.checker = fullTypeCheck
? program.getDiagnosticsProducingTypeChecker()
: program.getTypeChecker();
this.checker = program.getTypeChecker();
}

public *getSymbols(fileName: string): IterableIterator<TypeWriterSymbolResult> {
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/addMissingAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace ts.codefix {
errorCodes,
getCodeActions: function getCodeActionsToAddMissingAsync(context) {
const { sourceFile, errorCode, cancellationToken, program, span } = context;
const diagnostic = find(program.getDiagnosticsProducingTypeChecker().getDiagnostics(sourceFile, cancellationToken), getIsMatchingAsyncError(span, errorCode));
const diagnostic = find(program.getTypeChecker().getDiagnostics(sourceFile, cancellationToken), getIsMatchingAsyncError(span, errorCode));
const directSpan = diagnostic && diagnostic.relatedInformation && find(diagnostic.relatedInformation, r => r.code === Diagnostics.Did_you_mean_to_mark_this_function_as_async.code) as TextSpan | undefined;

const decl = getFixableErrorSpanDeclaration(sourceFile, directSpan);
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/addMissingAwait.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ namespace ts.codefix {
}

function isMissingAwaitError(sourceFile: SourceFile, errorCode: number, span: TextSpan, cancellationToken: CancellationToken, program: Program) {
const checker = program.getDiagnosticsProducingTypeChecker();
const checker = program.getTypeChecker();
const diagnostics = checker.getDiagnostics(sourceFile, cancellationToken);
return some(diagnostics, ({ start, length, relatedInformation, code }) =>
isNumber(start) && isNumber(length) && textSpansEqual({ start, length }, span) &&
Expand Down
Loading