Improved lookahead for arrow functions.
Fixes issue #34. Specifically: * We no longer automatically assume "()" is a function expression. It must be followed by an arrow, colon, or curly brace. * If an arrow is missing following a signature, but a curly brace is present, we assume the user forgot the arrow and try to parse the body anyway.
This commit is contained in:
parent
c8fc26a20a
commit
5b6bb5b649
|
@ -1445,6 +1445,7 @@ module ts {
|
|||
function tryParseParenthesizedArrowFunctionExpression(): Expression {
|
||||
var pos = getNodePos();
|
||||
|
||||
// Whether we are certain that we should parse an arrow expression.
|
||||
var triState = isParenthesizedArrowFunctionExpression();
|
||||
|
||||
// It is not a parenthesized arrow function.
|
||||
|
@ -1457,11 +1458,12 @@ module ts {
|
|||
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
|
||||
|
||||
// If we have an arrow, then try to parse the body.
|
||||
if (parseExpected(SyntaxKind.EqualsGreaterThanToken)) {
|
||||
return parseArrowExpressionTail(pos, sig, /*noIn:*/ false);
|
||||
// Even if not, try to parse if we have an opening brace, just in case we're in an error state.
|
||||
if (parseExpected(SyntaxKind.EqualsGreaterThanToken) || token === SyntaxKind.OpenBraceToken) {
|
||||
return parseArrowExpressionTail(pos, sig, /* noIn: */ false);
|
||||
}
|
||||
// If not, we're probably better off bailing out and returning a bogus function expression.
|
||||
else {
|
||||
// If not, we're probably better off bailing out and returning a bogus function expression.
|
||||
return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /* name */ undefined, sig, createMissingNode());
|
||||
}
|
||||
}
|
||||
|
@ -1477,29 +1479,44 @@ module ts {
|
|||
}
|
||||
}
|
||||
|
||||
// True -> There is definitely a parenthesized arrow function here.
|
||||
// False -> There is definitely *not* a parenthesized arrow function here.
|
||||
// True -> We definitely expect a parenthesized arrow function here.
|
||||
// False -> There *cannot* be a parenthesized arrow function here.
|
||||
// Unknown -> There *might* be a parenthesized arrow function here.
|
||||
// Speculatively look ahead to be sure.
|
||||
// Speculatively look ahead to be sure, and rollback if not.
|
||||
function isParenthesizedArrowFunctionExpression(): Tristate {
|
||||
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
|
||||
return lookAhead(() => {
|
||||
var first = token;
|
||||
nextToken();
|
||||
var second = nextToken();
|
||||
|
||||
if (first === SyntaxKind.OpenParenToken) {
|
||||
if (token === SyntaxKind.CloseParenToken || token === SyntaxKind.DotDotDotToken) {
|
||||
// Simple cases. if we see () or (... then presume that presume
|
||||
// that this must be an arrow function. Note, this may be too aggressive
|
||||
// for the "()" case. It's not uncommon for this to appear while editing
|
||||
// code. We should look to see if there's actually a => before proceeding.
|
||||
if (second === SyntaxKind.CloseParenToken) {
|
||||
// Simple cases: "() =>", "(): ", and "() {".
|
||||
// This is an arrow function with no parameters.
|
||||
// The last one is not actually an arrow function,
|
||||
// but this is probably what the user intended.
|
||||
var third = nextToken();
|
||||
switch (third) {
|
||||
case SyntaxKind.EqualsGreaterThanToken:
|
||||
case SyntaxKind.ColonToken:
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return Tristate.True;
|
||||
default:
|
||||
return Tristate.False;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple case: "(..."
|
||||
// This is an arrow function with a rest parameter.
|
||||
if (second === SyntaxKind.DotDotDotToken) {
|
||||
return Tristate.True;
|
||||
}
|
||||
|
||||
// We had "(" not followed by an identifier. 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,
|
||||
// but we could provide a good error message instead of bailing out.
|
||||
if (!isIdentifier()) {
|
||||
// We had "(" not followed by an identifier. 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,
|
||||
// but we could provide a good error message instead of bailing out.
|
||||
return Tristate.False;
|
||||
}
|
||||
|
||||
|
|
145
tests/baselines/reference/arrowFunctionsMissingTokens.errors.txt
Normal file
145
tests/baselines/reference/arrowFunctionsMissingTokens.errors.txt
Normal file
|
@ -0,0 +1,145 @@
|
|||
==== tests/cases/compiler/arrowFunctionsMissingTokens.ts (39 errors) ====
|
||||
|
||||
module missingArrowsWithCurly {
|
||||
var a = () { };
|
||||
~
|
||||
!!! '=>' expected.
|
||||
|
||||
var b = (): void { }
|
||||
~
|
||||
!!! '=>' expected.
|
||||
|
||||
var c = (x) { };
|
||||
~
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! Cannot find name 'x'.
|
||||
|
||||
var d = (x: number, y: string) { };
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! Variable declaration expected.
|
||||
~
|
||||
!!! Cannot find name 'x'.
|
||||
|
||||
var e = (x: number, y: string): void { };
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! Variable declaration expected.
|
||||
~~~~
|
||||
!!! Variable declaration expected.
|
||||
~
|
||||
!!! Cannot find name 'x'.
|
||||
}
|
||||
|
||||
module missingCurliesWithArrow {
|
||||
module withStatement {
|
||||
var a = () => var k = 10;};
|
||||
~~~
|
||||
!!! '{' expected.
|
||||
|
||||
var b = (): void => var k = 10;}
|
||||
~~~
|
||||
!!! '{' expected.
|
||||
|
||||
var c = (x) => var k = 10;};
|
||||
~~~
|
||||
!!! '{' expected.
|
||||
|
||||
var d = (x: number, y: string) => var k = 10;};
|
||||
~~~
|
||||
!!! '{' expected.
|
||||
|
||||
var e = (x: number, y: string): void => var k = 10;};
|
||||
~~~
|
||||
!!! '{' expected.
|
||||
|
||||
var f = () => var k = 10;}
|
||||
~~~
|
||||
!!! '{' expected.
|
||||
}
|
||||
|
||||
module withoutStatement {
|
||||
var a = () => };
|
||||
~
|
||||
!!! Expression expected.
|
||||
|
||||
var b = (): void => }
|
||||
~
|
||||
!!! Expression expected.
|
||||
|
||||
var c = (x) => };
|
||||
~
|
||||
!!! Expression expected.
|
||||
|
||||
var d = (x: number, y: string) => };
|
||||
~
|
||||
!!! Expression expected.
|
||||
|
||||
var e = (x: number, y: string): void => };
|
||||
~
|
||||
!!! Expression expected.
|
||||
|
||||
var f = () => }
|
||||
~
|
||||
!!! Expression expected.
|
||||
}
|
||||
~
|
||||
!!! Declaration or statement expected.
|
||||
}
|
||||
~
|
||||
!!! Declaration or statement expected.
|
||||
|
||||
module ceci_nEst_pas_une_arrow_function {
|
||||
var a = ();
|
||||
~
|
||||
!!! Expression expected.
|
||||
|
||||
var b = (): void;
|
||||
~
|
||||
!!! '=>' expected.
|
||||
|
||||
var c = (x);
|
||||
~
|
||||
!!! Cannot find name 'x'.
|
||||
|
||||
var d = (x: number, y: string);
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! Cannot find name 'x'.
|
||||
|
||||
var e = (x: number, y: string): void;
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! Variable declaration expected.
|
||||
~~~~
|
||||
!!! Variable declaration expected.
|
||||
~
|
||||
!!! Expression expected.
|
||||
~
|
||||
!!! Cannot find name 'x'.
|
||||
}
|
||||
|
||||
module okay {
|
||||
var a = () => { };
|
||||
|
||||
var b = (): void => { }
|
||||
|
||||
var c = (x) => { };
|
||||
|
||||
var d = (x: number, y: string) => { };
|
||||
|
||||
var e = (x: number, y: string): void => { };
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
==== tests/cases/compiler/emptyMemberAccess.ts (2 errors) ====
|
||||
==== tests/cases/compiler/emptyMemberAccess.ts (1 errors) ====
|
||||
function getObj() {
|
||||
|
||||
().toString();
|
||||
~
|
||||
!!! '=>' expected.
|
||||
~~~~~~~~
|
||||
!!! Cannot find name 'toString'.
|
||||
~
|
||||
!!! Expression expected.
|
||||
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
declare class foo();
|
||||
~
|
||||
!!! '{' expected.
|
||||
~
|
||||
!!! '=>' expected.
|
||||
~
|
||||
!!! Expression expected.
|
||||
function foo() {}
|
||||
~~~
|
||||
!!! Duplicate identifier 'foo'.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
==== tests/cases/conformance/parser/ecmascript5/RegressionTests/parser566700.ts (1 errors) ====
|
||||
var v = ()({});
|
||||
~
|
||||
!!! '=>' expected.
|
||||
~
|
||||
!!! Expression expected.
|
|
@ -1,8 +1,6 @@
|
|||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserEmptyParenthesizedExpression1.ts (2 errors) ====
|
||||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserEmptyParenthesizedExpression1.ts (1 errors) ====
|
||||
function getObj() {
|
||||
().toString();
|
||||
~
|
||||
!!! '=>' expected.
|
||||
~~~~~~~~
|
||||
!!! Cannot find name 'toString'.
|
||||
~
|
||||
!!! Expression expected.
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/Expressions/parserErrorRecovery_Expression1.ts (1 errors) ====
|
||||
var v = ()({});
|
||||
~
|
||||
!!! '=>' expected.
|
||||
~
|
||||
!!! Expression expected.
|
|
@ -1,9 +1,7 @@
|
|||
==== tests/cases/compiler/uncaughtCompilerError2.ts (2 errors) ====
|
||||
==== tests/cases/compiler/uncaughtCompilerError2.ts (1 errors) ====
|
||||
function getObj() {
|
||||
().toString();
|
||||
~
|
||||
!!! '=>' expected.
|
||||
~~~~~~~~
|
||||
!!! Cannot find name 'toString'.
|
||||
~
|
||||
!!! Expression expected.
|
||||
}
|
||||
|
66
tests/cases/compiler/arrowFunctionsMissingTokens.ts
Normal file
66
tests/cases/compiler/arrowFunctionsMissingTokens.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
module missingArrowsWithCurly {
|
||||
var a = () { };
|
||||
|
||||
var b = (): void { }
|
||||
|
||||
var c = (x) { };
|
||||
|
||||
var d = (x: number, y: string) { };
|
||||
|
||||
var e = (x: number, y: string): void { };
|
||||
}
|
||||
|
||||
module missingCurliesWithArrow {
|
||||
module withStatement {
|
||||
var a = () => var k = 10;};
|
||||
|
||||
var b = (): void => var k = 10;}
|
||||
|
||||
var c = (x) => var k = 10;};
|
||||
|
||||
var d = (x: number, y: string) => var k = 10;};
|
||||
|
||||
var e = (x: number, y: string): void => var k = 10;};
|
||||
|
||||
var f = () => var k = 10;}
|
||||
}
|
||||
|
||||
module withoutStatement {
|
||||
var a = () => };
|
||||
|
||||
var b = (): void => }
|
||||
|
||||
var c = (x) => };
|
||||
|
||||
var d = (x: number, y: string) => };
|
||||
|
||||
var e = (x: number, y: string): void => };
|
||||
|
||||
var f = () => }
|
||||
}
|
||||
}
|
||||
|
||||
module ceci_nEst_pas_une_arrow_function {
|
||||
var a = ();
|
||||
|
||||
var b = (): void;
|
||||
|
||||
var c = (x);
|
||||
|
||||
var d = (x: number, y: string);
|
||||
|
||||
var e = (x: number, y: string): void;
|
||||
}
|
||||
|
||||
module okay {
|
||||
var a = () => { };
|
||||
|
||||
var b = (): void => { }
|
||||
|
||||
var c = (x) => { };
|
||||
|
||||
var d = (x: number, y: string) => { };
|
||||
|
||||
var e = (x: number, y: string): void => { };
|
||||
}
|
Loading…
Reference in a new issue