From f2d1337558780c8e00fd711dd0e04455a13e85f6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 23 Aug 2018 15:34:29 -0700 Subject: [PATCH] In pickLongestCandidateSignature, instantiate using inferred type arguments --- src/compiler/checker.ts | 53 ++++++++++++------- .../fourslash/quickInfoCanBeTruncated.ts | 2 +- .../signatureHelpExplicitTypeArguments.ts | 2 +- .../cases/fourslash/signatureHelpInference.ts | 12 +++++ .../fourslash/trailingCommaSignatureHelp.ts | 2 +- 5 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 tests/cases/fourslash/signatureHelpInference.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0509d53c9b35d..37e699b24d3de 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18636,7 +18636,7 @@ namespace ts { return getInferredTypes(context); } - function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, excludeArgument: boolean[] | undefined, context: InferenceContext): Type[] { + function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, excludeArgument: ReadonlyArray | undefined, context: InferenceContext): Type[] { // Clear out all the inference results from the last time inferTypeArguments was called on this context for (const inference of context.inferences) { // As an optimization, we don't have to clear (and later recompute) inferred types @@ -19050,19 +19050,7 @@ namespace ts { // For a decorator, no arguments are susceptible to contextual typing due to the fact // decorators are applied to a declaration by the emitter, and not to an expression. const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters; - let excludeArgument: boolean[] | undefined; - if (!isDecorator && !isSingleNonGenericCandidate) { - // We do not need to call `getEffectiveArgumentCount` here as it only - // applies when calculating the number of arguments for a decorator. - for (let i = 0; i < args.length; i++) { - if (isContextSensitive(args[i])) { - if (!excludeArgument) { - excludeArgument = new Array(args.length); - } - excludeArgument[i] = true; - } - } - } + let excludeArgument = !isDecorator && !isSingleNonGenericCandidate ? getExcludeArgument(args) : undefined; // The following variables are captured and modified by calls to chooseOverload. // If overload resolution or type argument inference fails, we want to report the @@ -19225,6 +19213,21 @@ namespace ts { } } + function getExcludeArgument(args: ReadonlyArray): boolean[] | undefined { + let excludeArgument: boolean[] | undefined; + // We do not need to call `getEffectiveArgumentCount` here as it only + // applies when calculating the number of arguments for a decorator. + for (let i = 0; i < args.length; i++) { + if (isContextSensitive(args[i])) { + if (!excludeArgument) { + excludeArgument = new Array(args.length); + } + excludeArgument[i] = true; + } + } + return excludeArgument; + } + // No signature was applicable. We have already reported the errors for the invalid signature. // If this is a type resolution session, e.g. Language Service, try to get better information than anySignature. function getCandidateForOverloadFailure( @@ -19303,17 +19306,29 @@ namespace ts { return candidate; } - const typeArgumentNodes: ReadonlyArray = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments || emptyArray : emptyArray; + const typeArgumentNodes: ReadonlyArray | undefined = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments : undefined; + const instantiated = typeArgumentNodes + ? createSignatureInstantiation(candidate, getTypeArgumentsFromNodes(typeArgumentNodes, typeParameters, isInJavaScriptFile(node))) + : inferSignatureInstantiationForOverloadFailure(node, typeParameters, candidate, args); + candidates[bestIndex] = instantiated; + return instantiated; + } + + function getTypeArgumentsFromNodes(typeArgumentNodes: ReadonlyArray, typeParameters: ReadonlyArray, isJs: boolean): ReadonlyArray { const typeArguments = typeArgumentNodes.map(getTypeOfNode); while (typeArguments.length > typeParameters.length) { typeArguments.pop(); } while (typeArguments.length < typeParameters.length) { - typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isInJavaScriptFile(node))); + typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isJs)); } - const instantiated = createSignatureInstantiation(candidate, typeArguments); - candidates[bestIndex] = instantiated; - return instantiated; + return typeArguments; + } + + function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: ReadonlyArray, candidate: Signature, args: ReadonlyArray): Signature { + const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); + const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args), inferenceContext); + return createSignatureInstantiation(candidate, typeArgumentTypes); } function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number { diff --git a/tests/cases/fourslash/quickInfoCanBeTruncated.ts b/tests/cases/fourslash/quickInfoCanBeTruncated.ts index bdf64323636cf..25ca216a07553 100644 --- a/tests/cases/fourslash/quickInfoCanBeTruncated.ts +++ b/tests/cases/fourslash/quickInfoCanBeTruncated.ts @@ -519,7 +519,7 @@ verify.quickInfoIs(`type Less = "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" | goTo.marker("3"); verify.signatureHelp({ marker: "3", - text: `f(s: "_0" | "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" | "_8" | "_9" | "_10" | "_11" | "_12" | "_13" | "_14" | "_15" | "_16" | "_17" | "_18" | "_19" | "_20" | "_21" | "_22" | "_23" | "_24" | ... 474 more ... | "_499", x: never, y: string): void` + text: `f(s: "_499", x: "_0" | "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" | "_8" | "_9" | "_10" | "_11" | "_12" | "_13" | "_14" | "_15" | "_16" | "_17" | "_18" | "_19" | "_20" | "_21" | "_22" | "_23" | "_24" | ... 473 more ... | "_498", y: string): void` }); goTo.marker("4"); verify.quickInfoIs(`type Decomposed = { diff --git a/tests/cases/fourslash/signatureHelpExplicitTypeArguments.ts b/tests/cases/fourslash/signatureHelpExplicitTypeArguments.ts index 2511cfecc1921..b1af18469a888 100644 --- a/tests/cases/fourslash/signatureHelpExplicitTypeArguments.ts +++ b/tests/cases/fourslash/signatureHelpExplicitTypeArguments.ts @@ -8,7 +8,7 @@ verify.signatureHelp( { marker: "1", text: "f(x: number, y: string): number" }, - { marker: "2", text: "f(x: {}, y: {}): {}" }, + { marker: "2", text: "f(x: boolean, y: string): boolean" }, // too few -- fill in rest with {} { marker: "3", text: "f(x: number, y: {}): number" }, // too many -- ignore extra type arguments diff --git a/tests/cases/fourslash/signatureHelpInference.ts b/tests/cases/fourslash/signatureHelpInference.ts new file mode 100644 index 0000000000000..186dba9c31d5d --- /dev/null +++ b/tests/cases/fourslash/signatureHelpInference.ts @@ -0,0 +1,12 @@ +/// + +////declare function f(a: T, b: T, c: T): void; +////f("x", /**/); + +verify.signatureHelp({ + marker: "", + text: 'f(a: "x", b: "x", c: "x"): void', + parameterCount: 3, + parameterName: "b", + parameterSpan: 'b: "x"', +}); diff --git a/tests/cases/fourslash/trailingCommaSignatureHelp.ts b/tests/cases/fourslash/trailingCommaSignatureHelp.ts index 362f7145c03d1..e22ea1c33d303 100644 --- a/tests/cases/fourslash/trailingCommaSignatureHelp.ts +++ b/tests/cases/fourslash/trailingCommaSignatureHelp.ts @@ -25,6 +25,6 @@ verify.signatureHelp( }, { marker: "b", - text: "f(a: {}): {}", + text: "f(a: 2): 2", }, );