Skip to content

Commit 4b92e42

Browse files
committed
Merge pull request #1931 from Microsoft/spreadCall
Support spread operator in call expressions
2 parents 91dd9b6 + 0819ca8 commit 4b92e42

11 files changed

+822
-159
lines changed

src/compiler/checker.ts

+134-131
Large diffs are not rendered by default.

src/compiler/diagnosticInformationMap.generated.ts

+1
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ module ts {
303303
this_cannot_be_referenced_in_a_computed_property_name: { code: 2465, category: DiagnosticCategory.Error, key: "'this' cannot be referenced in a computed property name." },
304304
super_cannot_be_referenced_in_a_computed_property_name: { code: 2466, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in a computed property name." },
305305
A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type: { code: 2466, category: DiagnosticCategory.Error, key: "A computed property name cannot reference a type parameter from its containing type." },
306+
Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_6_and_higher: { code: 2468, category: DiagnosticCategory.Error, key: "Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher." },
306307
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
307308
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
308309
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,10 @@
12041204
"category": "Error",
12051205
"code": 2466
12061206
},
1207+
"Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": {
1208+
"category": "Error",
1209+
"code": 2468
1210+
},
12071211

12081212
"Import declaration '{0}' is using private name '{1}'.": {
12091213
"category": "Error",

src/compiler/emitter.ts

+94-17
Original file line numberDiff line numberDiff line change
@@ -1994,7 +1994,7 @@ module ts {
19941994
break;
19951995
}
19961996
// _a .. _h, _j ... _z, _0, _1, ...
1997-
name = "_" + (tempCount < 25 ? String.fromCharCode(tempCount + (tempCount < 8 ? 0: 1) + CharacterCodes.a) : tempCount - 25);
1997+
name = "_" + (tempCount < 25 ? String.fromCharCode(tempCount + (tempCount < 8 ? 0 : 1) + CharacterCodes.a) : tempCount - 25);
19981998
tempCount++;
19991999
}
20002000
var result = <Identifier>createNode(SyntaxKind.Identifier);
@@ -2423,22 +2423,10 @@ module ts {
24232423
return true;
24242424
}
24252425

2426-
function emitArrayLiteral(node: ArrayLiteralExpression) {
2427-
var elements = node.elements;
2428-
var length = elements.length;
2429-
if (length === 0) {
2430-
write("[]");
2431-
return;
2432-
}
2433-
if (languageVersion >= ScriptTarget.ES6) {
2434-
write("[");
2435-
emitList(elements, 0, elements.length, /*multiLine*/(node.flags & NodeFlags.MultiLine) !== 0,
2436-
/*trailingComma*/ elements.hasTrailingComma);
2437-
write("]");
2438-
return;
2439-
}
2426+
function emitListWithSpread(elements: Expression[], multiLine: boolean, trailingComma: boolean) {
24402427
var pos = 0;
24412428
var group = 0;
2429+
var length = elements.length;
24422430
while (pos < length) {
24432431
// Emit using the pattern <group0>.concat(<group1>, <group2>, ...)
24442432
if (group === 1) {
@@ -2459,8 +2447,7 @@ module ts {
24592447
i++;
24602448
}
24612449
write("[");
2462-
emitList(elements, pos, i - pos, /*multiLine*/ (node.flags & NodeFlags.MultiLine) !== 0,
2463-
/*trailingComma*/ elements.hasTrailingComma);
2450+
emitList(elements, pos, i - pos, multiLine, trailingComma && i === length);
24642451
write("]");
24652452
pos = i;
24662453
}
@@ -2471,6 +2458,23 @@ module ts {
24712458
}
24722459
}
24732460

2461+
function emitArrayLiteral(node: ArrayLiteralExpression) {
2462+
var elements = node.elements;
2463+
if (elements.length === 0) {
2464+
write("[]");
2465+
}
2466+
else if (languageVersion >= ScriptTarget.ES6) {
2467+
write("[");
2468+
emitList(elements, 0, elements.length, /*multiLine*/ (node.flags & NodeFlags.MultiLine) !== 0,
2469+
/*trailingComma*/ elements.hasTrailingComma);
2470+
write("]");
2471+
}
2472+
else {
2473+
emitListWithSpread(elements, /*multiLine*/ (node.flags & NodeFlags.MultiLine) !== 0,
2474+
/*trailingComma*/ elements.hasTrailingComma);
2475+
}
2476+
}
2477+
24742478
function emitObjectLiteral(node: ObjectLiteralExpression) {
24752479
write("{");
24762480
var properties = node.properties;
@@ -2565,7 +2569,80 @@ module ts {
25652569
write("]");
25662570
}
25672571

2572+
function hasSpreadElement(elements: Expression[]) {
2573+
return forEach(elements, e => e.kind === SyntaxKind.SpreadElementExpression);
2574+
}
2575+
2576+
function skipParentheses(node: Expression): Expression {
2577+
while (node.kind === SyntaxKind.ParenthesizedExpression || node.kind === SyntaxKind.TypeAssertionExpression) {
2578+
node = (<ParenthesizedExpression | TypeAssertion>node).expression;
2579+
}
2580+
return node;
2581+
}
2582+
2583+
function emitCallTarget(node: Expression): Expression {
2584+
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) {
2585+
emit(node);
2586+
return node;
2587+
}
2588+
var temp = createTempVariable(node);
2589+
recordTempDeclaration(temp);
2590+
write("(");
2591+
emit(temp);
2592+
write(" = ");
2593+
emit(node);
2594+
write(")");
2595+
return temp;
2596+
}
2597+
2598+
function emitCallWithSpread(node: CallExpression) {
2599+
var target: Expression;
2600+
var expr = skipParentheses(node.expression);
2601+
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
2602+
// Target will be emitted as "this" argument
2603+
target = emitCallTarget((<PropertyAccessExpression>expr).expression);
2604+
write(".");
2605+
emit((<PropertyAccessExpression>expr).name);
2606+
}
2607+
else if (expr.kind === SyntaxKind.ElementAccessExpression) {
2608+
// Target will be emitted as "this" argument
2609+
target = emitCallTarget((<PropertyAccessExpression>expr).expression);
2610+
write("[");
2611+
emit((<ElementAccessExpression>expr).argumentExpression);
2612+
write("]");
2613+
}
2614+
else if (expr.kind === SyntaxKind.SuperKeyword) {
2615+
target = expr;
2616+
write("_super");
2617+
}
2618+
else {
2619+
emit(node.expression);
2620+
}
2621+
write(".apply(");
2622+
if (target) {
2623+
if (target.kind === SyntaxKind.SuperKeyword) {
2624+
// Calls of form super(...) and super.foo(...)
2625+
emitThis(target);
2626+
}
2627+
else {
2628+
// Calls of form obj.foo(...)
2629+
emit(target);
2630+
}
2631+
}
2632+
else {
2633+
// Calls of form foo(...)
2634+
write("void 0");
2635+
}
2636+
write(", ");
2637+
emitListWithSpread(node.arguments, /*multiLine*/ false, /*trailingComma*/ false);
2638+
write(")");
2639+
}
2640+
25682641
function emitCallExpression(node: CallExpression) {
2642+
if (languageVersion < ScriptTarget.ES6 && hasSpreadElement(node.arguments)) {
2643+
emitCallWithSpread(node);
2644+
return;
2645+
}
25692646
var superCall = false;
25702647
if (node.expression.kind === SyntaxKind.SuperKeyword) {
25712648
write("_super");

src/compiler/parser.ts

+6-11
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,6 @@ module ts {
15161516
case ParsingContext.TypeParameters:
15171517
return isIdentifier();
15181518
case ParsingContext.ArgumentExpressions:
1519-
return token === SyntaxKind.CommaToken || isStartOfExpression();
15201519
case ParsingContext.ArrayLiteralMembers:
15211520
return token === SyntaxKind.CommaToken || token === SyntaxKind.DotDotDotToken || isStartOfExpression();
15221521
case ParsingContext.Parameters:
@@ -3605,32 +3604,28 @@ module ts {
36053604
return finishNode(node);
36063605
}
36073606

3608-
function parseAssignmentExpressionOrOmittedExpression(): Expression {
3609-
return token === SyntaxKind.CommaToken
3610-
? <Expression>createNode(SyntaxKind.OmittedExpression)
3611-
: parseAssignmentExpressionOrHigher();
3612-
}
3613-
36143607
function parseSpreadElement(): Expression {
36153608
var node = <SpreadElementExpression>createNode(SyntaxKind.SpreadElementExpression);
36163609
parseExpected(SyntaxKind.DotDotDotToken);
36173610
node.expression = parseAssignmentExpressionOrHigher();
36183611
return finishNode(node);
36193612
}
36203613

3621-
function parseArrayLiteralElement(): Expression {
3622-
return token === SyntaxKind.DotDotDotToken ? parseSpreadElement() : parseAssignmentExpressionOrOmittedExpression();
3614+
function parseArgumentOrArrayLiteralElement(): Expression {
3615+
return token === SyntaxKind.DotDotDotToken ? parseSpreadElement() :
3616+
token === SyntaxKind.CommaToken ? <Expression>createNode(SyntaxKind.OmittedExpression) :
3617+
parseAssignmentExpressionOrHigher();
36233618
}
36243619

36253620
function parseArgumentExpression(): Expression {
3626-
return allowInAnd(parseAssignmentExpressionOrOmittedExpression);
3621+
return allowInAnd(parseArgumentOrArrayLiteralElement);
36273622
}
36283623

36293624
function parseArrayLiteralExpression(): ArrayLiteralExpression {
36303625
var node = <ArrayLiteralExpression>createNode(SyntaxKind.ArrayLiteralExpression);
36313626
parseExpected(SyntaxKind.OpenBracketToken);
36323627
if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine;
3633-
node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement);
3628+
node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArgumentOrArrayLiteralElement);
36343629
parseExpected(SyntaxKind.CloseBracketToken);
36353630
return finishNode(node);
36363631
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
tests/cases/conformance/expressions/functionCalls/callWithSpread.ts(52,21): error TS2468: Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.
2+
3+
4+
==== tests/cases/conformance/expressions/functionCalls/callWithSpread.ts (1 errors) ====
5+
interface X {
6+
foo(x: number, y: number, ...z: string[]);
7+
}
8+
9+
function foo(x: number, y: number, ...z: string[]) {
10+
}
11+
12+
var a: string[];
13+
var z: number[];
14+
var obj: X;
15+
var xa: X[];
16+
17+
foo(1, 2, "abc");
18+
foo(1, 2, ...a);
19+
foo(1, 2, ...a, "abc");
20+
21+
obj.foo(1, 2, "abc");
22+
obj.foo(1, 2, ...a);
23+
obj.foo(1, 2, ...a, "abc");
24+
25+
(obj.foo)(1, 2, "abc");
26+
(obj.foo)(1, 2, ...a);
27+
(obj.foo)(1, 2, ...a, "abc");
28+
29+
xa[1].foo(1, 2, "abc");
30+
xa[1].foo(1, 2, ...a);
31+
xa[1].foo(1, 2, ...a, "abc");
32+
33+
(<Function>xa[1].foo)(...[1, 2, "abc"]);
34+
35+
class C {
36+
constructor(x: number, y: number, ...z: string[]) {
37+
this.foo(x, y);
38+
this.foo(x, y, ...z);
39+
}
40+
foo(x: number, y: number, ...z: string[]) {
41+
}
42+
}
43+
44+
class D extends C {
45+
constructor() {
46+
super(1, 2);
47+
super(1, 2, ...a);
48+
}
49+
foo() {
50+
super.foo(1, 2);
51+
super.foo(1, 2, ...a);
52+
}
53+
}
54+
55+
// Only supported in when target is ES6
56+
var c = new C(1, 2, ...a);
57+
~~~~
58+
!!! error TS2468: Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.
59+
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//// [callWithSpread.ts]
2+
interface X {
3+
foo(x: number, y: number, ...z: string[]);
4+
}
5+
6+
function foo(x: number, y: number, ...z: string[]) {
7+
}
8+
9+
var a: string[];
10+
var z: number[];
11+
var obj: X;
12+
var xa: X[];
13+
14+
foo(1, 2, "abc");
15+
foo(1, 2, ...a);
16+
foo(1, 2, ...a, "abc");
17+
18+
obj.foo(1, 2, "abc");
19+
obj.foo(1, 2, ...a);
20+
obj.foo(1, 2, ...a, "abc");
21+
22+
(obj.foo)(1, 2, "abc");
23+
(obj.foo)(1, 2, ...a);
24+
(obj.foo)(1, 2, ...a, "abc");
25+
26+
xa[1].foo(1, 2, "abc");
27+
xa[1].foo(1, 2, ...a);
28+
xa[1].foo(1, 2, ...a, "abc");
29+
30+
(<Function>xa[1].foo)(...[1, 2, "abc"]);
31+
32+
class C {
33+
constructor(x: number, y: number, ...z: string[]) {
34+
this.foo(x, y);
35+
this.foo(x, y, ...z);
36+
}
37+
foo(x: number, y: number, ...z: string[]) {
38+
}
39+
}
40+
41+
class D extends C {
42+
constructor() {
43+
super(1, 2);
44+
super(1, 2, ...a);
45+
}
46+
foo() {
47+
super.foo(1, 2);
48+
super.foo(1, 2, ...a);
49+
}
50+
}
51+
52+
// Only supported in when target is ES6
53+
var c = new C(1, 2, ...a);
54+
55+
56+
//// [callWithSpread.js]
57+
var __extends = this.__extends || function (d, b) {
58+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
59+
function __() { this.constructor = d; }
60+
__.prototype = b.prototype;
61+
d.prototype = new __();
62+
};
63+
function foo(x, y) {
64+
var z = [];
65+
for (var _i = 2; _i < arguments.length; _i++) {
66+
z[_i - 2] = arguments[_i];
67+
}
68+
}
69+
var a;
70+
var z;
71+
var obj;
72+
var xa;
73+
foo(1, 2, "abc");
74+
foo.apply(void 0, [1, 2].concat(a));
75+
foo.apply(void 0, [1, 2].concat(a, ["abc"]));
76+
obj.foo(1, 2, "abc");
77+
obj.foo.apply(obj, [1, 2].concat(a));
78+
obj.foo.apply(obj, [1, 2].concat(a, ["abc"]));
79+
(obj.foo)(1, 2, "abc");
80+
obj.foo.apply(obj, [1, 2].concat(a));
81+
obj.foo.apply(obj, [1, 2].concat(a, ["abc"]));
82+
xa[1].foo(1, 2, "abc");
83+
(_a = xa[1]).foo.apply(_a, [1, 2].concat(a));
84+
(_b = xa[1]).foo.apply(_b, [1, 2].concat(a, ["abc"]));
85+
(_c = xa[1]).foo.apply(_c, [1, 2, "abc"]);
86+
var C = (function () {
87+
function C(x, y) {
88+
var z = [];
89+
for (var _i = 2; _i < arguments.length; _i++) {
90+
z[_i - 2] = arguments[_i];
91+
}
92+
this.foo(x, y);
93+
this.foo.apply(this, [x, y].concat(z));
94+
}
95+
C.prototype.foo = function (x, y) {
96+
var z = [];
97+
for (var _i = 2; _i < arguments.length; _i++) {
98+
z[_i - 2] = arguments[_i];
99+
}
100+
};
101+
return C;
102+
})();
103+
var D = (function (_super) {
104+
__extends(D, _super);
105+
function D() {
106+
_super.call(this, 1, 2);
107+
_super.apply(this, [1, 2].concat(a));
108+
}
109+
D.prototype.foo = function () {
110+
_super.prototype.foo.call(this, 1, 2);
111+
_super.prototype.foo.apply(this, [1, 2].concat(a));
112+
};
113+
return D;
114+
})(C);
115+
// Only supported in when target is ES6
116+
var c = new C(1, 2, ...a);
117+
var _a, _b, _c;

0 commit comments

Comments
 (0)