Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25051,22 +25051,40 @@ namespace ts {
effectiveParameterCount = args.length === 0 ? effectiveParameterCount : 1; // class may have argumentless ctor functions - still resolve ctor and compare vs props member type
effectiveMinimumArguments = Math.min(effectiveMinimumArguments, 1); // sfc may specify context argument - handled by framework and not typechecked
}
else if (!node.arguments) {
// This only happens when we have something of the form: 'new C'
Debug.assert(node.kind === SyntaxKind.NewExpression);
return getMinArgumentCount(signature) === 0;
}
else {
if (!node.arguments) {
// This only happens when we have something of the form: 'new C'
Debug.assert(node.kind === SyntaxKind.NewExpression);
return getMinArgumentCount(signature) === 0;
}

argCount = signatureHelpTrailingComma ? args.length + 1 : args.length;

// If we are missing the close parenthesis, the call is incomplete.
callIsIncomplete = node.arguments.end === node.end;

// If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range.
const spreadArgIndex = getSpreadArgumentIndex(args);
if (spreadArgIndex >= 0) {
return spreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || spreadArgIndex < getParameterCount(signature));
// If one or more spread arguments are present, check that they correspond to a rest parameter or at least that they are in the valid range.
const firstSpreadArgIndex = getSpreadArgumentIndex(args);
if (firstSpreadArgIndex >= 0) {
if (firstSpreadArgIndex === args.length - 1) {
// Special case, handles the munged arguments that we receive in case of a spread in the end (breaks the arg.expression below)
// (see below for code that starts with "const spreadArgument")
return firstSpreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || firstSpreadArgIndex < getParameterCount(signature));
}

let totalCount = firstSpreadArgIndex; // count previous arguments
for (let i = firstSpreadArgIndex; i < args.length; i++) {
const arg = args[i];
if (!isSpreadArgument(arg)) {
totalCount += 1;
}
else {
const argType = flowLoopCount ? checkExpression((<SpreadElement>arg).expression) : checkExpressionCached((<SpreadElement>arg).expression);
totalCount += isTupleType(argType) ? getTypeArguments(argType).length
: isArrayType(argType) ? 0
: 1;
}
}
return totalCount >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || totalCount <= getParameterCount(signature));
}
}

