Provide better services for incomplete generic calls (#16535)
* Provide better services for incomplete generic calls * Use clearer name * Remove `inferredAnyDefaultTypeArgument` and `getBestGuessSignature`; have `resolveSignature` always get the best signature if !produceDiagnostics * Update names and comments
This commit is contained in:
parent
aeb5264b74
commit
18357543c6
|
@ -75,6 +75,9 @@ namespace ts {
|
|||
undefinedSymbol.declarations = [];
|
||||
const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments");
|
||||
|
||||
/** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */
|
||||
let apparentArgumentCount: number | undefined;
|
||||
|
||||
// for public members that accept a Node or one of its subtypes, we must guard against
|
||||
// synthetic nodes created during transformations by calling `getParseTreeNode`.
|
||||
// for most of these, we perform the guard only on `checker` to avoid any possible
|
||||
|
@ -160,9 +163,12 @@ namespace ts {
|
|||
return node ? getContextualType(node) : undefined;
|
||||
},
|
||||
getFullyQualifiedName,
|
||||
getResolvedSignature: (node, candidatesOutArray?) => {
|
||||
getResolvedSignature: (node, candidatesOutArray, theArgumentCount) => {
|
||||
node = getParseTreeNode(node, isCallLikeExpression);
|
||||
return node ? getResolvedSignature(node, candidatesOutArray) : undefined;
|
||||
apparentArgumentCount = theArgumentCount;
|
||||
const res = node ? getResolvedSignature(node, candidatesOutArray) : undefined;
|
||||
apparentArgumentCount = undefined;
|
||||
return res;
|
||||
},
|
||||
getConstantValue: node => {
|
||||
node = getParseTreeNode(node, canHaveConstantValue);
|
||||
|
@ -6313,12 +6319,12 @@ namespace ts {
|
|||
// If a type parameter does not have a default type, or if the default type
|
||||
// is a forward reference, the empty object type is used.
|
||||
for (let i = numTypeArguments; i < numTypeParameters; i++) {
|
||||
typeArguments[i] = isJavaScript ? anyType : emptyObjectType;
|
||||
typeArguments[i] = getDefaultTypeArgumentType(isJavaScript);
|
||||
}
|
||||
for (let i = numTypeArguments; i < numTypeParameters; i++) {
|
||||
const mapper = createTypeMapper(typeParameters, typeArguments);
|
||||
const defaultType = getDefaultFromTypeParameter(typeParameters[i]);
|
||||
typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : isJavaScript ? anyType : emptyObjectType;
|
||||
typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7968,7 +7974,8 @@ namespace ts {
|
|||
};
|
||||
}
|
||||
|
||||
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
|
||||
function createTypeMapper(sources: TypeParameter[], targets: Type[]): TypeMapper {
|
||||
Debug.assert(targets === undefined || sources.length === targets.length);
|
||||
const mapper: TypeMapper = sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
|
||||
sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
|
||||
makeArrayTypeMapper(sources, targets);
|
||||
|
@ -7976,7 +7983,7 @@ namespace ts {
|
|||
return mapper;
|
||||
}
|
||||
|
||||
function createTypeEraser(sources: Type[]): TypeMapper {
|
||||
function createTypeEraser(sources: TypeParameter[]): TypeMapper {
|
||||
return createTypeMapper(sources, /*targets*/ undefined);
|
||||
}
|
||||
|
||||
|
@ -10628,7 +10635,7 @@ namespace ts {
|
|||
context));
|
||||
}
|
||||
else {
|
||||
inferredType = context.flags & InferenceFlags.AnyDefault ? anyType : emptyObjectType;
|
||||
inferredType = getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault));
|
||||
}
|
||||
}
|
||||
inference.inferredType = inferredType;
|
||||
|
@ -10644,6 +10651,10 @@ namespace ts {
|
|||
return inferredType;
|
||||
}
|
||||
|
||||
function getDefaultTypeArgumentType(isInJavaScriptFile: boolean): Type {
|
||||
return isInJavaScriptFile ? anyType : emptyObjectType;
|
||||
}
|
||||
|
||||
function getInferredTypes(context: InferenceContext): Type[] {
|
||||
const result: Type[] = [];
|
||||
for (let i = 0; i < context.inferences.length; i++) {
|
||||
|
@ -12787,7 +12798,9 @@ namespace ts {
|
|||
const args = getEffectiveCallArguments(callTarget);
|
||||
const argIndex = indexOf(args, arg);
|
||||
if (argIndex >= 0) {
|
||||
const signature = getResolvedOrAnySignature(callTarget);
|
||||
// If we're already in the process of resolving the given signature, don't resolve again as
|
||||
// that could cause infinite recursion. Instead, return anySignature.
|
||||
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
|
||||
return getTypeAtPosition(signature, argIndex);
|
||||
}
|
||||
return undefined;
|
||||
|
@ -14962,16 +14975,14 @@ namespace ts {
|
|||
}
|
||||
|
||||
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): Type[] {
|
||||
const inferences = context.inferences;
|
||||
|
||||
// Clear out all the inference results from the last time inferTypeArguments was called on this context
|
||||
for (let i = 0; i < inferences.length; i++) {
|
||||
for (const inference of context.inferences) {
|
||||
// As an optimization, we don't have to clear (and later recompute) inferred types
|
||||
// for type parameters that have already been fixed on the previous call to inferTypeArguments.
|
||||
// It would be just as correct to reset all of them. But then we'd be repeating the same work
|
||||
// for the type parameters that were fixed, namely the work done by getInferredType.
|
||||
if (!inferences[i].isFixed) {
|
||||
inferences[i].inferredType = undefined;
|
||||
if (!inference.isFixed) {
|
||||
inference.inferredType = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15640,19 +15651,34 @@ namespace ts {
|
|||
}
|
||||
|
||||
// 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 that anySignature.
|
||||
// Pick the first candidate that matches the arity. This way we can get a contextual type for cases like:
|
||||
// declare function f(a: { xa: number; xb: number; });
|
||||
// f({ |
|
||||
// If this is a type resolution session, e.g. Language Service, try to get better information than anySignature.
|
||||
// Pick the longest signature. This way we can get a contextual type for cases like:
|
||||
// declare function f(a: { xa: number; xb: number; }, b: number);
|
||||
// f({ |
|
||||
// Also, use explicitly-supplied type arguments if they are provided, so we can get a contextual signature in cases like:
|
||||
// declare function f<T>(k: keyof T);
|
||||
// f<Foo>("
|
||||
if (!produceDiagnostics) {
|
||||
for (let candidate of candidates) {
|
||||
if (hasCorrectArity(node, args, candidate)) {
|
||||
if (candidate.typeParameters && typeArguments) {
|
||||
candidate = getSignatureInstantiation(candidate, map(typeArguments, getTypeFromTypeNode));
|
||||
}
|
||||
return candidate;
|
||||
Debug.assert(candidates.length > 0); // Else would have exited above.
|
||||
const bestIndex = getLongestCandidateIndex(candidates, apparentArgumentCount === undefined ? args.length : apparentArgumentCount);
|
||||
const candidate = candidates[bestIndex];
|
||||
|
||||
const { typeParameters } = candidate;
|
||||
if (typeParameters && callLikeExpressionMayHaveTypeArguments(node) && node.typeArguments) {
|
||||
const typeArguments = node.typeArguments.map(getTypeOfNode);
|
||||
while (typeArguments.length > typeParameters.length) {
|
||||
typeArguments.pop();
|
||||
}
|
||||
while (typeArguments.length < typeParameters.length) {
|
||||
typeArguments.push(getDefaultTypeArgumentType(isInJavaScriptFile(node)));
|
||||
}
|
||||
|
||||
const instantiated = createSignatureInstantiation(candidate, typeArguments);
|
||||
candidates[bestIndex] = instantiated;
|
||||
return instantiated;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return resolveErrorCall(node);
|
||||
|
@ -15660,7 +15686,8 @@ namespace ts {
|
|||
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
|
||||
candidateForArgumentError = undefined;
|
||||
candidateForTypeArgumentError = undefined;
|
||||
for (const originalCandidate of candidates) {
|
||||
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
|
||||
const originalCandidate = candidates[candidateIndex];
|
||||
if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -15692,6 +15719,7 @@ namespace ts {
|
|||
}
|
||||
const index = excludeArgument ? indexOf(excludeArgument, /*value*/ true) : -1;
|
||||
if (index < 0) {
|
||||
candidates[candidateIndex] = candidate;
|
||||
return candidate;
|
||||
}
|
||||
excludeArgument[index] = false;
|
||||
|
@ -15703,6 +15731,24 @@ namespace ts {
|
|||
|
||||
}
|
||||
|
||||
function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number {
|
||||
let maxParamsIndex = -1;
|
||||
let maxParams = -1;
|
||||
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
const candidate = candidates[i];
|
||||
if (candidate.hasRestParameter || candidate.parameters.length >= argsCount) {
|
||||
return i;
|
||||
}
|
||||
if (candidate.parameters.length > maxParams) {
|
||||
maxParams = candidate.parameters.length;
|
||||
maxParamsIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return maxParamsIndex;
|
||||
}
|
||||
|
||||
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature {
|
||||
if (node.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
const superType = checkSuperExpression(node.expression);
|
||||
|
@ -16017,9 +16063,7 @@ namespace ts {
|
|||
|
||||
const callSignatures = elementType && getSignaturesOfType(elementType, SignatureKind.Call);
|
||||
if (callSignatures && callSignatures.length > 0) {
|
||||
let callSignature: Signature;
|
||||
callSignature = resolveCall(openingLikeElement, callSignatures, candidatesOutArray);
|
||||
return callSignature;
|
||||
return resolveCall(openingLikeElement, callSignatures, candidatesOutArray);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
@ -16069,12 +16113,6 @@ namespace ts {
|
|||
return result;
|
||||
}
|
||||
|
||||
function getResolvedOrAnySignature(node: CallLikeExpression) {
|
||||
// If we're already in the process of resolving the given signature, don't resolve again as
|
||||
// that could cause infinite recursion. Instead, return anySignature.
|
||||
return getNodeLinks(node).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a declaration can be treated as a constructor in a JavaScript
|
||||
* file.
|
||||
|
|
|
@ -2587,7 +2587,11 @@ namespace ts {
|
|||
getAugmentedPropertiesOfType(type: Type): Symbol[];
|
||||
getRootSymbols(symbol: Symbol): Symbol[];
|
||||
getContextualType(node: Expression): Type | undefined;
|
||||
getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature | undefined;
|
||||
/**
|
||||
* returns unknownSignature in the case of an error. Don't know when it returns undefined.
|
||||
* @param argumentCount Apparent number of arguments, passed in case of a possibly incomplete call. This should come from an ArgumentListInfo. See `signatureHelp.ts`.
|
||||
*/
|
||||
getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined;
|
||||
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature | undefined;
|
||||
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined;
|
||||
isUndefinedSymbol(symbol: Symbol): boolean;
|
||||
|
@ -3382,8 +3386,8 @@ namespace ts {
|
|||
/* @internal */
|
||||
export interface TypeMapper {
|
||||
(t: TypeParameter): Type;
|
||||
mappedTypes?: Type[]; // Types mapped by this mapper
|
||||
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
|
||||
mappedTypes?: TypeParameter[]; // Types mapped by this mapper
|
||||
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
|
||||
}
|
||||
|
||||
export const enum InferencePriority {
|
||||
|
|
|
@ -4688,6 +4688,11 @@ namespace ts {
|
|||
// All node tests in the following list should *not* reference parent pointers so that
|
||||
// they may be used with transformations.
|
||||
namespace ts {
|
||||
/* @internal */
|
||||
export function isSyntaxList(n: Node): n is SyntaxList {
|
||||
return n.kind === SyntaxKind.SyntaxList;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isNode(node: Node) {
|
||||
return isNodeKind(node.kind);
|
||||
|
|
|
@ -234,7 +234,7 @@ namespace ts.Completions {
|
|||
const entries: CompletionEntry[] = [];
|
||||
const uniques = createMap<true>();
|
||||
|
||||
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates);
|
||||
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker, uniques);
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace ts.SignatureHelp {
|
|||
invocation: CallLikeExpression;
|
||||
argumentsSpan: TextSpan;
|
||||
argumentIndex?: number;
|
||||
/** argumentCount is the *apparent* number of arguments. */
|
||||
argumentCount: number;
|
||||
}
|
||||
|
||||
|
@ -29,17 +30,14 @@ namespace ts.SignatureHelp {
|
|||
}
|
||||
|
||||
const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile);
|
||||
if (!argumentInfo) return undefined;
|
||||
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
// Semantic filtering of signature help
|
||||
if (!argumentInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const call = argumentInfo.invocation;
|
||||
const candidates = <Signature[]>[];
|
||||
const resolvedSignature = typeChecker.getResolvedSignature(call, candidates);
|
||||
const candidates: Signature[] = [];
|
||||
const resolvedSignature = typeChecker.getResolvedSignature(call, candidates, argumentInfo.argumentCount);
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
if (!candidates.length) {
|
||||
|
@ -101,7 +99,10 @@ namespace ts.SignatureHelp {
|
|||
*/
|
||||
export function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
|
||||
if (isCallOrNewExpression(node.parent)) {
|
||||
const callExpression = <CallExpression>node.parent;
|
||||
const invocation = node.parent;
|
||||
let list: Node;
|
||||
let argumentIndex: number;
|
||||
|
||||
// There are 3 cases to handle:
|
||||
// 1. The token introduces a list, and should begin a signature help session
|
||||
// 2. The token is either not associated with a list, or ends a list, so the session should end
|
||||
|
@ -116,48 +117,30 @@ namespace ts.SignatureHelp {
|
|||
// Case 3:
|
||||
// foo<T#, U#>(a#, #b#) -> The token is buried inside a list, and should give signature help
|
||||
// Find out if 'node' is an argument, a type argument, or neither
|
||||
if (node.kind === SyntaxKind.LessThanToken ||
|
||||
node.kind === SyntaxKind.OpenParenToken) {
|
||||
if (node.kind === SyntaxKind.LessThanToken || node.kind === SyntaxKind.OpenParenToken) {
|
||||
// Find the list that starts right *after* the < or ( token.
|
||||
// If the user has just opened a list, consider this item 0.
|
||||
const list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile);
|
||||
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
|
||||
list = getChildListThatStartsWithOpenerToken(invocation, node, sourceFile);
|
||||
Debug.assert(list !== undefined);
|
||||
return {
|
||||
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
|
||||
invocation: callExpression,
|
||||
argumentsSpan: getApplicableSpanForArguments(list, sourceFile),
|
||||
argumentIndex: 0,
|
||||
argumentCount: getArgumentCount(list)
|
||||
};
|
||||
argumentIndex = 0;
|
||||
}
|
||||
else {
|
||||
// findListItemInfo can return undefined if we are not in parent's argument list
|
||||
// or type argument list. This includes cases where the cursor is:
|
||||
// - To the right of the closing parenthesis, non-substitution template, or template tail.
|
||||
// - Between the type arguments and the arguments (greater than token)
|
||||
// - On the target of the call (parent.func)
|
||||
// - On the 'new' keyword in a 'new' expression
|
||||
list = findContainingList(node);
|
||||
if (!list) return undefined;
|
||||
argumentIndex = getArgumentIndex(list, node);
|
||||
}
|
||||
|
||||
// findListItemInfo can return undefined if we are not in parent's argument list
|
||||
// or type argument list. This includes cases where the cursor is:
|
||||
// - To the right of the closing parenthesis, non-substitution template, or template tail.
|
||||
// - Between the type arguments and the arguments (greater than token)
|
||||
// - On the target of the call (parent.func)
|
||||
// - On the 'new' keyword in a 'new' expression
|
||||
const listItemInfo = findListItemInfo(node);
|
||||
if (listItemInfo) {
|
||||
const list = listItemInfo.list;
|
||||
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
|
||||
|
||||
const argumentIndex = getArgumentIndex(list, node);
|
||||
const argumentCount = getArgumentCount(list);
|
||||
|
||||
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount,
|
||||
`argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
|
||||
|
||||
return {
|
||||
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
|
||||
invocation: callExpression,
|
||||
argumentsSpan: getApplicableSpanForArguments(list, sourceFile),
|
||||
argumentIndex: argumentIndex,
|
||||
argumentCount: argumentCount
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
const kind = invocation.typeArguments && invocation.typeArguments.pos === list.pos ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments;
|
||||
const argumentCount = getArgumentCount(list);
|
||||
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
|
||||
const argumentsSpan = getApplicableSpanForArguments(list, sourceFile);
|
||||
return { kind, invocation, argumentsSpan, argumentIndex, argumentCount };
|
||||
}
|
||||
else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) {
|
||||
// Check if we're actually inside the template;
|
||||
|
@ -294,8 +277,8 @@ namespace ts.SignatureHelp {
|
|||
kind: ArgumentListKind.TaggedTemplateArguments,
|
||||
invocation: tagExpression,
|
||||
argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression, sourceFile),
|
||||
argumentIndex: argumentIndex,
|
||||
argumentCount: argumentCount
|
||||
argumentIndex,
|
||||
argumentCount
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -367,38 +350,10 @@ namespace ts.SignatureHelp {
|
|||
return children[indexOfOpenerToken + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* The selectedItemIndex could be negative for several reasons.
|
||||
* 1. There are too many arguments for all of the overloads
|
||||
* 2. None of the overloads were type compatible
|
||||
* The solution here is to try to pick the best overload by picking
|
||||
* either the first one that has an appropriate number of parameters,
|
||||
* or the one with the most parameters.
|
||||
*/
|
||||
function selectBestInvalidOverloadIndex(candidates: Signature[], argumentCount: number): number {
|
||||
let maxParamsSignatureIndex = -1;
|
||||
let maxParams = -1;
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
const candidate = candidates[i];
|
||||
|
||||
if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) {
|
||||
return i;
|
||||
}
|
||||
|
||||
if (candidate.parameters.length > maxParams) {
|
||||
maxParams = candidate.parameters.length;
|
||||
maxParamsSignatureIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return maxParamsSignatureIndex;
|
||||
}
|
||||
|
||||
function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo, typeChecker: TypeChecker): SignatureHelpItems {
|
||||
const applicableSpan = argumentListInfo.argumentsSpan;
|
||||
function createSignatureHelpItems(candidates: Signature[], resolvedSignature: Signature, argumentListInfo: ArgumentListInfo, typeChecker: TypeChecker): SignatureHelpItems {
|
||||
const { argumentCount, argumentsSpan: applicableSpan, invocation, argumentIndex } = argumentListInfo;
|
||||
const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments;
|
||||
|
||||
const invocation = argumentListInfo.invocation;
|
||||
const callTarget = getInvokedExpression(invocation);
|
||||
const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget);
|
||||
const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined);
|
||||
|
@ -415,7 +370,8 @@ namespace ts.SignatureHelp {
|
|||
if (isTypeParameterList) {
|
||||
isVariadic = false; // type parameter lists are not variadic
|
||||
prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken));
|
||||
const typeParameters = candidateSignature.typeParameters;
|
||||
// Use `.mapper` to ensure we get the generic type arguments even if this is an instantiated version of the signature.
|
||||
const typeParameters = candidateSignature.mapper ? candidateSignature.mapper.mappedTypes : candidateSignature.typeParameters;
|
||||
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
|
||||
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
|
||||
const parameterParts = mapToDisplayParts(writer =>
|
||||
|
@ -429,8 +385,7 @@ namespace ts.SignatureHelp {
|
|||
addRange(prefixDisplayParts, typeParameterParts);
|
||||
prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
|
||||
const parameters = candidateSignature.parameters;
|
||||
signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray;
|
||||
signatureHelpParameters = map(candidateSignature.parameters, createSignatureHelpParameterForParameter);
|
||||
suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
|
||||
|
@ -449,25 +404,12 @@ namespace ts.SignatureHelp {
|
|||
};
|
||||
});
|
||||
|
||||
const argumentIndex = argumentListInfo.argumentIndex;
|
||||
|
||||
// argumentCount is the *apparent* number of arguments.
|
||||
const argumentCount = argumentListInfo.argumentCount;
|
||||
|
||||
let selectedItemIndex = candidates.indexOf(bestSignature);
|
||||
if (selectedItemIndex < 0) {
|
||||
selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount);
|
||||
}
|
||||
|
||||
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
|
||||
|
||||
return {
|
||||
items,
|
||||
applicableSpan,
|
||||
selectedItemIndex,
|
||||
argumentIndex,
|
||||
argumentCount
|
||||
};
|
||||
const selectedItemIndex = candidates.indexOf(resolvedSignature);
|
||||
Debug.assert(selectedItemIndex !== -1); // If candidates is non-empty it should always include bestSignature. We check for an empty candidates before calling this function.
|
||||
|
||||
return { items, applicableSpan, selectedItemIndex, argumentIndex, argumentCount };
|
||||
|
||||
function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter {
|
||||
const displayParts = mapToDisplayParts(writer =>
|
||||
|
|
|
@ -588,14 +588,14 @@ namespace ts {
|
|||
return forEach(n.getChildren(sourceFile), c => c.kind === kind && c);
|
||||
}
|
||||
|
||||
export function findContainingList(node: Node): Node {
|
||||
export function findContainingList(node: Node): SyntaxList | undefined {
|
||||
// The node might be a list element (nonsynthetic) or a comma (synthetic). Either way, it will
|
||||
// be parented by the container of the SyntaxList, not the SyntaxList itself.
|
||||
// In order to find the list item index, we first need to locate SyntaxList itself and then search
|
||||
// for the position of the relevant node (or comma).
|
||||
const syntaxList = forEach(node.parent.getChildren(), c => {
|
||||
// find syntax list that covers the span of the node
|
||||
if (c.kind === SyntaxKind.SyntaxList && c.pos <= node.pos && c.end >= node.end) {
|
||||
if (isSyntaxList(c) && c.pos <= node.pos && c.end >= node.end) {
|
||||
return c;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ verify.quickInfos({
|
|||
Asig: "constructor A<string>(): A<string>",
|
||||
Bsig: "constructor B<string>(val: string): B<string>",
|
||||
Csig: "constructor C<string>(val: string): C<string>",
|
||||
Dsig: "constructor D<T>(val: T): D<T>" // Cannot resolve signature
|
||||
Dsig: "constructor D<string>(val: string): D<string>" // Cannot resolve signature. Still fill in generics based on explicit type arguments.
|
||||
});
|
||||
|
||||
goTo.marker(C);
|
||||
|
@ -29,7 +29,7 @@ verify.quickInfos({
|
|||
Asig: "constructor A<string>(): A<string>",
|
||||
Bsig: "constructor B<string>(val: string): B<string>",
|
||||
Csig: "constructor C<T>(): C<T>", // Cannot resolve signature
|
||||
Dsig: "constructor D<T>(val: T): D<T>" // Cannot resolve signature
|
||||
Dsig: "constructor D<string>(val: string): D<string>" // Cannot resolve signature
|
||||
});
|
||||
|
||||
goTo.marker(D);
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
|
||||
goTo.marker('0');
|
||||
verify.completionListContains("jspm");
|
||||
verify.completionListAllowsNewIdentifier();
|
||||
verify.completionListCount(1);
|
||||
//verify.completionListAllowsNewIdentifier();
|
||||
//verify.completionListCount(1);
|
||||
|
||||
goTo.marker('1');
|
||||
/*goTo.marker('1');
|
||||
verify.completionListContains("jspm:dev");
|
||||
verify.completionListAllowsNewIdentifier();
|
||||
verify.completionListCount(4);
|
||||
verify.completionListCount(4);*/
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Note: Giving the functions two parameters means that the checker cannot resolve their signatures normally,
|
||||
// so it makes a best guess.
|
||||
|
||||
////interface I { x: number; y: number; }
|
||||
////
|
||||
////declare function f<T>(x: T, y: number): void;
|
||||
////f<I>({ /*f*/ });
|
||||
////
|
||||
////declare function g<T>(x: keyof T, y: number): void;
|
||||
////g<I>("/*g*/");
|
||||
|
||||
goTo.marker("f");
|
||||
verify.completionListCount(2);
|
||||
verify.completionListContains("x");
|
||||
verify.completionListContains("y");
|
||||
|
||||
goTo.marker("g");
|
||||
verify.completionListContains("x");
|
||||
verify.completionListContains("y");
|
||||
verify.completionListCount(2);
|
|
@ -4,4 +4,4 @@
|
|||
////f(/**/
|
||||
|
||||
goTo.marker();
|
||||
verify.currentSignatureHelpIs('f<T>(a: T): T');
|
||||
verify.currentSignatureHelpIs('f(a: {}): {}');
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
////f(/**/
|
||||
|
||||
goTo.marker();
|
||||
verify.currentSignatureHelpIs('f<T>(a: T): T');
|
||||
verify.currentSignatureHelpIs('f(a: {}): {}');
|
||||
|
|
|
@ -29,7 +29,7 @@ verify.currentSignatureHelpIs('foo3<T>(x: number, callback: (y3: T) => number):
|
|||
// verify.currentSignatureHelpIs('foo4(x: number, callback: (y4: string) => number): void');
|
||||
|
||||
goTo.marker('5');
|
||||
verify.currentSignatureHelpIs('foo5<T>(x: number, callback: (y5: T) => number): void');
|
||||
verify.currentSignatureHelpIs('foo5(x: number, callback: (y5: string) => number): void');
|
||||
|
||||
goTo.marker('6');
|
||||
// verify.currentSignatureHelpIs('foo6(x: number, callback: (y6: {}) => number): void');
|
||||
|
|
|
@ -36,7 +36,7 @@ verify.currentSignatureHelpIs('foo3<T>(x: number, callback: (y3: T) => number):
|
|||
// verify.currentSignatureHelpIs('foo4(x: number, callback: (y4: string) => number): void');
|
||||
|
||||
goTo.marker('5');
|
||||
verify.currentSignatureHelpIs('foo5<T>(x: number, callback: (y5: T) => number): void');
|
||||
verify.currentSignatureHelpIs('foo5(x: number, callback: (y5: string) => number): void');
|
||||
|
||||
goTo.marker('6');
|
||||
// verify.currentSignatureHelpIs('foo6(x: number, callback: (y6: {}) => number): void');
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
goTo.marker("3");
|
||||
verify.currentParameterHelpArgumentNameIs("a");
|
||||
verify.currentParameterSpanIs("a: T");
|
||||
verify.currentParameterSpanIs("a: any");
|
||||
|
||||
goTo.marker("4");
|
||||
verify.currentParameterHelpArgumentNameIs("M");
|
||||
|
|
21
tests/cases/fourslash/signatureHelpExplicitTypeArguments.ts
Normal file
21
tests/cases/fourslash/signatureHelpExplicitTypeArguments.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////declare function f<T = boolean, U = string>(x: T, y: U): T;
|
||||
////f<number, string>(/*1*/);
|
||||
////f(/*2*/);
|
||||
////f<number>(/*3*/);
|
||||
////f<number, string, boolean>(/*4*/);
|
||||
|
||||
goTo.marker("1");
|
||||
verify.currentSignatureHelpIs("f(x: number, y: string): number");
|
||||
|
||||
goTo.marker("2");
|
||||
verify.currentSignatureHelpIs("f<T = boolean, U = string>(x: T, y: U): T");
|
||||
|
||||
goTo.marker("3");
|
||||
// too few -- fill in rest with {}
|
||||
verify.currentSignatureHelpIs("f(x: number, y: {}): number");
|
||||
|
||||
goTo.marker("4");
|
||||
// too many -- ignore extra type arguments
|
||||
verify.currentSignatureHelpIs("f(x: number, y: string): number");
|
|
@ -5,4 +5,4 @@
|
|||
////foo(/*1*/
|
||||
|
||||
goTo.marker('1');
|
||||
verify.currentSignatureHelpIs("foo<T>(x: number, callback: (x: T) => number): void");
|
||||
verify.currentSignatureHelpIs("foo(x: number, callback: (x: {}) => number): void");
|
|
@ -17,6 +17,6 @@ edit.insert('a');
|
|||
verify.signatureHelpCountIs(2);
|
||||
// verify.currentSignatureHelpIs('B(v: A<number>): A<number>')
|
||||
edit.insert('); A.B(');
|
||||
verify.currentSignatureHelpIs('B<S>(v: A<S>): A<S>');
|
||||
verify.currentSignatureHelpIs('B(v: A<{}>): A<{}>');
|
||||
edit.insert('a');
|
||||
// verify.currentSignatureHelpIs('B(v: A<number>): A<number>')
|
||||
|
|
Loading…
Reference in a new issue