Merge pull request #203 from Microsoft/arrowLookAhead
Improved lookahead for arrow functions.
This commit is contained in:
commit
c625cd9259
|
@ -1637,64 +1637,87 @@ module ts {
|
|||
function tryParseParenthesizedArrowFunctionExpression(): Expression {
|
||||
var pos = getNodePos();
|
||||
|
||||
// Indicates whether we are certain that we should parse an arrow expression.
|
||||
var triState = isParenthesizedArrowFunctionExpression();
|
||||
|
||||
// It is not a parenthesized arrow function.
|
||||
if (triState === Tristate.False) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If we're certain that we have an arrow function expression, then just parse one out.
|
||||
if (triState === Tristate.True) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, *maybe* we had an arrow function and we need to *try* to parse it out
|
||||
// (which will ensure we rollback if we fail).
|
||||
var sig = tryParse(parseSignatureAndArrow);
|
||||
if (sig === undefined) {
|
||||
return undefined;
|
||||
// *Maybe* we had an arrow function and we need to try to parse it out,
|
||||
// rolling back and trying other parses if we fail.
|
||||
var sig = tryParseSignatureIfArrowOrBraceFollows();
|
||||
if (sig) {
|
||||
parseExpected(SyntaxKind.EqualsGreaterThanToken);
|
||||
return parseArrowExpressionTail(pos, sig, /*noIn:*/ false);
|
||||
}
|
||||
else {
|
||||
return parseArrowExpressionTail(pos, sig, /*noIn:*/ false);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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,
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// This *could* be a parenthesized arrow function.
|
||||
// Return Unknown to let the caller know.
|
||||
return Tristate.Unknown;
|
||||
|
@ -1718,10 +1741,24 @@ module ts {
|
|||
return Tristate.False;
|
||||
}
|
||||
|
||||
function parseSignatureAndArrow(): ParsedSignature {
|
||||
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
|
||||
parseExpected(SyntaxKind.EqualsGreaterThanToken);
|
||||
return sig;
|
||||
function tryParseSignatureIfArrowOrBraceFollows(): ParsedSignature {
|
||||
return tryParse(() => {
|
||||
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
|
||||
|
||||
// Parsing a signature isn't enough.
|
||||
// Parenthesized arrow signatures often look like other valid expressions.
|
||||
// For instance:
|
||||
// - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value.
|
||||
// - "(x,y)" is a comma expression parsed as a signature with two parameters.
|
||||
// - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation.
|
||||
//
|
||||
// So we need just a bit of lookahead to ensure that it can only be a signature.
|
||||
if (token === SyntaxKind.EqualsGreaterThanToken || token === SyntaxKind.OpenBraceToken) {
|
||||
return sig;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function parseArrowExpressionTail(pos: number, sig: ParsedSignature, noIn: boolean): FunctionExpression {
|
||||
|
|
115
tests/baselines/reference/arrowFunctionsMissingTokens.errors.txt
Normal file
115
tests/baselines/reference/arrowFunctionsMissingTokens.errors.txt
Normal file
|
@ -0,0 +1,115 @@
|
|||
==== tests/cases/compiler/arrowFunctionsMissingTokens.ts (24 errors) ====
|
||||
|
||||
module missingArrowsWithCurly {
|
||||
var a = () { };
|
||||
~
|
||||
!!! '=>' expected.
|
||||
|
||||
var b = (): void { }
|
||||
~
|
||||
!!! '=>' expected.
|
||||
|
||||
var c = (x) { };
|
||||
~
|
||||
!!! '=>' expected.
|
||||
|
||||
var d = (x: number, y: string) { };
|
||||
~
|
||||
!!! '=>' expected.
|
||||
|
||||
var e = (x: number, y: string): void { };
|
||||
~
|
||||
!!! '=>' expected.
|
||||
}
|
||||
|
||||
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 ce_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.
|
||||
|
||||
var e = (x: number, y: string): void;
|
||||
~
|
||||
!!! '=>' expected.
|
||||
}
|
||||
|
||||
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/compiler/fatarrowfunctionsErrors.ts (25 errors) ====
|
||||
==== tests/cases/compiler/fatarrowfunctionsErrors.ts (18 errors) ====
|
||||
foo((...Far:any[])=>{return 0;})
|
||||
~~~
|
||||
!!! Cannot find name 'foo'.
|
||||
|
@ -39,25 +39,11 @@
|
|||
~
|
||||
!!! '=>' expected.
|
||||
var x2 = (a:number) :void {};
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! Variable declaration expected.
|
||||
~~~~
|
||||
!!! Variable declaration expected.
|
||||
~
|
||||
!!! Cannot find name 'a'.
|
||||
~
|
||||
!!! '=>' expected.
|
||||
var x3 = (a:number) {};
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! Variable declaration expected.
|
||||
~
|
||||
!!! Cannot find name 'a'.
|
||||
!!! '=>' expected.
|
||||
var x4= (...a: any[]) { };
|
||||
~
|
||||
!!! '=>' expected.
|
|
@ -1,4 +1,4 @@
|
|||
==== tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts (5 errors) ====
|
||||
==== tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts (4 errors) ====
|
||||
function fn() {
|
||||
try {
|
||||
} catch { // syntax error, missing '(x)'
|
||||
|
@ -10,9 +10,7 @@
|
|||
~~~~~
|
||||
!!! Statement expected.
|
||||
~
|
||||
!!! ';' expected.
|
||||
~
|
||||
!!! Cannot find name 'x'.
|
||||
!!! '=>' expected.
|
||||
|
||||
finally{ } // error missing try
|
||||
~~~~~~~
|
||||
|
|
|
@ -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,10 +1,6 @@
|
|||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList5.ts (4 errors) ====
|
||||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList5.ts (2 errors) ====
|
||||
(a:number => { }
|
||||
~
|
||||
!!! ')' expected.
|
||||
~~
|
||||
!!! ';' expected.
|
||||
~
|
||||
!!! Cannot find name 'a'.
|
||||
~~~~~~
|
||||
!!! Cannot find name 'number'.
|
||||
!!! ',' expected.
|
||||
~
|
||||
!!! ')' expected.
|
|
@ -1,4 +1,4 @@
|
|||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts (23 errors) ====
|
||||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts (15 errors) ====
|
||||
declare module ng {
|
||||
interfaceICompiledExpression {
|
||||
~
|
||||
|
@ -6,24 +6,8 @@
|
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! Cannot find name 'interfaceICompiledExpression'.
|
||||
(context: any, locals?: any): any;
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! Expression expected.
|
||||
~
|
||||
!!! ';' expected.
|
||||
~
|
||||
!!! Statement expected.
|
||||
~~~~~~~
|
||||
!!! Cannot find name 'context'.
|
||||
~~~
|
||||
!!! Cannot find name 'any'.
|
||||
~~~~~~
|
||||
!!! Cannot find name 'locals'.
|
||||
~~~
|
||||
!!! Cannot find name 'any'.
|
||||
~~~
|
||||
!!! Cannot find name 'any'.
|
||||
~
|
||||
!!! '=>' expected.
|
||||
assign(context: any, value: any): any;
|
||||
~
|
||||
!!! ',' expected.
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction1.ts (4 errors) ====
|
||||
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction1.ts (1 errors) ====
|
||||
var v = (a: ) => {
|
||||
~
|
||||
!!! ')' expected.
|
||||
~
|
||||
!!! Variable declaration expected.
|
||||
~~
|
||||
!!! ';' expected.
|
||||
~
|
||||
!!! Cannot find name 'a'.
|
||||
!!! Type 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 ce_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