Expand Down Expand Up @@ -25544,7 +25562,7 @@ namespace ts {
const spreadArgument = <SpreadElement>args[length - 1];
const type = flowLoopCount ? checkExpression(spreadArgument.expression) : checkExpressionCached(spreadArgument.expression);
if (isTupleType(type)) {
const typeArguments = getTypeArguments(<TypeReference>type);
const typeArguments = getTypeArguments(type);
const restIndex = type.target.hasRestElement ? typeArguments.length - 1 : -1;
const syntheticArgs = map(typeArguments, (t, i) => createSyntheticExpression(spreadArgument, t, /*isSpread*/ i === restIndex, type.target.labeledElementDeclarations?.[i]));
return concatenate(args.slice(0, length - 1), syntheticArgs);
Expand Down
91 changes: 67 additions & 24 deletions tests/baselines/reference/callWithSpread3.errors.txt
Original file line number Diff line number Diff line change
@@ -1,32 +1,75 @@
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(5,14): error TS2554: Expected 2 arguments, but got 3.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(6,19): error TS2554: Expected 2 arguments, but got 5.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(7,19): error TS2556: Expected 2 arguments, but got 4 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(8,19): error TS2556: Expected 2 arguments, but got 5 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(9,16): error TS2556: Expected 2 arguments, but got 1 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(10,9): error TS2554: Expected 2 arguments, but got 3.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(14,10): error TS2554: Expected 2 arguments, but got 3.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(15,15): error TS2554: Expected 2 arguments, but got 5.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(16,15): error TS2556: Expected 2 arguments, but got 4 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(17,15): error TS2556: Expected 2 arguments, but got 5 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(18,12): error TS2556: Expected 2 arguments, but got 1 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(19,5): error TS2554: Expected 2 arguments, but got 3.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(20,6): error TS2557: Expected at least 2 arguments, but got 0 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(21,6): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(22,13): error TS2557: Expected at least 2 arguments, but got 1 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(23,13): error TS2557: Expected at least 2 arguments, but got 2 or more.
tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(25,7): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.


==== tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts (6 errors) ====
declare function takeTwo(a: string, b: string): void;
declare const t2: [string, string];
declare const t3: [string, string, string];
==== tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts (11 errors) ====
declare const s2: [string, string];
declare const s3: [string, string, string];
declare const s2_: [string, string, ...string[]];
declare const s_: string[];
declare const n_: number[];
declare const s2n_: [string, string, ...number[]];

takeTwo('a', ...t2); // error on ...t2
~~~~~
declare function fs2(a: string, b: string): void;
declare function fs2_(a: string, b: string, ...c: string[]): void;
declare function fs2n_(a: string, b: string, ...c: number[]): void;
declare function fs5(a: string, b: string, c: string, d: string, e: string): void;

// error
fs2('a', ...s2); // error on ...s2
~~~~~
!!! error TS2554: Expected 2 arguments, but got 3.
takeTwo('a', 'b', 'c', ...t2); // error on 'c' and ...t2
~~~~~~~~~~
fs2('a', 'b', 'c', ...s2); // error on 'c' and ...s2
~~~~~~~~~~
!!! error TS2554: Expected 2 arguments, but got 5.
takeTwo('a', 'b', ...t2, 'c'); // error on ...t2 and 'c'
~~~~~~~~~~
fs2('a', 'b', ...s2, 'c'); // error on ...s2 and 'c'
~~~~~~~~~~
!!! error TS2556: Expected 2 arguments, but got 4 or more.
takeTwo('a', 'b', 'c', ...t2, 'd'); // error on 'c', ...t2 and 'd'
~~~~~~~~~~~~~~~
fs2('a', 'b', 'c', ...s2, 'd'); // error on 'c', ...s2 and 'd'
~~~~~~~~~~~~~~~
!!! error TS2556: Expected 2 arguments, but got 5 or more.
takeTwo(...t2, 'a'); // error on 'a'
~~~
fs2(...s2, 'a'); // error on 'a'
~~~
!!! error TS2556: Expected 2 arguments, but got 1 or more.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts:1:37: An argument for 'b' was not provided.
takeTwo(...t3); // error on ...t3
~~~~~
!!! error TS2554: Expected 2 arguments, but got 3.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts:8:33: An argument for 'b' was not provided.
fs2(...s3); // error on ...s3
~~~~~
!!! error TS2554: Expected 2 arguments, but got 3.
fs2_(...s_); // error on ...s_
~~~~~
!!! error TS2557: Expected at least 2 arguments, but got 0 or more.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts:9:23: An argument for 'a' was not provided.
fs2_(...s2n_); // error on ...s2n_
~~~~~~~
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
fs2_(...s_, ...s_); // error FIXME: bad error message
~~~~~
!!! error TS2557: Expected at least 2 arguments, but got 1 or more.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts:9:34: An argument for 'b' was not provided.
fs2_(...s_, ...s_, ...s_); // error FIXME: worse error message
~~~~~~~~~~~~
!!! error TS2557: Expected at least 2 arguments, but got 2 or more.
// fs2n_(...s2, ...s_); // FIXME: should be a type error
fs2n_(...s2_); // error on ...s2_
~~~~~~
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

// ok
fs2_(...s2_);
fs2_(...s2_, ...s_);
fs2_(...s2_, ...s2_);
fs2_(...s_, ...s2_);
fs2n_(...s2n_);
fs2n_(...s2);
// fs2n_(...s2, ...n_); // FIXME: should compile
fs5(...s2, "foo", ...s2);

72 changes: 57 additions & 15 deletions tests/baselines/reference/callWithSpread3.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
//// [callWithSpread3.ts]
declare function takeTwo(a: string, b: string): void;
declare const t2: [string, string];
declare const t3: [string, string, string];
declare const s2: [string, string];
declare const s3: [string, string, string];
declare const s2_: [string, string, ...string[]];
declare const s_: string[];
declare const n_: number[];
declare const s2n_: [string, string, ...number[]];

takeTwo('a', ...t2); // error on ...t2
takeTwo('a', 'b', 'c', ...t2); // error on 'c' and ...t2
takeTwo('a', 'b', ...t2, 'c'); // error on ...t2 and 'c'
takeTwo('a', 'b', 'c', ...t2, 'd'); // error on 'c', ...t2 and 'd'
takeTwo(...t2, 'a'); // error on 'a'
takeTwo(...t3); // error on ...t3
declare function fs2(a: string, b: string): void;
declare function fs2_(a: string, b: string, ...c: string[]): void;
declare function fs2n_(a: string, b: string, ...c: number[]): void;
declare function fs5(a: string, b: string, c: string, d: string, e: string): void;

// error
fs2('a', ...s2); // error on ...s2
fs2('a', 'b', 'c', ...s2); // error on 'c' and ...s2
fs2('a', 'b', ...s2, 'c'); // error on ...s2 and 'c'
fs2('a', 'b', 'c', ...s2, 'd'); // error on 'c', ...s2 and 'd'
fs2(...s2, 'a'); // error on 'a'
fs2(...s3); // error on ...s3
fs2_(...s_); // error on ...s_
fs2_(...s2n_); // error on ...s2n_
fs2_(...s_, ...s_); // error FIXME: bad error message
fs2_(...s_, ...s_, ...s_); // error FIXME: worse error message
// fs2n_(...s2, ...s_); // FIXME: should be a type error
fs2n_(...s2_); // error on ...s2_

// ok
fs2_(...s2_);
fs2_(...s2_, ...s_);
fs2_(...s2_, ...s2_);
fs2_(...s_, ...s2_);
fs2n_(...s2n_);
fs2n_(...s2);
// fs2n_(...s2, ...n_); // FIXME: should compile
fs5(...s2, "foo", ...s2);


//// [callWithSpread3.js]
var __spreadArrays = (this && this.__spreadArrays) || function () {
Expand All @@ -18,9 +44,25 @@ var __spreadArrays = (this && this.__spreadArrays) || function () {
r[k] = a[j];
return r;
};
takeTwo.apply(void 0, __spreadArrays(['a'], t2)); // error on ...t2
takeTwo.apply(void 0, __spreadArrays(['a', 'b', 'c'], t2)); // error on 'c' and ...t2
takeTwo.apply(void 0, __spreadArrays(['a', 'b'], t2, ['c'])); // error on ...t2 and 'c'
takeTwo.apply(void 0, __spreadArrays(['a', 'b', 'c'], t2, ['d'])); // error on 'c', ...t2 and 'd'
takeTwo.apply(void 0, __spreadArrays(t2, ['a'])); // error on 'a'
takeTwo.apply(void 0, t3); // error on ...t3
// error
fs2.apply(void 0, __spreadArrays(['a'], s2)); // error on ...s2
fs2.apply(void 0, __spreadArrays(['a', 'b', 'c'], s2)); // error on 'c' and ...s2
fs2.apply(void 0, __spreadArrays(['a', 'b'], s2, ['c'])); // error on ...s2 and 'c'
fs2.apply(void 0, __spreadArrays(['a', 'b', 'c'], s2, ['d'])); // error on 'c', ...s2 and 'd'
fs2.apply(void 0, __spreadArrays(s2, ['a'])); // error on 'a'
fs2.apply(void 0, s3); // error on ...s3
fs2_.apply(void 0, s_); // error on ...s_
fs2_.apply(void 0, s2n_); // error on ...s2n_
fs2_.apply(void 0, __spreadArrays(s_, s_)); // error FIXME: bad error message
fs2_.apply(void 0, __spreadArrays(s_, s_, s_)); // error FIXME: worse error message
// fs2n_(...s2, ...s_); // FIXME: should be a type error
fs2n_.apply(void 0, s2_); // error on ...s2_
// ok
fs2_.apply(void 0, s2_);
fs2_.apply(void 0, __spreadArrays(s2_, s_));
fs2_.apply(void 0, __spreadArrays(s2_, s2_));
fs2_.apply(void 0, __spreadArrays(s_, s2_));
fs2n_.apply(void 0, s2n_);
fs2n_.apply(void 0, s2);
// fs2n_(...s2, ...n_); // FIXME: should compile
fs5.apply(void 0, __spreadArrays(s2, ["foo"], s2));
Loading