diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0304586bbca2f..2798ace3a208a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -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: "() =>", "(): " "() {". + // 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; } diff --git a/tests/baselines/reference/arrowFunctionsMissingTokens.errors.txt b/tests/baselines/reference/arrowFunctionsMissingTokens.errors.txt new file mode 100644 index 0000000000000..b9d139dba25e9 --- /dev/null +++ b/tests/baselines/reference/arrowFunctionsMissingTokens.errors.txt @@ -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 => { }; + } \ No newline at end of file diff --git a/tests/baselines/reference/emptyMemberAccess.errors.txt b/tests/baselines/reference/emptyMemberAccess.errors.txt index 122c3957554c6..19c24099d948b 100644 --- a/tests/baselines/reference/emptyMemberAccess.errors.txt +++ b/tests/baselines/reference/emptyMemberAccess.errors.txt @@ -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. } \ No newline at end of file diff --git a/tests/baselines/reference/es6ClassTest9.errors.txt b/tests/baselines/reference/es6ClassTest9.errors.txt index f5856c71918cc..1b8be82fc9dde 100644 --- a/tests/baselines/reference/es6ClassTest9.errors.txt +++ b/tests/baselines/reference/es6ClassTest9.errors.txt @@ -2,8 +2,8 @@ declare class foo(); ~ !!! '{' expected. - ~ -!!! '=>' expected. + ~ +!!! Expression expected. function foo() {} ~~~ !!! Duplicate identifier 'foo'. diff --git a/tests/baselines/reference/parser566700.errors.txt b/tests/baselines/reference/parser566700.errors.txt index 63093a1b5dfda..ed2ca730caadf 100644 --- a/tests/baselines/reference/parser566700.errors.txt +++ b/tests/baselines/reference/parser566700.errors.txt @@ -1,4 +1,4 @@ ==== tests/cases/conformance/parser/ecmascript5/RegressionTests/parser566700.ts (1 errors) ==== var v = ()({}); - ~ -!!! '=>' expected. \ No newline at end of file + ~ +!!! Expression expected. \ No newline at end of file diff --git a/tests/baselines/reference/parserEmptyParenthesizedExpression1.errors.txt b/tests/baselines/reference/parserEmptyParenthesizedExpression1.errors.txt index d84b0a9755a3b..c65700bc5e204 100644 --- a/tests/baselines/reference/parserEmptyParenthesizedExpression1.errors.txt +++ b/tests/baselines/reference/parserEmptyParenthesizedExpression1.errors.txt @@ -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. } \ No newline at end of file diff --git a/tests/baselines/reference/parserErrorRecovery_Expression1.errors.txt b/tests/baselines/reference/parserErrorRecovery_Expression1.errors.txt index e8f6af701af6e..6e6a450ec0295 100644 --- a/tests/baselines/reference/parserErrorRecovery_Expression1.errors.txt +++ b/tests/baselines/reference/parserErrorRecovery_Expression1.errors.txt @@ -1,4 +1,4 @@ ==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/Expressions/parserErrorRecovery_Expression1.ts (1 errors) ==== var v = ()({}); - ~ -!!! '=>' expected. \ No newline at end of file + ~ +!!! Expression expected. \ No newline at end of file diff --git a/tests/baselines/reference/uncaughtCompilerError2.errors.txt b/tests/baselines/reference/uncaughtCompilerError2.errors.txt index 70395e04bdd20..455e047034f87 100644 --- a/tests/baselines/reference/uncaughtCompilerError2.errors.txt +++ b/tests/baselines/reference/uncaughtCompilerError2.errors.txt @@ -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. } \ No newline at end of file diff --git a/tests/cases/compiler/arrowFunctionsMissingTokens.ts b/tests/cases/compiler/arrowFunctionsMissingTokens.ts new file mode 100644 index 0000000000000..0e1e693180774 --- /dev/null +++ b/tests/cases/compiler/arrowFunctionsMissingTokens.ts @@ -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 => { }; +} \ No newline at end of file