mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 21:36:50 -05:00
Merge pull request #25608 from Microsoft/genericRestArityCheck
Fix generic rest parameter arity checks
This commit is contained in:
@@ -18215,7 +18215,6 @@ namespace ts {
|
||||
|
||||
function hasCorrectArity(node: CallLikeExpression, args: ReadonlyArray<Expression>, signature: Signature, signatureHelpTrailingComma = false) {
|
||||
let argCount: number; // Apparent number of arguments we will have in this call
|
||||
let typeArguments: NodeArray<TypeNode> | undefined; // Type arguments (undefined if none)
|
||||
let callIsIncomplete = false; // In incomplete call we want to be lenient when we have too few arguments
|
||||
let spreadArgIndex = -1;
|
||||
|
||||
@@ -18228,7 +18227,6 @@ namespace ts {
|
||||
// Even if the call is incomplete, we'll have a missing expression as our last argument,
|
||||
// so we can say the count is just the arg list length
|
||||
argCount = args.length;
|
||||
typeArguments = node.typeArguments;
|
||||
|
||||
if (node.template.kind === SyntaxKind.TemplateExpression) {
|
||||
// If a tagged template expression lacks a tail literal, the call is incomplete.
|
||||
@@ -18246,7 +18244,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.Decorator) {
|
||||
typeArguments = undefined;
|
||||
argCount = getEffectiveArgumentCount(node, /*args*/ undefined!, signature);
|
||||
}
|
||||
else {
|
||||
@@ -18261,14 +18258,9 @@ namespace ts {
|
||||
// If we are missing the close parenthesis, the call is incomplete.
|
||||
callIsIncomplete = node.arguments.end === node.end;
|
||||
|
||||
typeArguments = node.typeArguments;
|
||||
spreadArgIndex = getSpreadArgumentIndex(args);
|
||||
}
|
||||
|
||||
if (!hasCorrectTypeArgumentArity(signature, typeArguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range.
|
||||
if (spreadArgIndex >= 0) {
|
||||
return spreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || spreadArgIndex < getParameterCount(signature));
|
||||
@@ -18918,6 +18910,43 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getArgumentArityError(node: Node, signatures: ReadonlyArray<Signature>, args: ReadonlyArray<Expression>) {
|
||||
let min = Number.POSITIVE_INFINITY;
|
||||
let max = Number.NEGATIVE_INFINITY;
|
||||
let belowArgCount = Number.NEGATIVE_INFINITY;
|
||||
let aboveArgCount = Number.POSITIVE_INFINITY;
|
||||
|
||||
let argCount = args.length;
|
||||
for (const sig of signatures) {
|
||||
const minCount = getMinArgumentCount(sig);
|
||||
const maxCount = getParameterCount(sig);
|
||||
if (minCount < argCount && minCount > belowArgCount) belowArgCount = minCount;
|
||||
if (argCount < maxCount && maxCount < aboveArgCount) aboveArgCount = maxCount;
|
||||
min = Math.min(min, minCount);
|
||||
max = Math.max(max, maxCount);
|
||||
}
|
||||
|
||||
const hasRestParameter = some(signatures, hasEffectiveRestParameter);
|
||||
const paramRange = hasRestParameter ? min :
|
||||
min < max ? min + "-" + max :
|
||||
min;
|
||||
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
|
||||
if (argCount <= max && hasSpreadArgument) {
|
||||
argCount--;
|
||||
}
|
||||
|
||||
if (hasRestParameter || hasSpreadArgument) {
|
||||
const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more :
|
||||
hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 :
|
||||
Diagnostics.Expected_0_arguments_but_got_1_or_more;
|
||||
return createDiagnosticForNode(node, error, paramRange, argCount);
|
||||
}
|
||||
if (min < argCount && argCount < max) {
|
||||
return createDiagnosticForNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount);
|
||||
}
|
||||
return createDiagnosticForNode(node, Diagnostics.Expected_0_arguments_but_got_1, paramRange, argCount);
|
||||
}
|
||||
|
||||
function getTypeArgumentArityError(node: Node, signatures: ReadonlyArray<Signature>, typeArguments: NodeArray<TypeNode>) {
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
@@ -19008,6 +19037,7 @@ namespace ts {
|
||||
// foo<number>(0);
|
||||
//
|
||||
let candidateForArgumentError: Signature | undefined;
|
||||
let candidateForArgumentArityError: Signature | undefined;
|
||||
let candidateForTypeArgumentError: Signature | undefined;
|
||||
let result: Signature | undefined;
|
||||
|
||||
@@ -19052,6 +19082,9 @@ namespace ts {
|
||||
// an error, we don't need to exclude any arguments, although it would cause no harm to do so.
|
||||
checkApplicableSignature(node, args!, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
|
||||
}
|
||||
else if (candidateForArgumentArityError) {
|
||||
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args!));
|
||||
}
|
||||
else if (candidateForTypeArgumentError) {
|
||||
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression).typeArguments!, /*reportErrors*/ true, fallbackError);
|
||||
}
|
||||
@@ -19059,42 +19092,7 @@ namespace ts {
|
||||
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments));
|
||||
}
|
||||
else if (args) {
|
||||
let min = Number.POSITIVE_INFINITY;
|
||||
let max = Number.NEGATIVE_INFINITY;
|
||||
let belowArgCount = Number.NEGATIVE_INFINITY;
|
||||
let aboveArgCount = Number.POSITIVE_INFINITY;
|
||||
|
||||
let argCount = args.length;
|
||||
for (const sig of signatures) {
|
||||
const minCount = getMinArgumentCount(sig);
|
||||
const maxCount = getParameterCount(sig);
|
||||
if (minCount < argCount && minCount > belowArgCount) belowArgCount = minCount;
|
||||
if (argCount < maxCount && maxCount < aboveArgCount) aboveArgCount = maxCount;
|
||||
min = Math.min(min, minCount);
|
||||
max = Math.max(max, maxCount);
|
||||
}
|
||||
|
||||
const hasRestParameter = some(signatures, hasEffectiveRestParameter);
|
||||
const paramRange = hasRestParameter ? min :
|
||||
min < max ? min + "-" + max :
|
||||
min;
|
||||
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
|
||||
if (argCount <= max && hasSpreadArgument) {
|
||||
argCount--;
|
||||
}
|
||||
|
||||
if (hasRestParameter || hasSpreadArgument) {
|
||||
const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more :
|
||||
hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 :
|
||||
Diagnostics.Expected_0_arguments_but_got_1_or_more;
|
||||
diagnostics.add(createDiagnosticForNode(node, error, paramRange, argCount));
|
||||
}
|
||||
else if (min < argCount && argCount < max) {
|
||||
diagnostics.add(createDiagnosticForNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount));
|
||||
}
|
||||
else {
|
||||
diagnostics.add(createDiagnosticForNode(node, Diagnostics.Expected_0_arguments_but_got_1, paramRange, argCount));
|
||||
}
|
||||
diagnostics.add(getArgumentArityError(node, signatures, args));
|
||||
}
|
||||
else if (fallbackError) {
|
||||
diagnostics.add(createDiagnosticForNode(node, fallbackError));
|
||||
@@ -19104,11 +19102,12 @@ namespace ts {
|
||||
|
||||
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
|
||||
candidateForArgumentError = undefined;
|
||||
candidateForArgumentArityError = undefined;
|
||||
candidateForTypeArgumentError = undefined;
|
||||
|
||||
if (isSingleNonGenericCandidate) {
|
||||
const candidate = candidates[0];
|
||||
if (!hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
|
||||
if (typeArguments || !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!checkApplicableSignature(node, args!, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
|
||||
@@ -19120,7 +19119,7 @@ namespace ts {
|
||||
|
||||
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
|
||||
const originalCandidate = candidates[candidateIndex];
|
||||
if (!hasCorrectArity(node, args!, originalCandidate, signatureHelpTrailingComma)) {
|
||||
if (!hasCorrectTypeArgumentArity(originalCandidate, typeArguments) || !hasCorrectArity(node, args!, originalCandidate, signatureHelpTrailingComma)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -19148,6 +19147,12 @@ namespace ts {
|
||||
}
|
||||
const isJavascript = isInJavaScriptFile(candidate.declaration);
|
||||
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
|
||||
// If the original signature has a rest type parameter, instantiation may produce a
|
||||
// signature with different arity and we need to perform another arity check.
|
||||
if (getRestTypeParameter(originalCandidate) && !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
|
||||
candidateForArgumentArityError = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!checkApplicableSignature(node, args!, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
|
||||
candidateForArgumentError = candidate;
|
||||
|
||||
Reference in New Issue
Block a user