Skip to content

Commit 66e9aaa

Browse files
rFloriansandersn
authored andcommitted
Issue 19220 function parameter arity (#24031)
* Added reference test case and diagnostics message * Adjusted arity checks to account for non-contiguous overloads * Code cleanup, baseline not yet commited * Accepted test baselines and minor implementation changes * Cleaned up baseline tracking the now renamed arity check test * Add range response when range contains only 2 values * Added recent baseline * Refined arity error messages when available overloads can be grouped * Rolled back code formatting * WIP cleanup needed in a few edge cases * Finished adding new more descriptive error messages * Code cleanup * Added simplified version of bugfix for #19220 * Rebased onto master * Removed whitespace after type assertion * Code review simplifications * Use correct diagnostic name * Code review changes and simplification of diagnostic message * Revert formatting changes
1 parent 5f4a03c commit 66e9aaa

7 files changed

+249
-10
lines changed

src/compiler/checker.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19052,24 +19052,40 @@ namespace ts {
1905219052
else if (args) {
1905319053
let min = Number.POSITIVE_INFINITY;
1905419054
let max = Number.NEGATIVE_INFINITY;
19055+
let belowArgCount = Number.NEGATIVE_INFINITY;
19056+
let aboveArgCount = Number.POSITIVE_INFINITY;
19057+
19058+
let argCount = args.length;
1905519059
for (const sig of signatures) {
19056-
min = Math.min(min, getMinArgumentCount(sig));
19057-
max = Math.max(max, getParameterCount(sig));
19060+
const minCount = getMinArgumentCount(sig);
19061+
const maxCount = getParameterCount(sig);
19062+
if (minCount < argCount && minCount > belowArgCount) belowArgCount = minCount;
19063+
if (argCount < maxCount && maxCount < aboveArgCount) aboveArgCount = maxCount;
19064+
min = Math.min(min, minCount);
19065+
max = Math.max(max, maxCount);
1905819066
}
19067+
1905919068
const hasRestParameter = some(signatures, hasEffectiveRestParameter);
19060-
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
19061-
const paramCount = hasRestParameter ? min :
19069+
const paramRange = hasRestParameter ? min :
1906219070
min < max ? min + "-" + max :
1906319071
min;
19064-
let argCount = args.length;
19072+
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
1906519073
if (argCount <= max && hasSpreadArgument) {
1906619074
argCount--;
1906719075
}
19068-
const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more :
19069-
hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 :
19070-
hasSpreadArgument ? Diagnostics.Expected_0_arguments_but_got_1_or_more :
19071-
Diagnostics.Expected_0_arguments_but_got_1;
19072-
diagnostics.add(createDiagnosticForNode(node, error, paramCount, argCount));
19076+
19077+
if (hasRestParameter || hasSpreadArgument) {
19078+
const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_1_or_more :
19079+
hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 :
19080+
Diagnostics.Expected_0_arguments_but_got_1_or_more;
19081+
diagnostics.add(createDiagnosticForNode(node, error, paramRange, argCount));
19082+
}
19083+
else if (min < argCount && argCount < max) {
19084+
diagnostics.add(createDiagnosticForNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount));
19085+
}
19086+
else {
19087+
diagnostics.add(createDiagnosticForNode(node, Diagnostics.Expected_0_arguments_but_got_1, paramRange, argCount));
19088+
}
1907319089
}
1907419090
else if (fallbackError) {
1907519091
diagnostics.add(createDiagnosticForNode(node, fallbackError));

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2056,6 +2056,10 @@
20562056
"category": "Error",
20572057
"code": 2574
20582058
},
2059+
"No overload expects {0} arguments, but overloads do exist that expect either {1} or {2} arguments.": {
2060+
"category": "Error",
2061+
"code": 2575
2062+
},
20592063
"JSX element attributes type '{0}' may not be a union type.": {
20602064
"category": "Error",
20612065
"code": 2600
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
tests/cases/compiler/functionParameterArityMismatch.ts(3,1): error TS2554: Expected 1-3 arguments, but got 0.
2+
tests/cases/compiler/functionParameterArityMismatch.ts(4,1): error TS2575: No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
3+
tests/cases/compiler/functionParameterArityMismatch.ts(5,1): error TS2554: Expected 1-3 arguments, but got 4.
4+
tests/cases/compiler/functionParameterArityMismatch.ts(11,1): error TS2575: No overload expects 1 arguments, but overloads do exist that expect either 0 or 2 arguments.
5+
tests/cases/compiler/functionParameterArityMismatch.ts(12,1): error TS2575: No overload expects 3 arguments, but overloads do exist that expect either 2 or 4 arguments.
6+
tests/cases/compiler/functionParameterArityMismatch.ts(13,1): error TS2575: No overload expects 5 arguments, but overloads do exist that expect either 4 or 6 arguments.
7+
tests/cases/compiler/functionParameterArityMismatch.ts(14,1): error TS2554: Expected 0-6 arguments, but got 7.
8+
9+
10+
==== tests/cases/compiler/functionParameterArityMismatch.ts (7 errors) ====
11+
declare function f1(a: number);
12+
declare function f1(a: number, b: number, c: number);
13+
f1();
14+
~~~~
15+
!!! error TS2554: Expected 1-3 arguments, but got 0.
16+
f1(1, 2);
17+
~~~~~~~~
18+
!!! error TS2575: No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
19+
f1(1, 2, 3, 4);
20+
~~~~~~~~~~~~~~
21+
!!! error TS2554: Expected 1-3 arguments, but got 4.
22+
23+
declare function f2();
24+
declare function f2(a: number, b: number);
25+
declare function f2(a: number, b: number, c: number, d: number);
26+
declare function f2(a: number, b: number, c: number, d: number, e: number, f: number);
27+
f2(1);
28+
~~~~~
29+
!!! error TS2575: No overload expects 1 arguments, but overloads do exist that expect either 0 or 2 arguments.
30+
f2(1, 2, 3);
31+
~~~~~~~~~~~
32+
!!! error TS2575: No overload expects 3 arguments, but overloads do exist that expect either 2 or 4 arguments.
33+
f2(1, 2, 3, 4, 5);
34+
~~~~~~~~~~~~~~~~~
35+
!!! error TS2575: No overload expects 5 arguments, but overloads do exist that expect either 4 or 6 arguments.
36+
f2(1, 2, 3, 4, 5, 6, 7);
37+
~~~~~~~~~~~~~~~~~~~~~~~
38+
!!! error TS2554: Expected 0-6 arguments, but got 7.
39+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [functionParameterArityMismatch.ts]
2+
declare function f1(a: number);
3+
declare function f1(a: number, b: number, c: number);
4+
f1();
5+
f1(1, 2);
6+
f1(1, 2, 3, 4);
7+
8+
declare function f2();
9+
declare function f2(a: number, b: number);
10+
declare function f2(a: number, b: number, c: number, d: number);
11+
declare function f2(a: number, b: number, c: number, d: number, e: number, f: number);
12+
f2(1);
13+
f2(1, 2, 3);
14+
f2(1, 2, 3, 4, 5);
15+
f2(1, 2, 3, 4, 5, 6, 7);
16+
17+
18+
//// [functionParameterArityMismatch.js]
19+
f1();
20+
f1(1, 2);
21+
f1(1, 2, 3, 4);
22+
f2(1);
23+
f2(1, 2, 3);
24+
f2(1, 2, 3, 4, 5);
25+
f2(1, 2, 3, 4, 5, 6, 7);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
=== tests/cases/compiler/functionParameterArityMismatch.ts ===
2+
declare function f1(a: number);
3+
>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31))
4+
>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 0, 20))
5+
6+
declare function f1(a: number, b: number, c: number);
7+
>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31))
8+
>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 1, 20))
9+
>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 1, 30))
10+
>c : Symbol(c, Decl(functionParameterArityMismatch.ts, 1, 41))
11+
12+
f1();
13+
>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31))
14+
15+
f1(1, 2);
16+
>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31))
17+
18+
f1(1, 2, 3, 4);
19+
>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31))
20+
21+
declare function f2();
22+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
23+
24+
declare function f2(a: number, b: number);
25+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
26+
>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 7, 20))
27+
>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 7, 30))
28+
29+
declare function f2(a: number, b: number, c: number, d: number);
30+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
31+
>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 8, 20))
32+
>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 8, 30))
33+
>c : Symbol(c, Decl(functionParameterArityMismatch.ts, 8, 41))
34+
>d : Symbol(d, Decl(functionParameterArityMismatch.ts, 8, 52))
35+
36+
declare function f2(a: number, b: number, c: number, d: number, e: number, f: number);
37+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
38+
>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 9, 20))
39+
>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 9, 30))
40+
>c : Symbol(c, Decl(functionParameterArityMismatch.ts, 9, 41))
41+
>d : Symbol(d, Decl(functionParameterArityMismatch.ts, 9, 52))
42+
>e : Symbol(e, Decl(functionParameterArityMismatch.ts, 9, 63))
43+
>f : Symbol(f, Decl(functionParameterArityMismatch.ts, 9, 74))
44+
45+
f2(1);
46+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
47+
48+
f2(1, 2, 3);
49+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
50+
51+
f2(1, 2, 3, 4, 5);
52+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
53+
54+
f2(1, 2, 3, 4, 5, 6, 7);
55+
>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64))
56+
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
=== tests/cases/compiler/functionParameterArityMismatch.ts ===
2+
declare function f1(a: number);
3+
>f1 : { (a: number): any; (a: number, b: number, c: number): any; }
4+
>a : number
5+
6+
declare function f1(a: number, b: number, c: number);
7+
>f1 : { (a: number): any; (a: number, b: number, c: number): any; }
8+
>a : number
9+
>b : number
10+
>c : number
11+
12+
f1();
13+
>f1() : any
14+
>f1 : { (a: number): any; (a: number, b: number, c: number): any; }
15+
16+
f1(1, 2);
17+
>f1(1, 2) : any
18+
>f1 : { (a: number): any; (a: number, b: number, c: number): any; }
19+
>1 : 1
20+
>2 : 2
21+
22+
f1(1, 2, 3, 4);
23+
>f1(1, 2, 3, 4) : any
24+
>f1 : { (a: number): any; (a: number, b: number, c: number): any; }
25+
>1 : 1
26+
>2 : 2
27+
>3 : 3
28+
>4 : 4
29+
30+
declare function f2();
31+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
32+
33+
declare function f2(a: number, b: number);
34+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
35+
>a : number
36+
>b : number
37+
38+
declare function f2(a: number, b: number, c: number, d: number);
39+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
40+
>a : number
41+
>b : number
42+
>c : number
43+
>d : number
44+
45+
declare function f2(a: number, b: number, c: number, d: number, e: number, f: number);
46+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
47+
>a : number
48+
>b : number
49+
>c : number
50+
>d : number
51+
>e : number
52+
>f : number
53+
54+
f2(1);
55+
>f2(1) : any
56+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
57+
>1 : 1
58+
59+
f2(1, 2, 3);
60+
>f2(1, 2, 3) : any
61+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
62+
>1 : 1
63+
>2 : 2
64+
>3 : 3
65+
66+
f2(1, 2, 3, 4, 5);
67+
>f2(1, 2, 3, 4, 5) : any
68+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
69+
>1 : 1
70+
>2 : 2
71+
>3 : 3
72+
>4 : 4
73+
>5 : 5
74+
75+
f2(1, 2, 3, 4, 5, 6, 7);
76+
>f2(1, 2, 3, 4, 5, 6, 7) : any
77+
>f2 : { (): any; (a: number, b: number): any; (a: number, b: number, c: number, d: number): any; (a: number, b: number, c: number, d: number, e: number, f: number): any; }
78+
>1 : 1
79+
>2 : 2
80+
>3 : 3
81+
>4 : 4
82+
>5 : 5
83+
>6 : 6
84+
>7 : 7
85+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
declare function f1(a: number);
2+
declare function f1(a: number, b: number, c: number);
3+
f1();
4+
f1(1, 2);
5+
f1(1, 2, 3, 4);
6+
7+
declare function f2();
8+
declare function f2(a: number, b: number);
9+
declare function f2(a: number, b: number, c: number, d: number);
10+
declare function f2(a: number, b: number, c: number, d: number, e: number, f: number);
11+
f2(1);
12+
f2(1, 2, 3);
13+
f2(1, 2, 3, 4, 5);
14+
f2(1, 2, 3, 4, 5, 6, 7);

0 commit comments

Comments
 (0)