From 66e9aaac18de0a608f1f375cf30e449cfd4a503b Mon Sep 17 00:00:00 2001 From: rflorian Date: Thu, 12 Jul 2018 02:19:56 +0200 Subject: [PATCH] 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 --- src/compiler/checker.ts | 36 +++++--- src/compiler/diagnosticMessages.json | 4 + .../functionParameterArityMismatch.errors.txt | 39 +++++++++ .../functionParameterArityMismatch.js | 25 ++++++ .../functionParameterArityMismatch.symbols | 56 ++++++++++++ .../functionParameterArityMismatch.types | 85 +++++++++++++++++++ .../functionParameterArityMismatch.ts | 14 +++ 7 files changed, 249 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/functionParameterArityMismatch.errors.txt create mode 100644 tests/baselines/reference/functionParameterArityMismatch.js create mode 100644 tests/baselines/reference/functionParameterArityMismatch.symbols create mode 100644 tests/baselines/reference/functionParameterArityMismatch.types create mode 100644 tests/cases/compiler/functionParameterArityMismatch.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bb31d483b8..1e99d447e6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19052,24 +19052,40 @@ namespace ts { 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) { - min = Math.min(min, getMinArgumentCount(sig)); - max = Math.max(max, getParameterCount(sig)); + 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 hasSpreadArgument = getSpreadArgumentIndex(args) > -1; - const paramCount = hasRestParameter ? min : + const paramRange = hasRestParameter ? min : min < max ? min + "-" + max : min; - let argCount = args.length; + const hasSpreadArgument = getSpreadArgumentIndex(args) > -1; if (argCount <= max && hasSpreadArgument) { argCount--; } - 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 : - hasSpreadArgument ? Diagnostics.Expected_0_arguments_but_got_1_or_more : - Diagnostics.Expected_0_arguments_but_got_1; - diagnostics.add(createDiagnosticForNode(node, error, paramCount, 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)); + } } else if (fallbackError) { diagnostics.add(createDiagnosticForNode(node, fallbackError)); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 68eafc6b99..5150984d1f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2056,6 +2056,10 @@ "category": "Error", "code": 2574 }, + "No overload expects {0} arguments, but overloads do exist that expect either {1} or {2} arguments.": { + "category": "Error", + "code": 2575 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600 diff --git a/tests/baselines/reference/functionParameterArityMismatch.errors.txt b/tests/baselines/reference/functionParameterArityMismatch.errors.txt new file mode 100644 index 0000000000..07c9c8845c --- /dev/null +++ b/tests/baselines/reference/functionParameterArityMismatch.errors.txt @@ -0,0 +1,39 @@ +tests/cases/compiler/functionParameterArityMismatch.ts(3,1): error TS2554: Expected 1-3 arguments, but got 0. +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. +tests/cases/compiler/functionParameterArityMismatch.ts(5,1): error TS2554: Expected 1-3 arguments, but got 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. +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. +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. +tests/cases/compiler/functionParameterArityMismatch.ts(14,1): error TS2554: Expected 0-6 arguments, but got 7. + + +==== tests/cases/compiler/functionParameterArityMismatch.ts (7 errors) ==== + declare function f1(a: number); + declare function f1(a: number, b: number, c: number); + f1(); + ~~~~ +!!! error TS2554: Expected 1-3 arguments, but got 0. + f1(1, 2); + ~~~~~~~~ +!!! error TS2575: No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments. + f1(1, 2, 3, 4); + ~~~~~~~~~~~~~~ +!!! error TS2554: Expected 1-3 arguments, but got 4. + + declare function f2(); + declare function f2(a: number, b: number); + declare function f2(a: number, b: number, c: number, d: number); + declare function f2(a: number, b: number, c: number, d: number, e: number, f: number); + f2(1); + ~~~~~ +!!! error TS2575: No overload expects 1 arguments, but overloads do exist that expect either 0 or 2 arguments. + f2(1, 2, 3); + ~~~~~~~~~~~ +!!! error TS2575: No overload expects 3 arguments, but overloads do exist that expect either 2 or 4 arguments. + f2(1, 2, 3, 4, 5); + ~~~~~~~~~~~~~~~~~ +!!! error TS2575: No overload expects 5 arguments, but overloads do exist that expect either 4 or 6 arguments. + f2(1, 2, 3, 4, 5, 6, 7); + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2554: Expected 0-6 arguments, but got 7. + \ No newline at end of file diff --git a/tests/baselines/reference/functionParameterArityMismatch.js b/tests/baselines/reference/functionParameterArityMismatch.js new file mode 100644 index 0000000000..cfbe27d5f2 --- /dev/null +++ b/tests/baselines/reference/functionParameterArityMismatch.js @@ -0,0 +1,25 @@ +//// [functionParameterArityMismatch.ts] +declare function f1(a: number); +declare function f1(a: number, b: number, c: number); +f1(); +f1(1, 2); +f1(1, 2, 3, 4); + +declare function f2(); +declare function f2(a: number, b: number); +declare function f2(a: number, b: number, c: number, d: number); +declare function f2(a: number, b: number, c: number, d: number, e: number, f: number); +f2(1); +f2(1, 2, 3); +f2(1, 2, 3, 4, 5); +f2(1, 2, 3, 4, 5, 6, 7); + + +//// [functionParameterArityMismatch.js] +f1(); +f1(1, 2); +f1(1, 2, 3, 4); +f2(1); +f2(1, 2, 3); +f2(1, 2, 3, 4, 5); +f2(1, 2, 3, 4, 5, 6, 7); diff --git a/tests/baselines/reference/functionParameterArityMismatch.symbols b/tests/baselines/reference/functionParameterArityMismatch.symbols new file mode 100644 index 0000000000..5615ac694e --- /dev/null +++ b/tests/baselines/reference/functionParameterArityMismatch.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/functionParameterArityMismatch.ts === +declare function f1(a: number); +>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31)) +>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 0, 20)) + +declare function f1(a: number, b: number, c: number); +>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31)) +>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 1, 20)) +>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 1, 30)) +>c : Symbol(c, Decl(functionParameterArityMismatch.ts, 1, 41)) + +f1(); +>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31)) + +f1(1, 2); +>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31)) + +f1(1, 2, 3, 4); +>f1 : Symbol(f1, Decl(functionParameterArityMismatch.ts, 0, 0), Decl(functionParameterArityMismatch.ts, 0, 31)) + +declare function f2(); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) + +declare function f2(a: number, b: number); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) +>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 7, 20)) +>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 7, 30)) + +declare function f2(a: number, b: number, c: number, d: number); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) +>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 8, 20)) +>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 8, 30)) +>c : Symbol(c, Decl(functionParameterArityMismatch.ts, 8, 41)) +>d : Symbol(d, Decl(functionParameterArityMismatch.ts, 8, 52)) + +declare function f2(a: number, b: number, c: number, d: number, e: number, f: number); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) +>a : Symbol(a, Decl(functionParameterArityMismatch.ts, 9, 20)) +>b : Symbol(b, Decl(functionParameterArityMismatch.ts, 9, 30)) +>c : Symbol(c, Decl(functionParameterArityMismatch.ts, 9, 41)) +>d : Symbol(d, Decl(functionParameterArityMismatch.ts, 9, 52)) +>e : Symbol(e, Decl(functionParameterArityMismatch.ts, 9, 63)) +>f : Symbol(f, Decl(functionParameterArityMismatch.ts, 9, 74)) + +f2(1); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) + +f2(1, 2, 3); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) + +f2(1, 2, 3, 4, 5); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) + +f2(1, 2, 3, 4, 5, 6, 7); +>f2 : Symbol(f2, Decl(functionParameterArityMismatch.ts, 4, 15), Decl(functionParameterArityMismatch.ts, 6, 22), Decl(functionParameterArityMismatch.ts, 7, 42), Decl(functionParameterArityMismatch.ts, 8, 64)) + diff --git a/tests/baselines/reference/functionParameterArityMismatch.types b/tests/baselines/reference/functionParameterArityMismatch.types new file mode 100644 index 0000000000..f7117a2eef --- /dev/null +++ b/tests/baselines/reference/functionParameterArityMismatch.types @@ -0,0 +1,85 @@ +=== tests/cases/compiler/functionParameterArityMismatch.ts === +declare function f1(a: number); +>f1 : { (a: number): any; (a: number, b: number, c: number): any; } +>a : number + +declare function f1(a: number, b: number, c: number); +>f1 : { (a: number): any; (a: number, b: number, c: number): any; } +>a : number +>b : number +>c : number + +f1(); +>f1() : any +>f1 : { (a: number): any; (a: number, b: number, c: number): any; } + +f1(1, 2); +>f1(1, 2) : any +>f1 : { (a: number): any; (a: number, b: number, c: number): any; } +>1 : 1 +>2 : 2 + +f1(1, 2, 3, 4); +>f1(1, 2, 3, 4) : any +>f1 : { (a: number): any; (a: number, b: number, c: number): any; } +>1 : 1 +>2 : 2 +>3 : 3 +>4 : 4 + +declare function f2(); +>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; } + +declare function f2(a: number, b: number); +>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; } +>a : number +>b : number + +declare function f2(a: number, b: number, c: number, d: number); +>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; } +>a : number +>b : number +>c : number +>d : number + +declare function f2(a: number, b: number, c: number, d: number, e: number, f: number); +>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; } +>a : number +>b : number +>c : number +>d : number +>e : number +>f : number + +f2(1); +>f2(1) : any +>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; } +>1 : 1 + +f2(1, 2, 3); +>f2(1, 2, 3) : any +>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; } +>1 : 1 +>2 : 2 +>3 : 3 + +f2(1, 2, 3, 4, 5); +>f2(1, 2, 3, 4, 5) : any +>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; } +>1 : 1 +>2 : 2 +>3 : 3 +>4 : 4 +>5 : 5 + +f2(1, 2, 3, 4, 5, 6, 7); +>f2(1, 2, 3, 4, 5, 6, 7) : any +>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; } +>1 : 1 +>2 : 2 +>3 : 3 +>4 : 4 +>5 : 5 +>6 : 6 +>7 : 7 + diff --git a/tests/cases/compiler/functionParameterArityMismatch.ts b/tests/cases/compiler/functionParameterArityMismatch.ts new file mode 100644 index 0000000000..738568012d --- /dev/null +++ b/tests/cases/compiler/functionParameterArityMismatch.ts @@ -0,0 +1,14 @@ +declare function f1(a: number); +declare function f1(a: number, b: number, c: number); +f1(); +f1(1, 2); +f1(1, 2, 3, 4); + +declare function f2(); +declare function f2(a: number, b: number); +declare function f2(a: number, b: number, c: number, d: number); +declare function f2(a: number, b: number, c: number, d: number, e: number, f: number); +f2(1); +f2(1, 2, 3); +f2(1, 2, 3, 4, 5); +f2(1, 2, 3, 4, 5, 6, 7);