From 0546042df3c61245b14d070707a51522de6b329c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 8 Apr 2015 18:01:21 -0700 Subject: [PATCH 1/5] Add support for parsing destructuring patterns in arrow function parameter lists --- src/compiler/parser.ts | 27 +++++++-- .../reference/arrowFunctionExpressions.js | 42 ++++++++++++++ .../reference/arrowFunctionExpressions.types | 55 +++++++++++++++++++ .../functions/arrowFunctionExpressions.ts | 11 ++++ 4 files changed, 130 insertions(+), 5 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index bde4cec3f3f..3a6100bbe6b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3221,10 +3221,27 @@ module ts { } } - // Simple case: "(..." + // If we had "(" followed by "{" or "[", this could be the start of a binding pattern. + let possiblyInArrayBindingPattern = false; + let possiblyInObjectBindingPattern = false; + while (second === SyntaxKind.OpenBraceToken || second === SyntaxKind.OpenBracketToken) { + possiblyInObjectBindingPattern = second === SyntaxKind.OpenBraceToken; + possiblyInArrayBindingPattern = second === SyntaxKind.OpenBracketToken; + second = nextToken(); + } + + if (possiblyInArrayBindingPattern) { + // If we are possibly in an array binding pattern, skip empty elements + while (second === SyntaxKind.CommaToken) { + second = nextToken(); + } + } + + // Simple case: "(..." or "([...", but not "({..." // This is an arrow function with a rest parameter. - if (second === SyntaxKind.DotDotDotToken) { - return Tristate.True; + if (second === SyntaxKind.DotDotDotToken && !possiblyInObjectBindingPattern) { + // if we are possibly in an array binding pattern, then this may be a lambda, otherwise it must be a lambda. + return possiblyInArrayBindingPattern ? Tristate.Unknown : Tristate.True; } // If we had "(" followed by something that's not an identifier, @@ -3236,9 +3253,9 @@ module ts { return Tristate.False; } - // If we have something like "(a:", then we must have a + // If we have something like "(a:", but not "({ a:", then we must have a // type-annotated parameter in an arrow function expression. - if (nextToken() === SyntaxKind.ColonToken) { + if (nextToken() === SyntaxKind.ColonToken && !possiblyInObjectBindingPattern) { return Tristate.True; } diff --git a/tests/baselines/reference/arrowFunctionExpressions.js b/tests/baselines/reference/arrowFunctionExpressions.js index 68516b52cb2..5e365291da7 100644 --- a/tests/baselines/reference/arrowFunctionExpressions.js +++ b/tests/baselines/reference/arrowFunctionExpressions.js @@ -13,6 +13,17 @@ var d = n => c = n; var d = (n) => c = n; var d: (n: any) => any; +// Binding patterns in arrow functions +var p1 = ([a]) => { }; +var p2 = ([...a]) => { }; +var p3 = ([, a]) => { }; +var p4 = ([, ...a]) => { }; +var p5 = ([a = 1]) => { }; +var p6 = ({ a }) => { }; +var p7 = ({ a: { b } }) => { }; +var p8 = ({ a = 1 }) => { }; +var p9 = ({ a: { b = 1 } = { b: 1 } }) => { }; +var p10 = ([{ value, done }]) => { }; // Arrow function used in class member initializer // Arrow function used in class member function @@ -100,6 +111,37 @@ var c; var d = function (n) { return c = n; }; var d = function (n) { return c = n; }; var d; +// Binding patterns in arrow functions +var p1 = function (_a) { + var a = _a[0]; +}; +var p2 = function (_a) { + var a = _a.slice(0); +}; +var p3 = function (_a) { + var a = _a[1]; +}; +var p4 = function (_a) { + var a = _a.slice(1); +}; +var p5 = function (_a) { + var _b = _a[0], a = _b === void 0 ? 1 : _b; +}; +var p6 = function (_a) { + var a = _a.a; +}; +var p7 = function (_a) { + var b = _a.a.b; +}; +var p8 = function (_a) { + var _b = _a.a, a = _b === void 0 ? 1 : _b; +}; +var p9 = function (_a) { + var _b = _a.a, _c = (_b === void 0 ? { b: 1 } : _b).b, b = _c === void 0 ? 1 : _c; +}; +var p10 = function (_a) { + var _b = _a[0], value = _b.value, done = _b.done; +}; // Arrow function used in class member initializer // Arrow function used in class member function var MyClass = (function () { diff --git a/tests/baselines/reference/arrowFunctionExpressions.types b/tests/baselines/reference/arrowFunctionExpressions.types index 5807f6d6a7d..c10ecdad353 100644 --- a/tests/baselines/reference/arrowFunctionExpressions.types +++ b/tests/baselines/reference/arrowFunctionExpressions.types @@ -51,6 +51,61 @@ var d: (n: any) => any; >d : (n: any) => any >n : any +// Binding patterns in arrow functions +var p1 = ([a]) => { }; +>p1 : ([a]: [any]) => void +>([a]) => { } : ([a]: [any]) => void +>a : any + +var p2 = ([...a]) => { }; +>p2 : ([...a]: any[]) => void +>([...a]) => { } : ([...a]: any[]) => void +>a : any[] + +var p3 = ([, a]) => { }; +>p3 : ([, a]: [any, any]) => void +>([, a]) => { } : ([, a]: [any, any]) => void +>a : any + +var p4 = ([, ...a]) => { }; +>p4 : ([, ...a]: any[]) => void +>([, ...a]) => { } : ([, ...a]: any[]) => void +>a : any[] + +var p5 = ([a = 1]) => { }; +>p5 : ([a = 1]: [number]) => void +>([a = 1]) => { } : ([a = 1]: [number]) => void +>a : number + +var p6 = ({ a }) => { }; +>p6 : ({ a }: { a: any; }) => void +>({ a }) => { } : ({ a }: { a: any; }) => void +>a : any + +var p7 = ({ a: { b } }) => { }; +>p7 : ({ a: { b } }: { a: { b: any; }; }) => void +>({ a: { b } }) => { } : ({ a: { b } }: { a: { b: any; }; }) => void +>a : unknown +>b : any + +var p8 = ({ a = 1 }) => { }; +>p8 : ({ a = 1 }: { a?: number; }) => void +>({ a = 1 }) => { } : ({ a = 1 }: { a?: number; }) => void +>a : number + +var p9 = ({ a: { b = 1 } = { b: 1 } }) => { }; +>p9 : ({ a: { b = 1 } = { b: 1 } }: { a?: { b: number; }; }) => void +>({ a: { b = 1 } = { b: 1 } }) => { } : ({ a: { b = 1 } = { b: 1 } }: { a?: { b: number; }; }) => void +>a : unknown +>b : number +>{ b: 1 } : { b: number; } +>b : number + +var p10 = ([{ value, done }]) => { }; +>p10 : ([{ value, done }]: [{ value: any; done: any; }]) => void +>([{ value, done }]) => { } : ([{ value, done }]: [{ value: any; done: any; }]) => void +>value : any +>done : any // Arrow function used in class member initializer // Arrow function used in class member function diff --git a/tests/cases/conformance/expressions/functions/arrowFunctionExpressions.ts b/tests/cases/conformance/expressions/functions/arrowFunctionExpressions.ts index a0d3b46bf43..707ef02de82 100644 --- a/tests/cases/conformance/expressions/functions/arrowFunctionExpressions.ts +++ b/tests/cases/conformance/expressions/functions/arrowFunctionExpressions.ts @@ -12,6 +12,17 @@ var d = n => c = n; var d = (n) => c = n; var d: (n: any) => any; +// Binding patterns in arrow functions +var p1 = ([a]) => { }; +var p2 = ([...a]) => { }; +var p3 = ([, a]) => { }; +var p4 = ([, ...a]) => { }; +var p5 = ([a = 1]) => { }; +var p6 = ({ a }) => { }; +var p7 = ({ a: { b } }) => { }; +var p8 = ({ a = 1 }) => { }; +var p9 = ({ a: { b = 1 } = { b: 1 } }) => { }; +var p10 = ([{ value, done }]) => { }; // Arrow function used in class member initializer // Arrow function used in class member function From 19695f9bca94ef3a59ec4bf66d7854351522e27e Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 9 Apr 2015 17:45:45 -0700 Subject: [PATCH 2/5] Simplified lookahead and added ES6 test cases --- src/compiler/parser.ts | 54 +++++++++++------- .../reference/emitArrowFunctionES6.js | 23 ++++++++ .../reference/emitArrowFunctionES6.types | 56 +++++++++++++++++++ .../es6/arrowFunction/emitArrowFunctionES6.ts | 12 ++++ 4 files changed, 125 insertions(+), 20 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3a6100bbe6b..c6ae2972b30 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3221,30 +3221,44 @@ module ts { } } - // If we had "(" followed by "{" or "[", this could be the start of a binding pattern. - let possiblyInArrayBindingPattern = false; - let possiblyInObjectBindingPattern = false; - while (second === SyntaxKind.OpenBraceToken || second === SyntaxKind.OpenBracketToken) { - possiblyInObjectBindingPattern = second === SyntaxKind.OpenBraceToken; - possiblyInArrayBindingPattern = second === SyntaxKind.OpenBracketToken; - second = nextToken(); - } - - if (possiblyInArrayBindingPattern) { - // If we are possibly in an array binding pattern, skip empty elements - while (second === SyntaxKind.CommaToken) { - second = nextToken(); + // If encounter "([", this could be the start of an array binding pattern. + // Examples: + // ([ x ]) => { } + // ([ x ]) + if (second === SyntaxKind.OpenBracketToken) { + // If the next token is not ",", "...", "[", "{", or an identifier, then this could either be + // an arrow function with an array binding pattern or a parenthesized array literal expression. + // Otherwise, it cannot be an array binding pattern. + let third = nextToken(); + if (isIdentifierOrPattern() || third === SyntaxKind.CommaToken || third === SyntaxKind.DotDotDotToken) { + return Tristate.Unknown; } + + return Tristate.False; } - // Simple case: "(..." or "([...", but not "({..." + // If we encounter "({", this could be the start of an object binding pattern. + // Examples: + // ({ x }) => { } + // ({ x }) + // ({ *x() { }) + if (second === SyntaxKind.OpenBraceToken) { + // If we encountered an asterisk, then this is a generator method on an + // object literal and cannot be a binding pattern. + if (nextToken() === SyntaxKind.AsteriskToken) { + return Tristate.False; + } + + return Tristate.Unknown; + } + + // Simple case: "(..." // This is an arrow function with a rest parameter. - if (second === SyntaxKind.DotDotDotToken && !possiblyInObjectBindingPattern) { - // if we are possibly in an array binding pattern, then this may be a lambda, otherwise it must be a lambda. - return possiblyInArrayBindingPattern ? Tristate.Unknown : Tristate.True; + if (second === SyntaxKind.DotDotDotToken) { + return Tristate.True; } - // If we had "(" followed by something that's not an identifier, + // If we had something like "(" followed by something that's not an identifier, // then this definitely doesn't look like a lambda. // Note: we could be a little more lenient and allow // "(public" or "(private". These would not ever actually be allowed, @@ -3253,9 +3267,9 @@ module ts { return Tristate.False; } - // If we have something like "(a:", but not "({ a:", then we must have a + // If we have something like "(a:", then we may have a // type-annotated parameter in an arrow function expression. - if (nextToken() === SyntaxKind.ColonToken && !possiblyInObjectBindingPattern) { + if (nextToken() === SyntaxKind.ColonToken) { return Tristate.True; } diff --git a/tests/baselines/reference/emitArrowFunctionES6.js b/tests/baselines/reference/emitArrowFunctionES6.js index 603b2737fc5..f9f5669c92e 100644 --- a/tests/baselines/reference/emitArrowFunctionES6.js +++ b/tests/baselines/reference/emitArrowFunctionES6.js @@ -6,6 +6,18 @@ var f4 = (x: string, y: number, z=10) => { } function foo(func: () => boolean) { } foo(() => true); foo(() => { return false; }); + +// Binding patterns in arrow functions +var p1 = ([a]) => { }; +var p2 = ([...a]) => { }; +var p3 = ([, a]) => { }; +var p4 = ([, ...a]) => { }; +var p5 = ([a = 1]) => { }; +var p6 = ({ a }) => { }; +var p7 = ({ a: { b } }) => { }; +var p8 = ({ a = 1 }) => { }; +var p9 = ({ a: { b = 1 } = { b: 1 } }) => { }; +var p10 = ([{ value, done }]) => { }; //// [emitArrowFunctionES6.js] @@ -16,3 +28,14 @@ var f4 = (x, y, z = 10) => { }; function foo(func) { } foo(() => true); foo(() => { return false; }); +// Binding patterns in arrow functions +var p1 = ([a]) => { }; +var p2 = ([...a]) => { }; +var p3 = ([, a]) => { }; +var p4 = ([, ...a]) => { }; +var p5 = ([a = 1]) => { }; +var p6 = ({ a }) => { }; +var p7 = ({ a: { b } }) => { }; +var p8 = ({ a = 1 }) => { }; +var p9 = ({ a: { b = 1 } = { b: 1 } }) => { }; +var p10 = ([{ value, done }]) => { }; diff --git a/tests/baselines/reference/emitArrowFunctionES6.types b/tests/baselines/reference/emitArrowFunctionES6.types index 1f0c1941bdb..7ded82f9a98 100644 --- a/tests/baselines/reference/emitArrowFunctionES6.types +++ b/tests/baselines/reference/emitArrowFunctionES6.types @@ -37,3 +37,59 @@ foo(() => { return false; }); >foo : (func: () => boolean) => void >() => { return false; } : () => boolean +// Binding patterns in arrow functions +var p1 = ([a]) => { }; +>p1 : ([a]: [any]) => void +>([a]) => { } : ([a]: [any]) => void +>a : any + +var p2 = ([...a]) => { }; +>p2 : ([...a]: any[]) => void +>([...a]) => { } : ([...a]: any[]) => void +>a : any[] + +var p3 = ([, a]) => { }; +>p3 : ([, a]: [any, any]) => void +>([, a]) => { } : ([, a]: [any, any]) => void +>a : any + +var p4 = ([, ...a]) => { }; +>p4 : ([, ...a]: any[]) => void +>([, ...a]) => { } : ([, ...a]: any[]) => void +>a : any[] + +var p5 = ([a = 1]) => { }; +>p5 : ([a = 1]: [number]) => void +>([a = 1]) => { } : ([a = 1]: [number]) => void +>a : number + +var p6 = ({ a }) => { }; +>p6 : ({ a }: { a: any; }) => void +>({ a }) => { } : ({ a }: { a: any; }) => void +>a : any + +var p7 = ({ a: { b } }) => { }; +>p7 : ({ a: { b } }: { a: { b: any; }; }) => void +>({ a: { b } }) => { } : ({ a: { b } }: { a: { b: any; }; }) => void +>a : unknown +>b : any + +var p8 = ({ a = 1 }) => { }; +>p8 : ({ a = 1 }: { a?: number; }) => void +>({ a = 1 }) => { } : ({ a = 1 }: { a?: number; }) => void +>a : number + +var p9 = ({ a: { b = 1 } = { b: 1 } }) => { }; +>p9 : ({ a: { b = 1 } = { b: 1 } }: { a?: { b: number; }; }) => void +>({ a: { b = 1 } = { b: 1 } }) => { } : ({ a: { b = 1 } = { b: 1 } }: { a?: { b: number; }; }) => void +>a : unknown +>b : number +>{ b: 1 } : { b: number; } +>b : number + +var p10 = ([{ value, done }]) => { }; +>p10 : ([{ value, done }]: [{ value: any; done: any; }]) => void +>([{ value, done }]) => { } : ([{ value, done }]: [{ value: any; done: any; }]) => void +>value : any +>done : any + diff --git a/tests/cases/conformance/es6/arrowFunction/emitArrowFunctionES6.ts b/tests/cases/conformance/es6/arrowFunction/emitArrowFunctionES6.ts index d56d07c6c4e..9dff987cd64 100644 --- a/tests/cases/conformance/es6/arrowFunction/emitArrowFunctionES6.ts +++ b/tests/cases/conformance/es6/arrowFunction/emitArrowFunctionES6.ts @@ -6,3 +6,15 @@ var f4 = (x: string, y: number, z=10) => { } function foo(func: () => boolean) { } foo(() => true); foo(() => { return false; }); + +// Binding patterns in arrow functions +var p1 = ([a]) => { }; +var p2 = ([...a]) => { }; +var p3 = ([, a]) => { }; +var p4 = ([, ...a]) => { }; +var p5 = ([a = 1]) => { }; +var p6 = ({ a }) => { }; +var p7 = ({ a: { b } }) => { }; +var p8 = ({ a = 1 }) => { }; +var p9 = ({ a: { b = 1 } = { b: 1 } }) => { }; +var p10 = ([{ value, done }]) => { }; From 96361427333de2f83e33a0055adb30bf510a54e2 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 9 Apr 2015 17:47:56 -0700 Subject: [PATCH 3/5] Comment cleanup --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c6ae2972b30..19dbc1a962f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3258,7 +3258,7 @@ module ts { return Tristate.True; } - // If we had something like "(" followed by something that's not an identifier, + // If we had "(" followed by something that's not an identifier, // then this definitely doesn't look like a lambda. // Note: we could be a little more lenient and allow // "(public" or "(private". These would not ever actually be allowed, @@ -3267,7 +3267,7 @@ module ts { return Tristate.False; } - // If we have something like "(a:", then we may have a + // If we have something like "(a:", then we must have a // type-annotated parameter in an arrow function expression. if (nextToken() === SyntaxKind.ColonToken) { return Tristate.True; From 02d88f29684671629196f03231ff8e43a4e4c582 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 10 Apr 2015 09:53:21 -0700 Subject: [PATCH 4/5] Simpler lookahead, let the tryParse do the hard work --- src/compiler/parser.ts | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 19dbc1a962f..4e23e26e6b7 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3221,34 +3221,13 @@ module ts { } } - // If encounter "([", this could be the start of an array binding pattern. + // If encounter "([" or "({", this could be the start of a binding pattern. // Examples: // ([ x ]) => { } - // ([ x ]) - if (second === SyntaxKind.OpenBracketToken) { - // If the next token is not ",", "...", "[", "{", or an identifier, then this could either be - // an arrow function with an array binding pattern or a parenthesized array literal expression. - // Otherwise, it cannot be an array binding pattern. - let third = nextToken(); - if (isIdentifierOrPattern() || third === SyntaxKind.CommaToken || third === SyntaxKind.DotDotDotToken) { - return Tristate.Unknown; - } - - return Tristate.False; - } - - // If we encounter "({", this could be the start of an object binding pattern. - // Examples: // ({ x }) => { } + // ([ x ]) // ({ x }) - // ({ *x() { }) - if (second === SyntaxKind.OpenBraceToken) { - // If we encountered an asterisk, then this is a generator method on an - // object literal and cannot be a binding pattern. - if (nextToken() === SyntaxKind.AsteriskToken) { - return Tristate.False; - } - + if (second === SyntaxKind.OpenBracketToken || second === SyntaxKind.OpenBraceToken) { return Tristate.Unknown; } From 15df45d8130beaf1ea0de63c60a5bcdf6d1ade88 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 10 Apr 2015 11:09:24 -0700 Subject: [PATCH 5/5] Update baselines due to changes in master --- tests/baselines/reference/emitArrowFunctionES6.types | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/emitArrowFunctionES6.types b/tests/baselines/reference/emitArrowFunctionES6.types index 7ded82f9a98..6f47b51a167 100644 --- a/tests/baselines/reference/emitArrowFunctionES6.types +++ b/tests/baselines/reference/emitArrowFunctionES6.types @@ -44,8 +44,8 @@ var p1 = ([a]) => { }; >a : any var p2 = ([...a]) => { }; ->p2 : ([...a]: any[]) => void ->([...a]) => { } : ([...a]: any[]) => void +>p2 : ([...a]: Iterable) => void +>([...a]) => { } : ([...a]: Iterable) => void >a : any[] var p3 = ([, a]) => { }; @@ -54,8 +54,8 @@ var p3 = ([, a]) => { }; >a : any var p4 = ([, ...a]) => { }; ->p4 : ([, ...a]: any[]) => void ->([, ...a]) => { } : ([, ...a]: any[]) => void +>p4 : ([, ...a]: Iterable) => void +>([, ...a]) => { } : ([, ...a]: Iterable) => void >a : any[] var p5 = ([a = 1]) => { };