Skip to content

Commit 87ad612

Browse files
committed
Additional arity check following instantiation of generic rest parameter
1 parent 8a559e4 commit 87ad612

File tree

1 file changed

+37
-30
lines changed

1 file changed

+37
-30
lines changed

src/compiler/checker.ts

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18200,7 +18200,6 @@ namespace ts {
1820018200

1820118201
function hasCorrectArity(node: CallLikeExpression, args: ReadonlyArray<Expression>, signature: Signature, signatureHelpTrailingComma = false) {
1820218202
let argCount: number; // Apparent number of arguments we will have in this call
18203-
let typeArguments: NodeArray<TypeNode> | undefined; // Type arguments (undefined if none)
1820418203
let callIsIncomplete = false; // In incomplete call we want to be lenient when we have too few arguments
1820518204
let spreadArgIndex = -1;
1820618205

@@ -18213,7 +18212,6 @@ namespace ts {
1821318212
// Even if the call is incomplete, we'll have a missing expression as our last argument,
1821418213
// so we can say the count is just the arg list length
1821518214
argCount = args.length;
18216-
typeArguments = node.typeArguments;
1821718215

1821818216
if (node.template.kind === SyntaxKind.TemplateExpression) {
1821918217
// If a tagged template expression lacks a tail literal, the call is incomplete.
@@ -18231,7 +18229,6 @@ namespace ts {
1823118229
}
1823218230
}
1823318231
else if (node.kind === SyntaxKind.Decorator) {
18234-
typeArguments = undefined;
1823518232
argCount = getEffectiveArgumentCount(node, /*args*/ undefined!, signature);
1823618233
}
1823718234
else {
@@ -18246,14 +18243,9 @@ namespace ts {
1824618243
// If we are missing the close parenthesis, the call is incomplete.
1824718244
callIsIncomplete = node.arguments.end === node.end;
1824818245

18249-
typeArguments = node.typeArguments;
1825018246
spreadArgIndex = getSpreadArgumentIndex(args);
1825118247
}
1825218248

18253-
if (!hasCorrectTypeArgumentArity(signature, typeArguments)) {
18254-
return false;
18255-
}
18256-
1825718249
// If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range.
1825818250
if (spreadArgIndex >= 0) {
1825918251
return spreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || spreadArgIndex < getParameterCount(signature));
@@ -18903,6 +18895,29 @@ namespace ts {
1890318895
}
1890418896
}
1890518897

18898+
function getArgumentArityError(node: Node, signatures: ReadonlyArray<Signature>, args: ReadonlyArray<Expression>) {
18899+
let min = Number.POSITIVE_INFINITY;
18900+
let max = Number.NEGATIVE_INFINITY;
18901+
for (const sig of signatures) {
18902+
min = Math.min(min, getMinArgumentCount(sig));
18903+
max = Math.max(max, getParameterCount(sig));
18904+
}
18905+
const hasRestParameter = some(signatures, hasEffectiveRestParameter);
18906+
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
18907+
const paramCount = hasRestParameter ? min :
18908+
min < max ? min + "-" + max :
18909+
min;
18910+
let argCount = args.length;
18911+
if (argCount <= max && hasSpreadArgument) {
18912+
argCount--;
18913+
}
18914+
const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more :
18915+
hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 :
18916+
hasSpreadArgument ? Diagnostics.Expected_0_arguments_but_got_1_or_more :
18917+
Diagnostics.Expected_0_arguments_but_got_1;
18918+
return createDiagnosticForNode(node, error, paramCount, argCount);
18919+
}
18920+
1890618921
function getTypeArgumentArityError(node: Node, signatures: ReadonlyArray<Signature>, typeArguments: NodeArray<TypeNode>) {
1890718922
let min = Infinity;
1890818923
let max = -Infinity;
@@ -18993,6 +19008,7 @@ namespace ts {
1899319008
// foo<number>(0);
1899419009
//
1899519010
let candidateForArgumentError: Signature | undefined;
19011+
let candidateForArgumentArityError: Signature | undefined;
1899619012
let candidateForTypeArgumentError: Signature | undefined;
1899719013
let result: Signature | undefined;
1899819014

@@ -19037,33 +19053,17 @@ namespace ts {
1903719053
// an error, we don't need to exclude any arguments, although it would cause no harm to do so.
1903819054
checkApplicableSignature(node, args!, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
1903919055
}
19056+
else if (candidateForArgumentArityError) {
19057+
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args!));
19058+
}
1904019059
else if (candidateForTypeArgumentError) {
1904119060
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression).typeArguments!, /*reportErrors*/ true, fallbackError);
1904219061
}
1904319062
else if (typeArguments && every(signatures, sig => length(sig.typeParameters) !== typeArguments!.length)) {
1904419063
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments));
1904519064
}
1904619065
else if (args) {
19047-
let min = Number.POSITIVE_INFINITY;
19048-
let max = Number.NEGATIVE_INFINITY;
19049-
for (const sig of signatures) {
19050-
min = Math.min(min, getMinArgumentCount(sig));
19051-
max = Math.max(max, getParameterCount(sig));
19052-
}
19053-
const hasRestParameter = some(signatures, hasEffectiveRestParameter);
19054-
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
19055-
const paramCount = hasRestParameter ? min :
19056-
min < max ? min + "-" + max :
19057-
min;
19058-
let argCount = args.length;
19059-
if (argCount <= max && hasSpreadArgument) {
19060-
argCount--;
19061-
}
19062-
const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more :
19063-
hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 :
19064-
hasSpreadArgument ? Diagnostics.Expected_0_arguments_but_got_1_or_more :
19065-
Diagnostics.Expected_0_arguments_but_got_1;
19066-
diagnostics.add(createDiagnosticForNode(node, error, paramCount, argCount));
19066+
diagnostics.add(getArgumentArityError(node, signatures, args));
1906719067
}
1906819068
else if (fallbackError) {
1906919069
diagnostics.add(createDiagnosticForNode(node, fallbackError));
@@ -19104,11 +19104,12 @@ namespace ts {
1910419104

1910519105
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
1910619106
candidateForArgumentError = undefined;
19107+
candidateForArgumentArityError = undefined;
1910719108
candidateForTypeArgumentError = undefined;
1910819109

1910919110
if (isSingleNonGenericCandidate) {
1911019111
const candidate = candidates[0];
19111-
if (!hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
19112+
if (typeArguments || !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
1911219113
return undefined;
1911319114
}
1911419115
if (!checkApplicableSignature(node, args!, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
@@ -19120,7 +19121,7 @@ namespace ts {
1912019121

1912119122
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
1912219123
const originalCandidate = candidates[candidateIndex];
19123-
if (!hasCorrectArity(node, args!, originalCandidate, signatureHelpTrailingComma)) {
19124+
if (!hasCorrectTypeArgumentArity(originalCandidate, typeArguments) || !hasCorrectArity(node, args!, originalCandidate, signatureHelpTrailingComma)) {
1912419125
continue;
1912519126
}
1912619127

@@ -19148,6 +19149,12 @@ namespace ts {
1914819149
}
1914919150
const isJavascript = isInJavaScriptFile(candidate.declaration);
1915019151
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
19152+
// If the original signature has a rest type parameter, instantiation may produce a
19153+
// signature with different arity and we need to perform another arity check.
19154+
if (getRestTypeParameter(originalCandidate) && !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
19155+
candidateForArgumentArityError = candidate;
19156+
break;
19157+
}
1915119158
}
1915219159
if (!checkApplicableSignature(node, args!, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
1915319160
candidateForArgumentError = candidate;

0 commit comments

Comments
 (0)