Avoid caching resolved signatures for language service requests like signature help (#52679)

This commit is contained in:
Mateusz Burzyński
2023-02-14 09:47:32 -08:00
committed by GitHub
parent f23dd7a7b2
commit ab40f5e5d0
3 changed files with 54 additions and 11 deletions
+18 -11
View File
@@ -1637,9 +1637,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
getResolvedSignature: (node, candidatesOutArray, argumentCount) =>
getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),
getResolvedSignatureForStringLiteralCompletions: (call, editingArgument, candidatesOutArray) =>
getResolvedSignatureWorker(call, candidatesOutArray, /*argumentCount*/ undefined, CheckMode.IsForStringLiteralArgumentCompletions, editingArgument),
runWithInferenceBlockedFromSourceNode(editingArgument, () => getResolvedSignatureWorker(call, candidatesOutArray, /*argumentCount*/ undefined, CheckMode.IsForStringLiteralArgumentCompletions)),
getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) =>
getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp),
runWithoutResolvedSignatureCaching(node, () => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp)),
getExpandedParameters,
hasEffectiveRestParameter,
containsArgumentsReference,
@@ -1814,36 +1814,43 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
isTypeParameterPossiblyReferenced,
};
function runWithInferenceBlockedFromSourceNode<T>(node: Node | undefined, fn: () => T): T {
function runWithoutResolvedSignatureCaching<T>(node: Node | undefined, fn: () => T): T {
const containingCall = findAncestor(node, isCallLikeExpression);
const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
if (containingCall) {
getNodeLinks(containingCall).resolvedSignature = undefined;
}
const result = fn();
if (containingCall) {
getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
}
return result;
}
function runWithInferenceBlockedFromSourceNode<T>(node: Node | undefined, fn: () => T): T {
const containingCall = findAncestor(node, isCallLikeExpression);
if (containingCall) {
let toMarkSkip = node!;
do {
getNodeLinks(toMarkSkip).skipDirectInference = true;
toMarkSkip = toMarkSkip.parent;
} while (toMarkSkip && toMarkSkip !== containingCall);
getNodeLinks(containingCall).resolvedSignature = undefined;
}
const result = fn();
const result = runWithoutResolvedSignatureCaching(node, fn);
if (containingCall) {
let toMarkSkip = node!;
do {
getNodeLinks(toMarkSkip).skipDirectInference = undefined;
toMarkSkip = toMarkSkip.parent;
} while (toMarkSkip && toMarkSkip !== containingCall);
getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
}
return result;
}
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode, editingArgument?: Node): Signature | undefined {
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined {
const node = getParseTreeNode(nodeIn, isCallLikeExpression);
apparentArgumentCount = argumentCount;
const res =
!node ? undefined :
editingArgument ? runWithInferenceBlockedFromSourceNode(editingArgument, () => getResolvedSignature(node, candidatesOutArray, checkMode)) :
getResolvedSignature(node, candidatesOutArray, checkMode);
const res = !node ? undefined : getResolvedSignature(node, candidatesOutArray, checkMode);
apparentArgumentCount = undefined;
return res;
}
@@ -0,0 +1,22 @@
///<reference path="fourslash.ts"/>
// @strict: true
////
//// declare function f(x: string, y: number): any;
////
//// /*1*/f(/*2*/)/*3*/
goTo.marker("2");
verify.signatureHelp({
triggerReason: {
kind: "invoked"
}
})
edit.insert(`"`)
edit.insert(`"`)
verify.signatureHelp({
triggerReason: {
kind: "retrigger"
}
})
verify.not.codeFixAvailable() // trigger typecheck
verify.errorExistsBetweenMarkers("1", "3");
@@ -0,0 +1,14 @@
///<reference path="fourslash.ts"/>
// @strict: true
////
//// interface Events {
//// click: any;
//// drag: any;
//// }
////
//// declare function addListener<K extends keyof Events>(type: K, listener: (ev: Events[K]) => any): void;
////
//// /*1*/addListener("/*2*/")/*3*/
verify.completions({ marker: ["2"], exact: ["click", "drag"] });
verify.errorExistsBetweenMarkers("1", "3");