Skip to content

Commit 849ab7c

Browse files
author
Andy
committed
Merge pull request #8942 from Microsoft/param_trailing_commas
Allow trailing commas in function parameter and argument lists
2 parents 53a5b42 + ed48e58 commit 849ab7c

27 files changed

+285
-171
lines changed

src/compiler/checker.ts

+17-18
Original file line numberDiff line numberDiff line change
@@ -10322,8 +10322,8 @@ namespace ts {
1032210322
return -1;
1032310323
}
1032410324

10325-
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) {
10326-
let adjustedArgCount: number; // Apparent number of arguments we will have in this call
10325+
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature, signatureHelpTrailingComma = false) {
10326+
let argCount: number; // Apparent number of arguments we will have in this call
1032710327
let typeArguments: NodeArray<TypeNode>; // Type arguments (undefined if none)
1032810328
let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments
1032910329
let isDecorator: boolean;
@@ -10334,7 +10334,7 @@ namespace ts {
1033410334

1033510335
// Even if the call is incomplete, we'll have a missing expression as our last argument,
1033610336
// so we can say the count is just the arg list length
10337-
adjustedArgCount = args.length;
10337+
argCount = args.length;
1033810338
typeArguments = undefined;
1033910339

1034010340
if (tagExpression.template.kind === SyntaxKind.TemplateExpression) {
@@ -10357,7 +10357,7 @@ namespace ts {
1035710357
else if (node.kind === SyntaxKind.Decorator) {
1035810358
isDecorator = true;
1035910359
typeArguments = undefined;
10360-
adjustedArgCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
10360+
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
1036110361
}
1036210362
else {
1036310363
const callExpression = <CallExpression>node;
@@ -10368,8 +10368,7 @@ namespace ts {
1036810368
return signature.minArgumentCount === 0;
1036910369
}
1037010370

10371-
// For IDE scenarios we may have an incomplete call, so a trailing comma is tantamount to adding another argument.
10372-
adjustedArgCount = callExpression.arguments.hasTrailingComma ? args.length + 1 : args.length;
10371+
argCount = signatureHelpTrailingComma ? args.length + 1 : args.length;
1037310372

1037410373
// If we are missing the close paren, the call is incomplete.
1037510374
callIsIncomplete = (<CallExpression>callExpression).arguments.end === callExpression.end;
@@ -10393,12 +10392,12 @@ namespace ts {
1039310392
}
1039410393

1039510394
// Too many arguments implies incorrect arity.
10396-
if (!signature.hasRestParameter && adjustedArgCount > signature.parameters.length) {
10395+
if (!signature.hasRestParameter && argCount > signature.parameters.length) {
1039710396
return false;
1039810397
}
1039910398

1040010399
// If the call is incomplete, we should skip the lower bound check.
10401-
const hasEnoughArguments = adjustedArgCount >= signature.minArgumentCount;
10400+
const hasEnoughArguments = argCount >= signature.minArgumentCount;
1040210401
return callIsIncomplete || hasEnoughArguments;
1040310402
}
1040410403

@@ -10970,6 +10969,11 @@ namespace ts {
1097010969
let resultOfFailedInference: InferenceContext;
1097110970
let result: Signature;
1097210971

10972+
// If we are in signature help, a trailing comma indicates that we intend to provide another argument,
10973+
// so we will only accept overloads with arity at least 1 higher than the current number of provided arguments.
10974+
const signatureHelpTrailingComma =
10975+
candidatesOutArray && node.kind === SyntaxKind.CallExpression && (<CallExpression>node).arguments.hasTrailingComma;
10976+
1097310977
// Section 4.12.1:
1097410978
// if the candidate list contains one or more signatures for which the type of each argument
1097510979
// expression is a subtype of each corresponding parameter type, the return type of the first
@@ -10981,14 +10985,14 @@ namespace ts {
1098110985
// is just important for choosing the best signature. So in the case where there is only one
1098210986
// signature, the subtype pass is useless. So skipping it is an optimization.
1098310987
if (candidates.length > 1) {
10984-
result = chooseOverload(candidates, subtypeRelation);
10988+
result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma);
1098510989
}
1098610990
if (!result) {
1098710991
// Reinitialize these pointers for round two
1098810992
candidateForArgumentError = undefined;
1098910993
candidateForTypeArgumentError = undefined;
1099010994
resultOfFailedInference = undefined;
10991-
result = chooseOverload(candidates, assignableRelation);
10995+
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
1099210996
}
1099310997
if (result) {
1099410998
return result;
@@ -11059,9 +11063,9 @@ namespace ts {
1105911063
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo));
1106011064
}
1106111065

11062-
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>) {
11066+
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
1106311067
for (const originalCandidate of candidates) {
11064-
if (!hasCorrectArity(node, args, originalCandidate)) {
11068+
if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) {
1106511069
continue;
1106611070
}
1106711071

@@ -18028,10 +18032,6 @@ namespace ts {
1802818032
}
1802918033

1803018034
function checkGrammarParameterList(parameters: NodeArray<ParameterDeclaration>) {
18031-
if (checkGrammarForDisallowedTrailingComma(parameters)) {
18032-
return true;
18033-
}
18034-
1803518035
let seenOptionalParameter = false;
1803618036
const parameterCount = parameters.length;
1803718037

@@ -18150,8 +18150,7 @@ namespace ts {
1815018150
}
1815118151

1815218152
function checkGrammarArguments(node: CallExpression, args: NodeArray<Expression>): boolean {
18153-
return checkGrammarForDisallowedTrailingComma(args) ||
18154-
checkGrammarForOmittedArgument(node, args);
18153+
return checkGrammarForOmittedArgument(node, args);
1815518154
}
1815618155

1815718156
function checkGrammarHeritageClause(node: HeritageClause): boolean {

tests/baselines/reference/ArrowFunction2.errors.txt

-12
This file was deleted.

tests/baselines/reference/ArrowFunction2.js

-8
This file was deleted.

tests/baselines/reference/parserErrorRecovery_ArgumentList5.errors.txt

-16
This file was deleted.

tests/baselines/reference/parserErrorRecovery_ArgumentList5.js

-11
This file was deleted.

tests/baselines/reference/parserErrorRecovery_ParameterList3.errors.txt

-8
This file was deleted.

tests/baselines/reference/parserErrorRecovery_ParameterList3.js

-7
This file was deleted.

tests/baselines/reference/parserParameterList12.errors.txt

-8
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=== tests/cases/conformance/parser/ecmascript5/ParameterLists/parserParameterList12.ts ===
2+
function F(a,) {
3+
>F : Symbol(F, Decl(parserParameterList12.ts, 0, 0))
4+
>a : Symbol(a, Decl(parserParameterList12.ts, 0, 11))
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=== tests/cases/conformance/parser/ecmascript5/ParameterLists/parserParameterList12.ts ===
2+
function F(a,) {
3+
>F : (a: any) => void
4+
>a : any
5+
}

tests/baselines/reference/parserX_ArrowFunction2.errors.txt

-12
This file was deleted.

tests/baselines/reference/parserX_ArrowFunction2.js

-8
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//// [trailingCommasInFunctionParametersAndArguments.ts]
2+
3+
function f1(x,) {}
4+
5+
f1(1,);
6+
7+
function f2(...args,) {}
8+
9+
f2(...[],);
10+
11+
// Not confused by overloads
12+
declare function f3(x, ): number;
13+
declare function f3(x, y,): string;
14+
15+
<number>f3(1,);
16+
<string>f3(1, 2,);
17+
18+
// Works for constructors too
19+
class X {
20+
constructor(a,) { }
21+
// See trailingCommasInGetter.ts
22+
set x(value,) { }
23+
}
24+
interface Y {
25+
new(x,);
26+
(x,);
27+
}
28+
29+
new X(1,);
30+
31+
32+
//// [trailingCommasInFunctionParametersAndArguments.js]
33+
function f1(x) { }
34+
f1(1);
35+
function f2() {
36+
var args = [];
37+
for (var _i = 0; _i < arguments.length; _i++) {
38+
args[_i - 0] = arguments[_i];
39+
}
40+
}
41+
f2.apply(void 0, []);
42+
f3(1);
43+
f3(1, 2);
44+
// Works for constructors too
45+
var X = (function () {
46+
function X(a) {
47+
}
48+
Object.defineProperty(X.prototype, "x", {
49+
// See trailingCommasInGetter.ts
50+
set: function (value) { },
51+
enumerable: true,
52+
configurable: true
53+
});
54+
return X;
55+
}());
56+
new X(1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
=== tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts ===
2+
3+
function f1(x,) {}
4+
>f1 : Symbol(f1, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 0))
5+
>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 1, 12))
6+
7+
f1(1,);
8+
>f1 : Symbol(f1, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 0))
9+
10+
function f2(...args,) {}
11+
>f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 3, 7))
12+
>args : Symbol(args, Decl(trailingCommasInFunctionParametersAndArguments.ts, 5, 12))
13+
14+
f2(...[],);
15+
>f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 3, 7))
16+
17+
// Not confused by overloads
18+
declare function f3(x, ): number;
19+
>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
20+
>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 20))
21+
22+
declare function f3(x, y,): string;
23+
>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
24+
>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 11, 20))
25+
>y : Symbol(y, Decl(trailingCommasInFunctionParametersAndArguments.ts, 11, 22))
26+
27+
<number>f3(1,);
28+
>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
29+
30+
<string>f3(1, 2,);
31+
>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
32+
33+
// Works for constructors too
34+
class X {
35+
>X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 14, 18))
36+
37+
constructor(a,) { }
38+
>a : Symbol(a, Decl(trailingCommasInFunctionParametersAndArguments.ts, 18, 16))
39+
40+
// See trailingCommasInGetter.ts
41+
set x(value,) { }
42+
>x : Symbol(X.x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 18, 23))
43+
>value : Symbol(value, Decl(trailingCommasInFunctionParametersAndArguments.ts, 20, 10))
44+
}
45+
interface Y {
46+
>Y : Symbol(Y, Decl(trailingCommasInFunctionParametersAndArguments.ts, 21, 1))
47+
48+
new(x,);
49+
>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 23, 8))
50+
51+
(x,);
52+
>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 24, 5))
53+
}
54+
55+
new X(1,);
56+
>X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 14, 18))
57+

0 commit comments

Comments
 (0)