Skip to content

Commit 004b3ae

Browse files
authored
Simplify arity errors, rewording spread errors (#43855)
* Scribbles + tests The second test actually requires node types * Basically working The two simple fixes, in arity error reporting, are in, and the simplification of arity error reporting is half-done. I haven't started on any improvements to call assignability. * trim out too-real test case * Finish cleanup And reword error a little. * Simplify and reword spread errors * handle spreads first * update baselines * Address PR comments
1 parent 4ecb563 commit 004b3ae

26 files changed

+523
-136
lines changed

src/compiler/checker.ts

Lines changed: 49 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -28613,88 +28613,67 @@ namespace ts {
2861328613
}
2861428614

2861528615
function getArgumentArityError(node: CallLikeExpression, signatures: readonly Signature[], args: readonly Expression[]) {
28616-
let min = Number.POSITIVE_INFINITY;
28617-
let max = Number.NEGATIVE_INFINITY;
28618-
let belowArgCount = Number.NEGATIVE_INFINITY;
28619-
let aboveArgCount = Number.POSITIVE_INFINITY;
28616+
const spreadIndex = getSpreadArgumentIndex(args);
28617+
if (spreadIndex > -1) {
28618+
return createDiagnosticForNode(args[spreadIndex], Diagnostics.A_spread_argument_must_either_have_a_tuple_type_or_be_passed_to_a_rest_parameter);
28619+
}
28620+
let min = Number.POSITIVE_INFINITY; // smallest parameter count
28621+
let max = Number.NEGATIVE_INFINITY; // largest parameter count
28622+
let maxBelow = Number.NEGATIVE_INFINITY; // largest parameter count that is smaller than the number of arguments
28623+
let minAbove = Number.POSITIVE_INFINITY; // smallest parameter count that is larger than the number of arguments
2862028624

28621-
let argCount = args.length;
2862228625
let closestSignature: Signature | undefined;
2862328626
for (const sig of signatures) {
28624-
const minCount = getMinArgumentCount(sig);
28625-
const maxCount = getParameterCount(sig);
28626-
if (minCount < argCount && minCount > belowArgCount) belowArgCount = minCount;
28627-
if (argCount < maxCount && maxCount < aboveArgCount) aboveArgCount = maxCount;
28628-
if (minCount < min) {
28629-
min = minCount;
28627+
const minParameter = getMinArgumentCount(sig);
28628+
const maxParameter = getParameterCount(sig);
28629+
// smallest/largest parameter counts
28630+
if (minParameter < min) {
28631+
min = minParameter;
2863028632
closestSignature = sig;
2863128633
}
28632-
max = Math.max(max, maxCount);
28633-
}
28634-
28635-
if (min < argCount && argCount < max) {
28636-
return getDiagnosticForCallNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount);
28634+
max = Math.max(max, maxParameter);
28635+
// shortest parameter count *longer than the call*/longest parameter count *shorter than the call*
28636+
if (minParameter < args.length && minParameter > maxBelow) maxBelow = minParameter;
28637+
if (args.length < maxParameter && maxParameter < minAbove) minAbove = maxParameter;
2863728638
}
28638-
2863928639
const hasRestParameter = some(signatures, hasEffectiveRestParameter);
28640-
const paramRange = hasRestParameter ? min :
28641-
min < max ? min + "-" + max :
28642-
min;
28643-
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
28644-
if (argCount <= max && hasSpreadArgument) {
28645-
argCount--;
28646-
}
28647-
28648-
let spanArray: NodeArray<Node>;
28649-
let related: DiagnosticWithLocation | undefined;
28650-
28651-
const error = hasRestParameter || hasSpreadArgument ?
28652-
hasRestParameter && hasSpreadArgument ?
28653-
Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more :
28654-
hasRestParameter ?
28655-
Diagnostics.Expected_at_least_0_arguments_but_got_1 :
28656-
Diagnostics.Expected_0_arguments_but_got_1_or_more :
28657-
paramRange === 1 && argCount === 0 && isPromiseResolveArityError(node) ?
28658-
Diagnostics.Expected_0_arguments_but_got_1_Did_you_forget_to_include_void_in_your_type_argument_to_Promise :
28659-
Diagnostics.Expected_0_arguments_but_got_1;
28660-
28661-
if (closestSignature && getMinArgumentCount(closestSignature) > argCount && closestSignature.declaration) {
28662-
const paramDecl = closestSignature.declaration.parameters[closestSignature.thisParameter ? argCount + 1 : argCount];
28663-
if (paramDecl) {
28664-
related = createDiagnosticForNode(
28665-
paramDecl,
28666-
isBindingPattern(paramDecl.name) ? Diagnostics.An_argument_matching_this_binding_pattern_was_not_provided :
28667-
isRestParameter(paramDecl) ? Diagnostics.Arguments_for_the_rest_parameter_0_were_not_provided : Diagnostics.An_argument_for_0_was_not_provided,
28668-
!paramDecl.name ? argCount : !isBindingPattern(paramDecl.name) ? idText(getFirstIdentifier(paramDecl.name)) : undefined
28640+
const parameterRange = hasRestParameter ? min
28641+
: min < max ? min + "-" + max
28642+
: min;
28643+
const error = hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1
28644+
: parameterRange === 1 && args.length === 0 && isPromiseResolveArityError(node) ? Diagnostics.Expected_0_arguments_but_got_1_Did_you_forget_to_include_void_in_your_type_argument_to_Promise
28645+
: Diagnostics.Expected_0_arguments_but_got_1;
28646+
if (min < args.length && args.length < max) {
28647+
// between min and max, but with no matching overload
28648+
return getDiagnosticForCallNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, args.length, maxBelow, minAbove);
28649+
}
28650+
else if (args.length < min) {
28651+
// too short: put the error span on the call expression, not any of the args
28652+
const diagnostic = getDiagnosticForCallNode(node, error, parameterRange, args.length);
28653+
const parameter = closestSignature?.declaration?.parameters[closestSignature.thisParameter ? args.length + 1 : args.length];
28654+
if (parameter) {
28655+
const parameterError = createDiagnosticForNode(
28656+
parameter,
28657+
isBindingPattern(parameter.name) ? Diagnostics.An_argument_matching_this_binding_pattern_was_not_provided
28658+
: isRestParameter(parameter) ? Diagnostics.Arguments_for_the_rest_parameter_0_were_not_provided
28659+
: Diagnostics.An_argument_for_0_was_not_provided,
28660+
!parameter.name ? args.length : !isBindingPattern(parameter.name) ? idText(getFirstIdentifier(parameter.name)) : undefined
2866928661
);
28662+
return addRelatedInfo(diagnostic, parameterError);
2867028663
}
28671-
}
28672-
28673-
if (!hasSpreadArgument && argCount < min) {
28674-
const diagnostic = getDiagnosticForCallNode(node, error, paramRange, argCount);
28675-
return related ? addRelatedInfo(diagnostic, related) : diagnostic;
28676-
}
28677-
28678-
if (hasRestParameter || hasSpreadArgument) {
28679-
spanArray = factory.createNodeArray(args);
28680-
if (hasSpreadArgument && argCount) {
28681-
const nextArg = elementAt(args, getSpreadArgumentIndex(args) + 1) || undefined;
28682-
spanArray = factory.createNodeArray(args.slice(max > argCount && nextArg ? args.indexOf(nextArg) : Math.min(max, args.length - 1)));
28683-
}
28664+
return diagnostic;
2868428665
}
2868528666
else {
28686-
spanArray = factory.createNodeArray(args.slice(max));
28687-
}
28688-
28689-
const pos = first(spanArray).pos;
28690-
let end = last(spanArray).end;
28691-
if (end === pos) {
28692-
end++;
28667+
// too long; error goes on the excess parameters
28668+
const errorSpan = factory.createNodeArray(args.slice(max));
28669+
const pos = first(errorSpan).pos;
28670+
let end = last(errorSpan).end;
28671+
if (end === pos) {
28672+
end++;
28673+
}
28674+
setTextRangePosEnd(errorSpan, pos, end);
28675+
return createDiagnosticForNodeArray(getSourceFileOfNode(node), errorSpan, error, parameterRange, args.length);
2869328676
}
28694-
setTextRangePosEnd(spanArray, pos, end);
28695-
const diagnostic = createDiagnosticForNodeArray(
28696-
getSourceFileOfNode(node), spanArray, error, paramRange, argCount);
28697-
return related ? addRelatedInfo(diagnostic, related) : diagnostic;
2869828677
}
2869928678

2870028679
function getTypeArgumentArityError(node: Node, signatures: readonly Signature[], typeArguments: NodeArray<TypeNode>) {

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2386,14 +2386,10 @@
23862386
"category": "Error",
23872387
"code": 2555
23882388
},
2389-
"Expected {0} arguments, but got {1} or more.": {
2389+
"A spread argument must either have a tuple type or be passed to a rest parameter.": {
23902390
"category": "Error",
23912391
"code": 2556
23922392
},
2393-
"Expected at least {0} arguments, but got {1} or more.": {
2394-
"category": "Error",
2395-
"code": 2557
2396-
},
23972393
"Expected {0} type arguments, but got {1}.": {
23982394
"category": "Error",
23992395
"code": 2558

tests/baselines/reference/callOverload.errors.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
tests/cases/conformance/expressions/functionCalls/callOverload.ts(7,7): error TS2554: Expected 1 arguments, but got 4.
22
tests/cases/conformance/expressions/functionCalls/callOverload.ts(8,15): error TS2554: Expected 2 arguments, but got 4.
33
tests/cases/conformance/expressions/functionCalls/callOverload.ts(10,1): error TS2555: Expected at least 1 arguments, but got 0.
4-
tests/cases/conformance/expressions/functionCalls/callOverload.ts(11,10): error TS2557: Expected at least 1 arguments, but got 0 or more.
4+
tests/cases/conformance/expressions/functionCalls/callOverload.ts(11,10): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
55

66

77
==== tests/cases/conformance/expressions/functionCalls/callOverload.ts (4 errors) ====
@@ -24,5 +24,4 @@ tests/cases/conformance/expressions/functionCalls/callOverload.ts(11,10): error
2424
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callOverload.ts:3:27: An argument for 'a' was not provided.
2525
withRest(...n);
2626
~~~~
27-
!!! error TS2557: Expected at least 1 arguments, but got 0 or more.
28-
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callOverload.ts:3:27: An argument for 'a' was not provided.
27+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.

tests/baselines/reference/callWithSpread2.errors.txt

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(23,13): error TS2556: Expected 1 arguments, but got 2 or more.
2-
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(24,7): error TS2556: Expected 0 arguments, but got 1 or more.
1+
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(23,13): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
2+
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(24,7): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
33
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(27,5): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'.
44
Type 'string' is not assignable to type 'number'.
55
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(28,5): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
@@ -9,10 +9,10 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(30,13): err
99
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(31,11): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'.
1010
Type 'string' is not assignable to type 'number'.
1111
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(32,11): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
12-
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(33,8): error TS2556: Expected 1-3 arguments, but got 0 or more.
13-
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(34,8): error TS2556: Expected 1-3 arguments, but got 0 or more.
12+
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(33,8): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
13+
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(34,8): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
1414
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(35,8): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
15-
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(36,14): error TS2556: Expected 2-4 arguments, but got 1 or more.
15+
tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(36,14): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
1616

1717

1818
==== tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts (12 errors) ====
@@ -40,10 +40,10 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(36,14): err
4040
// extra arguments
4141
normal("g", ...ns)
4242
~~~~~
43-
!!! error TS2556: Expected 1 arguments, but got 2 or more.
43+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
4444
thunk(...ns)
4545
~~~~~
46-
!!! error TS2556: Expected 0 arguments, but got 1 or more.
46+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
4747

4848
// bad
4949
all(...mixed)
@@ -69,17 +69,14 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(36,14): err
6969
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
7070
prefix(...ns) // required parameters are required
7171
~~~~~
72-
!!! error TS2556: Expected 1-3 arguments, but got 0 or more.
73-
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts:3:25: An argument for 's' was not provided.
72+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
7473
prefix(...mixed)
7574
~~~~~~~~
76-
!!! error TS2556: Expected 1-3 arguments, but got 0 or more.
77-
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts:3:25: An argument for 's' was not provided.
75+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
7876
prefix(...tuple)
7977
~~~~~~~~
8078
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
8179
prefix2("g", ...ns);
8280
~~~~~
83-
!!! error TS2556: Expected 2-4 arguments, but got 1 or more.
84-
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts:7:37: An argument for 'n' was not provided.
81+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
8582

tests/baselines/reference/callWithSpread3.errors.txt

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(16,15): err
44
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(17,15): error TS2554: Expected 2 arguments, but got 6.
55
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(18,12): error TS2554: Expected 2 arguments, but got 3.
66
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(19,5): error TS2554: Expected 2 arguments, but got 3.
7-
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(20,6): error TS2557: Expected at least 2 arguments, but got 0 or more.
7+
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(20,6): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
88
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(21,6): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
9-
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(22,13): error TS2557: Expected at least 2 arguments, but got 1 or more.
10-
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(23,13): error TS2557: Expected at least 2 arguments, but got 2 or more.
9+
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(22,6): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
10+
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(23,6): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
1111
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(25,7): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
12-
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(31,13): error TS2557: Expected at least 2 arguments, but got 4 or more.
12+
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(31,6): error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
1313

1414

1515
==== tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts (12 errors) ====
@@ -46,18 +46,16 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(31,13): err
4646
!!! error TS2554: Expected 2 arguments, but got 3.
4747
fs2_(...s_); // error on ...s_
4848
~~~~~
49-
!!! error TS2557: Expected at least 2 arguments, but got 0 or more.
50-
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts:9:23: An argument for 'a' was not provided.
49+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
5150
fs2_(...s2n_); // error on ...s2n_
5251
~~~~~~~
5352
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
54-
fs2_(...s_, ...s_); // error FIXME: bad error message
55-
~~~~~
56-
!!! error TS2557: Expected at least 2 arguments, but got 1 or more.
57-
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts:9:34: An argument for 'b' was not provided.
58-
fs2_(...s_, ...s_, ...s_); // error FIXME: worse error message
59-
~~~~~~~~~~~~
60-
!!! error TS2557: Expected at least 2 arguments, but got 2 or more.
53+
fs2_(...s_, ...s_); // error on ...s_
54+
~~~~~
55+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
56+
fs2_(...s_, ...s_, ...s_); // error on ...s_
57+
~~~~~
58+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
6159
// fs2n_(...s2, ...s_); // FIXME: should be a type error
6260
fs2n_(...s2_); // error on ...s2_
6361
~~~~~~
@@ -68,8 +66,8 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(31,13): err
6866
fs2_(...s2_, ...s_);
6967
fs2_(...s2_, ...s2_);
7068
fs2_(...s_, ...s2_);
71-
~~~~~~
72-
!!! error TS2557: Expected at least 2 arguments, but got 4 or more.
69+
~~~~~
70+
!!! error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
7371
fs2n_(...s2n_);
7472
fs2n_(...s2);
7573
// fs2n_(...s2, ...n_); // FIXME: should compile

0 commit comments

Comments
 (0)