From d796bf1e0a30e899e52d3355b219d22c4875256f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2014 08:53:45 -0800 Subject: [PATCH 1/6] Improve the flexibility of speculative parsing. We now no longer fail a speculative parse just because an error was encountered at any point while speculating. instead, we allow the speculative function that is being called to determine if the parse was successful or not. Only if it decides it was not successful is parsing rewound. This improves our error recovery in several cases (esp. around arrow functions). it will also help in a followup refactoring to prevent lookahead/speculative parsing from causing lambda allocations. --- src/compiler/parser.ts | 177 ++++++++++-------- .../reference/ArrowFunction3.errors.txt | 15 +- .../reference/TypeArgumentList1.errors.txt | 13 +- .../genericCallWithoutArgs.errors.txt | 16 +- .../genericCallsWithoutParens.errors.txt | 16 +- ...cConstructExpressionWithoutArgs.errors.txt | 9 +- ...icObjectCreationWithoutTypeArgs.errors.txt | 9 +- .../newOperatorErrorCases.errors.txt | 9 +- .../parserConstructorAmbiguity3.errors.txt | 9 +- .../parserMemberAccessExpression1.errors.txt | 28 +-- ...erMemberAccessOffOfGenericType1.errors.txt | 15 +- .../parserX_ArrowFunction3.errors.txt | 15 +- .../parserX_TypeArgumentList1.errors.txt | 13 +- 13 files changed, 165 insertions(+), 179 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 58c715d721f..758b4253ddc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -869,12 +869,6 @@ module ts { } }; - const enum LookAheadMode { - NotLookingAhead, - NoErrorYet, - Error - } - export interface ReferencePathMatchResult { fileReference?: FileReference diagnosticMessage?: DiagnosticMessage @@ -960,8 +954,6 @@ module ts { var nodeCount = 0; var lineStarts: number[]; - var lookAheadMode = LookAheadMode.NotLookingAhead; - // Flags that dictate what parsing context we're in. For example: // Whether or not we are in strict parsing mode. All that changes in strict parsing mode is // that some tokens that would be considered identifiers may be considered keywords. When @@ -1125,10 +1117,6 @@ module ts { var diagnostic = createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2); sourceFile.parseDiagnostics.push(diagnostic); } - - if (lookAheadMode === LookAheadMode.NoErrorYet) { - lookAheadMode = LookAheadMode.Error; - } } function scanError(message: DiagnosticMessage) { @@ -1172,29 +1160,14 @@ module ts { // Keep track of the state we'll need to rollback to if lookahead fails (or if the // caller asked us to always reset our state). var saveToken = token; - var saveSyntacticErrorsLength = sourceFile.parseDiagnostics.length; + var saveParseDiagnosticsLength = sourceFile.parseDiagnostics.length; - // Keep track of the current look ahead mode (this matters if we have nested - // speculative parsing). - var saveLookAheadMode = lookAheadMode; - - // Mark that we're in speculative parsing and then try to parse out whatever code - // the callback wants. - lookAheadMode = LookAheadMode.NoErrorYet; var result = callback(); - // If we switched from 1 to -1 then a parse error occurred during the callback. - // If that's the case, then we want to act as if we never got any result at all. - Debug.assert(lookAheadMode === LookAheadMode.Error || lookAheadMode === LookAheadMode.NoErrorYet); - if (lookAheadMode === LookAheadMode.Error) { - result = undefined; - } - // Now restore as appropriate. - lookAheadMode = saveLookAheadMode; if (!result || alwaysResetState) { token = saveToken; - sourceFile.parseDiagnostics.length = saveSyntacticErrorsLength; + sourceFile.parseDiagnostics.length = saveParseDiagnosticsLength; } return result; @@ -1813,7 +1786,7 @@ module ts { var node = createNode(SyntaxKind.TypeReference); node.typeName = parseEntityName(/*allowReservedWords*/ false, Diagnostics.Type_expected); if (!scanner.hasPrecedingLineBreak() && token === SyntaxKind.LessThanToken) { - node.typeArguments = parseTypeArguments(); + node.typeArguments = parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); } return finishNode(node); } @@ -1927,10 +1900,11 @@ module ts { function fillSignature( returnToken: SyntaxKind, yieldAndGeneratorParameterContext: boolean, + requireCompleteParameterList: boolean, signature: SignatureDeclaration): void { var returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken; signature.typeParameters = parseTypeParameters(); - signature.parameters = parseParameterList(yieldAndGeneratorParameterContext); + signature.parameters = parseParameterList(yieldAndGeneratorParameterContext, requireCompleteParameterList); if (returnTokenRequired) { parseExpected(returnToken); @@ -1945,7 +1919,7 @@ module ts { // have 'Yield' And 'GeneratorParameter' not in sync. i.e. any production calling // this FormalParameters production either always sets both to true, or always sets // both to false. As such we only have a single parameter to represent both. - function parseParameterList(yieldAndGeneratorParameterContext: boolean) { + function parseParameterList(yieldAndGeneratorParameterContext: boolean, requireCompleteParameterList: boolean) { // FormalParameters[Yield,GeneratorParameter] : // ... // @@ -1968,15 +1942,20 @@ module ts { setGeneratorParameterContext(yieldAndGeneratorParameterContext); var result = parseDelimitedList(ParsingContext.Parameters, parseParameter); - parseExpected(SyntaxKind.CloseParenToken); setYieldContext(savedYieldContext); setGeneratorParameterContext(savedGeneratorParameterContext); + if (!parseExpected(SyntaxKind.CloseParenToken) && requireCompleteParameterList) { + // Caller insisted that we had to end with a ) We didn't. So just return + // undefined here. + return undefined; + } + return result; } - return createMissingList(); + return requireCompleteParameterList ? undefined : createMissingList(); } function parseSignatureMember(kind: SyntaxKind): SignatureDeclaration { @@ -1984,7 +1963,7 @@ module ts { if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); parseSemicolon(); return finishNode(node); } @@ -2073,7 +2052,7 @@ module ts { // Method signatues don't exist in expression contexts. So they have neither // [Yield] nor [GeneratorParameter] - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, method); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, method); parseSemicolon(); return finishNode(method); @@ -2174,7 +2153,7 @@ module ts { if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldAndGeneratorParameterContext:*/ false, node); + fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); return finishNode(node); } @@ -2671,7 +2650,18 @@ module ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): FunctionExpression { var node = createNode(SyntaxKind.ArrowFunction); // Arrow functions are never generators. - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, node); + // + // If we're speculatively parsing a signature for a parenthesized arrow function, then + // we have to have a complete parameter list. Otherwise we might see something like + // a => (b => c) + // And think that "(b =>" was actually a parenthesized arrow function with a missing + // close paren. + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ !allowAmbiguity, node); + + // If we couldn't get parameters, we definitely could not parse out an arrow function. + if (!node.parameters) { + return undefined; + } // Parsing a signature isn't enough. // Parenthesized arrow signatures often look like other valid expressions. @@ -3042,8 +3032,11 @@ module ts { expression = parseMemberExpressionRest(expression); if (token === SyntaxKind.LessThanToken) { - // Might be arithmetic, or it might be a type argument list. - var typeArguments = tryParse(parseTypeArgumentsAndOpenParen); + // See if this is the start of a generic invocation. If so, consume it and + // keep checking for postfix expressions. Otherwise, it's just a '<' that's + // part of an arithmetic expression. Break out so we consume it higher in the + // stack. + var typeArguments = tryParse(parseTypeArgumentsInExpression); if (!typeArguments) { return expression; } @@ -3051,18 +3044,16 @@ module ts { var callExpr = createNode(SyntaxKind.CallExpression, expression.pos); callExpr.expression = expression; callExpr.typeArguments = typeArguments; - callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); - parseExpected(SyntaxKind.CloseParenToken); + if (callExpr.typeArguments || token === SyntaxKind.OpenParenToken) { + callExpr.arguments = parseArgumentList(); + } expression = finishNode(callExpr); continue; } - - if (token === SyntaxKind.OpenParenToken) { + else if (token === SyntaxKind.OpenParenToken) { var callExpr = createNode(SyntaxKind.CallExpression, expression.pos); callExpr.expression = expression; - parseExpected(SyntaxKind.OpenParenToken); - callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); - parseExpected(SyntaxKind.CloseParenToken); + callExpr.arguments = parseArgumentList(); expression = finishNode(callExpr); continue; } @@ -3071,33 +3062,64 @@ module ts { } } - function parseTypeArgumentsAndOpenParen(): NodeArray { - var result = parseTypeArguments(); + function parseArgumentList() { parseExpected(SyntaxKind.OpenParenToken); + var result = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); + parseExpected(SyntaxKind.CloseParenToken); return result; } - function parseTypeArguments(): NodeArray { - // We pass parseSingleTypeArgument instead of parseType as the element parser - // because parseSingleTypeArgument knows how to parse a missing type argument. - // This is useful for signature help. parseType has the disadvantage that when - // it sees a missing type, it changes the LookAheadMode to Error, and the result - // is a broken binary expression, which breaks signature help. - return parseBracketedList(ParsingContext.TypeArguments, parseSingleTypeArgument, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); - } - - function parseSingleTypeArgument(): TypeNode { - // Be resilient to something like: Foo<,,>(); - // We want to parse this out as a type argument list (esp. for signature help), and we - // don't want to rollback just because we were missing a type arg. The grammar checker - // will report the actual error later on. - if (token === SyntaxKind.CommaToken) { - var result = createNode(SyntaxKind.TypeReference); - result.typeName = createMissingNodeWithoutError(SyntaxKind.Identifier); - return finishNode(result); + function parseTypeArgumentsInExpression() { + if (!parseOptional(SyntaxKind.LessThanToken)) { + return undefined; } - return parseType(); + var typeArguments = parseDelimitedList(ParsingContext.TypeArguments, parseType); + if (!parseExpected(SyntaxKind.GreaterThanToken)) { + // If it doesn't have the closing > then it's definitely not an type argument list. + return undefined; + } + + // If we have a '<', then only parse this as a arugment list if the type arguments + // are complete and we have an open paren. if we don't, rewind and return nothing. + return typeArguments && canFollowTypeArgumentsInExpression() + ? typeArguments + : undefined; + } + + function canFollowTypeArgumentsInExpression(): boolean { + switch (token) { + case SyntaxKind.OpenParenToken: // foo( + case SyntaxKind.DotToken: // foo. + // These two cases are the only cases where this token can legally follow a + // type argument list. So we definitely want to treat this as a type arg list. + + case SyntaxKind.CloseParenToken: // foo) + case SyntaxKind.CloseBracketToken: // foo] + case SyntaxKind.ColonToken: // foo: + case SyntaxKind.SemicolonToken: // foo; + case SyntaxKind.CommaToken: // foo, + case SyntaxKind.QuestionToken: // foo? + case SyntaxKind.EqualsEqualsToken: // foo == + case SyntaxKind.EqualsEqualsEqualsToken: // foo === + case SyntaxKind.ExclamationEqualsToken: // foo != + case SyntaxKind.ExclamationEqualsEqualsToken: // foo !== + case SyntaxKind.AmpersandAmpersandToken: // foo && + case SyntaxKind.BarBarToken: // foo || + case SyntaxKind.CaretToken: // foo ^ + case SyntaxKind.AmpersandToken: // foo & + case SyntaxKind.BarToken: // foo | + case SyntaxKind.CloseBraceToken: // foo } + case SyntaxKind.EndOfFileToken: // foo + // these cases can't legally follow a type arg list. However, they're not legal + // expressions either. The user is probably in the middle of a generic type. So + // treat it as such. + return true; + + default: + // Anything else treat as an expression. + return false; + } } function parsePrimaryExpression(): PrimaryExpression { @@ -3224,7 +3246,7 @@ module ts { parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); node.name = node.asteriskToken ? doInYieldContext(parseOptionalIdentifier) : parseOptionalIdentifier(); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, /*requireCompleteParameterList:*/ false, node); node.body = parseFunctionBlock(/*allowYield:*/ !!node.asteriskToken, /* ignoreMissingOpenBrace */ false); return finishNode(node); } @@ -3237,14 +3259,11 @@ module ts { var node = createNode(SyntaxKind.NewExpression); parseExpected(SyntaxKind.NewKeyword); node.expression = parseMemberExpressionOrHigher(); - if (token === SyntaxKind.LessThanToken) { - node.typeArguments = tryParse(parseTypeArgumentsAndOpenParen); + node.typeArguments = tryParse(parseTypeArgumentsInExpression); + if (node.typeArguments || token === SyntaxKind.OpenParenToken) { + node.arguments = parseArgumentList(); } - if (node.typeArguments || parseOptional(SyntaxKind.OpenParenToken)) { - node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); - parseExpected(SyntaxKind.CloseParenToken); - } return finishNode(node); } @@ -3699,7 +3718,7 @@ module ts { parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); node.name = parseIdentifier(); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, /*requireCompleteParameterList:*/ false, node); node.body = parseFunctionBlockOrSemicolon(!!node.asteriskToken); return finishNode(node); } @@ -3708,7 +3727,7 @@ module ts { var node = createNode(SyntaxKind.Constructor, pos); setModifiers(node, modifiers); parseExpected(SyntaxKind.ConstructorKeyword); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); node.body = parseFunctionBlockOrSemicolon(/*isGenerator:*/ false); return finishNode(node); } @@ -3719,7 +3738,7 @@ module ts { method.asteriskToken = asteriskToken; method.name = name; method.questionToken = questionToken; - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!asteriskToken, method); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!asteriskToken, /*requireCompleteParameterList:*/ false, method); method.body = requireBlock ? parseFunctionBlock(!!asteriskToken, /*ignoreMissingOpenBrace:*/ false) : parseFunctionBlockOrSemicolon(!!asteriskToken); return finishNode(method); } @@ -3754,7 +3773,7 @@ module ts { var node = createNode(kind, fullStart); setModifiers(node, modifiers); node.name = parsePropertyName(); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); node.body = parseFunctionBlockOrSemicolon(/*isGenerator:*/ false); return finishNode(node); } diff --git a/tests/baselines/reference/ArrowFunction3.errors.txt b/tests/baselines/reference/ArrowFunction3.errors.txt index be20d77c343..a1287eca881 100644 --- a/tests/baselines/reference/ArrowFunction3.errors.txt +++ b/tests/baselines/reference/ArrowFunction3.errors.txt @@ -1,15 +1,12 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,12): error TS1005: ',' expected. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,14): error TS1005: ';' expected. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,10): error TS2304: Cannot find name 'a'. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,14): error TS1110: Type expected. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,13): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value or consist of a single 'throw' statement. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts (3 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts (2 errors) ==== var v = (a): => { - ~ -!!! error TS1005: ',' expected. ~~ -!!! error TS1005: ';' expected. - ~ -!!! error TS2304: Cannot find name 'a'. +!!! error TS1110: Type expected. + +!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value or consist of a single 'throw' statement. }; \ No newline at end of file diff --git a/tests/baselines/reference/TypeArgumentList1.errors.txt b/tests/baselines/reference/TypeArgumentList1.errors.txt index 4337f520c6d..16550c3ed37 100644 --- a/tests/baselines/reference/TypeArgumentList1.errors.txt +++ b/tests/baselines/reference/TypeArgumentList1.errors.txt @@ -1,19 +1,10 @@ tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts(1,9): error TS1127: Invalid character. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts(1,1): error TS2304: Cannot find name 'Foo'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts(1,5): error TS2304: Cannot find name 'A'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts(1,7): error TS2304: Cannot find name 'B'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts(1,11): error TS2304: Cannot find name 'C'. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts (5 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts (2 errors) ==== Foo(4, 5, 6); !!! error TS1127: Invalid character. ~~~ -!!! error TS2304: Cannot find name 'Foo'. - ~ -!!! error TS2304: Cannot find name 'A'. - ~ -!!! error TS2304: Cannot find name 'B'. - ~ -!!! error TS2304: Cannot find name 'C'. \ No newline at end of file +!!! error TS2304: Cannot find name 'Foo'. \ No newline at end of file diff --git a/tests/baselines/reference/genericCallWithoutArgs.errors.txt b/tests/baselines/reference/genericCallWithoutArgs.errors.txt index 390fad31389..9d2ca3c1b83 100644 --- a/tests/baselines/reference/genericCallWithoutArgs.errors.txt +++ b/tests/baselines/reference/genericCallWithoutArgs.errors.txt @@ -1,19 +1,13 @@ -tests/cases/compiler/genericCallWithoutArgs.ts(4,17): error TS1109: Expression expected. -tests/cases/compiler/genericCallWithoutArgs.ts(4,18): error TS1003: Identifier expected. -tests/cases/compiler/genericCallWithoutArgs.ts(4,3): error TS2304: Cannot find name 'number'. -tests/cases/compiler/genericCallWithoutArgs.ts(4,10): error TS2304: Cannot find name 'string'. +tests/cases/compiler/genericCallWithoutArgs.ts(4,17): error TS1005: '(' expected. +tests/cases/compiler/genericCallWithoutArgs.ts(4,18): error TS1005: ')' expected. -==== tests/cases/compiler/genericCallWithoutArgs.ts (4 errors) ==== +==== tests/cases/compiler/genericCallWithoutArgs.ts (2 errors) ==== function f(x: X, y: Y) { } f. ~ -!!! error TS1109: Expression expected. +!!! error TS1005: '(' expected. -!!! error TS1003: Identifier expected. - ~~~~~~ -!!! error TS2304: Cannot find name 'number'. - ~~~~~~ -!!! error TS2304: Cannot find name 'string'. \ No newline at end of file +!!! error TS1005: ')' expected. \ No newline at end of file diff --git a/tests/baselines/reference/genericCallsWithoutParens.errors.txt b/tests/baselines/reference/genericCallsWithoutParens.errors.txt index aba66ed545f..9883efb3783 100644 --- a/tests/baselines/reference/genericCallsWithoutParens.errors.txt +++ b/tests/baselines/reference/genericCallsWithoutParens.errors.txt @@ -1,24 +1,18 @@ -tests/cases/compiler/genericCallsWithoutParens.ts(2,18): error TS1109: Expression expected. -tests/cases/compiler/genericCallsWithoutParens.ts(7,22): error TS1109: Expression expected. -tests/cases/compiler/genericCallsWithoutParens.ts(2,11): error TS2304: Cannot find name 'number'. -tests/cases/compiler/genericCallsWithoutParens.ts(7,15): error TS2304: Cannot find name 'number'. +tests/cases/compiler/genericCallsWithoutParens.ts(2,18): error TS1005: '(' expected. +tests/cases/compiler/genericCallsWithoutParens.ts(7,22): error TS1005: '(' expected. -==== tests/cases/compiler/genericCallsWithoutParens.ts (4 errors) ==== +==== tests/cases/compiler/genericCallsWithoutParens.ts (2 errors) ==== function f() { } var r = f; // parse error ~ -!!! error TS1109: Expression expected. - ~~~~~~ -!!! error TS2304: Cannot find name 'number'. +!!! error TS1005: '(' expected. class C { foo: T; } var c = new C; // parse error ~ -!!! error TS1109: Expression expected. - ~~~~~~ -!!! error TS2304: Cannot find name 'number'. +!!! error TS1005: '(' expected. \ No newline at end of file diff --git a/tests/baselines/reference/genericConstructExpressionWithoutArgs.errors.txt b/tests/baselines/reference/genericConstructExpressionWithoutArgs.errors.txt index e89bf106ff4..2f16241aa6c 100644 --- a/tests/baselines/reference/genericConstructExpressionWithoutArgs.errors.txt +++ b/tests/baselines/reference/genericConstructExpressionWithoutArgs.errors.txt @@ -1,8 +1,7 @@ -tests/cases/compiler/genericConstructExpressionWithoutArgs.ts(10,1): error TS1109: Expression expected. -tests/cases/compiler/genericConstructExpressionWithoutArgs.ts(9,16): error TS2304: Cannot find name 'number'. +tests/cases/compiler/genericConstructExpressionWithoutArgs.ts(10,1): error TS1005: '(' expected. -==== tests/cases/compiler/genericConstructExpressionWithoutArgs.ts (2 errors) ==== +==== tests/cases/compiler/genericConstructExpressionWithoutArgs.ts (1 errors) ==== class B { } var b = new B; // no error @@ -12,8 +11,6 @@ tests/cases/compiler/genericConstructExpressionWithoutArgs.ts(9,16): error TS230 var c = new C // C var c2 = new C // error, type params are actually part of the arg list so you need both - ~~~~~~ -!!! error TS2304: Cannot find name 'number'. -!!! error TS1109: Expression expected. \ No newline at end of file +!!! error TS1005: '(' expected. \ No newline at end of file diff --git a/tests/baselines/reference/genericObjectCreationWithoutTypeArgs.errors.txt b/tests/baselines/reference/genericObjectCreationWithoutTypeArgs.errors.txt index 3224d577fd2..825fec7fa72 100644 --- a/tests/baselines/reference/genericObjectCreationWithoutTypeArgs.errors.txt +++ b/tests/baselines/reference/genericObjectCreationWithoutTypeArgs.errors.txt @@ -1,8 +1,7 @@ -tests/cases/compiler/genericObjectCreationWithoutTypeArgs.ts(6,26): error TS1109: Expression expected. -tests/cases/compiler/genericObjectCreationWithoutTypeArgs.ts(6,19): error TS2304: Cannot find name 'number'. +tests/cases/compiler/genericObjectCreationWithoutTypeArgs.ts(6,26): error TS1005: '(' expected. -==== tests/cases/compiler/genericObjectCreationWithoutTypeArgs.ts (2 errors) ==== +==== tests/cases/compiler/genericObjectCreationWithoutTypeArgs.ts (1 errors) ==== class SS{ } @@ -10,9 +9,7 @@ tests/cases/compiler/genericObjectCreationWithoutTypeArgs.ts(6,19): error TS2304 var x1 = new SS(); // OK var x2 = new SS < number>; // Correctly give error ~ -!!! error TS1109: Expression expected. - ~~~~~~ -!!! error TS2304: Cannot find name 'number'. +!!! error TS1005: '(' expected. var x3 = new SS(); // OK var x4 = new SS; // Should be allowed, but currently give error ('supplied parameters do not match any signature of the call target') \ No newline at end of file diff --git a/tests/baselines/reference/newOperatorErrorCases.errors.txt b/tests/baselines/reference/newOperatorErrorCases.errors.txt index 8b94c902e50..11a6beb1d39 100644 --- a/tests/baselines/reference/newOperatorErrorCases.errors.txt +++ b/tests/baselines/reference/newOperatorErrorCases.errors.txt @@ -1,10 +1,9 @@ tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts(27,16): error TS1005: ',' expected. -tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts(32,23): error TS1109: Expression expected. -tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts(32,16): error TS2304: Cannot find name 'string'. +tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts(32,23): error TS1005: '(' expected. tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts(37,9): error TS2350: Only a void function can be called with the 'new' keyword. -==== tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts (4 errors) ==== +==== tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts (3 errors) ==== class C0 { @@ -40,9 +39,7 @@ tests/cases/conformance/expressions/newOperator/newOperatorErrorCases.ts(37,9): var c1: T<{}>; var c2 = new T; // Parse error ~ -!!! error TS1109: Expression expected. - ~~~~~~ -!!! error TS2304: Cannot find name 'string'. +!!! error TS1005: '(' expected. // Construct expression of non-void returning function diff --git a/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt b/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt index 674bb1ff17b..0562bc6c498 100644 --- a/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt +++ b/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt @@ -1,10 +1,13 @@ -tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,12): error TS1109: Expression expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,12): error TS1005: '(' expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,10): error TS2304: Cannot find name 'A'. -==== tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts (3 errors) ==== new Date -!!! error TS1109: Expression expected. +!!! error TS1005: '(' expected. + ~~~~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. ~ !!! error TS2304: Cannot find name 'A'. \ No newline at end of file diff --git a/tests/baselines/reference/parserMemberAccessExpression1.errors.txt b/tests/baselines/reference/parserMemberAccessExpression1.errors.txt index 90980e6fc18..266f84457d3 100644 --- a/tests/baselines/reference/parserMemberAccessExpression1.errors.txt +++ b/tests/baselines/reference/parserMemberAccessExpression1.errors.txt @@ -1,14 +1,16 @@ -tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(3,7): error TS1109: Expression expected. -tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(4,7): error TS1109: Expression expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(3,7): error TS1005: '(' expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(3,13): error TS1005: ')' expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(4,7): error TS1005: '(' expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(4,16): error TS1005: ')' expected. tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(1,1): error TS2304: Cannot find name 'Foo'. tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(2,1): error TS2304: Cannot find name 'Foo'. tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(3,1): error TS2304: Cannot find name 'Foo'. -tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(3,5): error TS2304: Cannot find name 'T'. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(3,8): error TS2304: Cannot find name 'Bar'. tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(4,1): error TS2304: Cannot find name 'Foo'. -tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(4,5): error TS2304: Cannot find name 'T'. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts(4,8): error TS2304: Cannot find name 'Bar'. -==== tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts (8 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression1.ts (10 errors) ==== Foo(); ~~~ !!! error TS2304: Cannot find name 'Foo'. @@ -17,16 +19,20 @@ tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessExpression !!! error TS2304: Cannot find name 'Foo'. Foo.Bar(); ~ -!!! error TS1109: Expression expected. +!!! error TS1005: '(' expected. + ~ +!!! error TS1005: ')' expected. ~~~ !!! error TS2304: Cannot find name 'Foo'. - ~ -!!! error TS2304: Cannot find name 'T'. + ~~~ +!!! error TS2304: Cannot find name 'Bar'. Foo.Bar(); ~ -!!! error TS1109: Expression expected. +!!! error TS1005: '(' expected. + ~ +!!! error TS1005: ')' expected. ~~~ !!! error TS2304: Cannot find name 'Foo'. - ~ -!!! error TS2304: Cannot find name 'T'. + ~~~ +!!! error TS2304: Cannot find name 'Bar'. \ No newline at end of file diff --git a/tests/baselines/reference/parserMemberAccessOffOfGenericType1.errors.txt b/tests/baselines/reference/parserMemberAccessOffOfGenericType1.errors.txt index 29d321e849d..8040ff52bdd 100644 --- a/tests/baselines/reference/parserMemberAccessOffOfGenericType1.errors.txt +++ b/tests/baselines/reference/parserMemberAccessOffOfGenericType1.errors.txt @@ -1,13 +1,16 @@ -tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts(1,21): error TS1109: Expression expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts(1,21): error TS1005: '(' expected. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts(1,33): error TS1005: ')' expected. tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts(1,9): error TS2304: Cannot find name 'List'. -tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts(1,14): error TS2304: Cannot find name 'number'. +tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts(1,22): error TS2304: Cannot find name 'makeChild'. -==== tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts (3 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Generics/parserMemberAccessOffOfGenericType1.ts (4 errors) ==== var v = List.makeChild(); ~ -!!! error TS1109: Expression expected. +!!! error TS1005: '(' expected. + ~ +!!! error TS1005: ')' expected. ~~~~ !!! error TS2304: Cannot find name 'List'. - ~~~~~~ -!!! error TS2304: Cannot find name 'number'. \ No newline at end of file + ~~~~~~~~~ +!!! error TS2304: Cannot find name 'makeChild'. \ No newline at end of file diff --git a/tests/baselines/reference/parserX_ArrowFunction3.errors.txt b/tests/baselines/reference/parserX_ArrowFunction3.errors.txt index 14b37b96ec7..e2be2abc482 100644 --- a/tests/baselines/reference/parserX_ArrowFunction3.errors.txt +++ b/tests/baselines/reference/parserX_ArrowFunction3.errors.txt @@ -1,15 +1,12 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,12): error TS1005: ',' expected. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,14): error TS1005: ';' expected. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,10): error TS2304: Cannot find name 'a'. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,14): error TS1110: Type expected. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,13): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value or consist of a single 'throw' statement. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts (3 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts (2 errors) ==== var v = (a): => { - ~ -!!! error TS1005: ',' expected. ~~ -!!! error TS1005: ';' expected. - ~ -!!! error TS2304: Cannot find name 'a'. +!!! error TS1110: Type expected. + +!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value or consist of a single 'throw' statement. }; \ No newline at end of file diff --git a/tests/baselines/reference/parserX_TypeArgumentList1.errors.txt b/tests/baselines/reference/parserX_TypeArgumentList1.errors.txt index 9ad9d839a27..67fc7e32380 100644 --- a/tests/baselines/reference/parserX_TypeArgumentList1.errors.txt +++ b/tests/baselines/reference/parserX_TypeArgumentList1.errors.txt @@ -1,19 +1,10 @@ tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/parserX_TypeArgumentList1.ts(1,9): error TS1127: Invalid character. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/parserX_TypeArgumentList1.ts(1,1): error TS2304: Cannot find name 'Foo'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/parserX_TypeArgumentList1.ts(1,5): error TS2304: Cannot find name 'A'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/parserX_TypeArgumentList1.ts(1,7): error TS2304: Cannot find name 'B'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/parserX_TypeArgumentList1.ts(1,11): error TS2304: Cannot find name 'C'. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/parserX_TypeArgumentList1.ts (5 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/parserX_TypeArgumentList1.ts (2 errors) ==== Foo(4, 5, 6); !!! error TS1127: Invalid character. ~~~ -!!! error TS2304: Cannot find name 'Foo'. - ~ -!!! error TS2304: Cannot find name 'A'. - ~ -!!! error TS2304: Cannot find name 'B'. - ~ -!!! error TS2304: Cannot find name 'C'. \ No newline at end of file +!!! error TS2304: Cannot find name 'Foo'. \ No newline at end of file From 589e6274f80957eda8009042d9471025c08f0779 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2014 09:04:35 -0800 Subject: [PATCH 2/6] Remove function expression allocation when speculatively parsing or looking ahead. Also, remove unnecessary grammar check now that the previous hack to insert a missing type argument node has been removed. --- src/compiler/parser.ts | 57 +++++++++++++++++------------------------ src/compiler/scanner.ts | 25 ++++++++++++++++-- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 758b4253ddc..42af2b52210 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -997,6 +997,10 @@ module ts { // // Getting this all correct is tricky and requires careful reading of the grammar to // understand when these values should be changed versus when they should be inherited. + // + // Note: it should not be necessary to save/restore these flags during speculative/lookahead + // parsing. These context flags are naturally stored and restored through normal recursive + // descent parsing and unwinding. var contextFlags: ParserContextFlags = 0; function setContextFlag(val: Boolean, flag: ParserContextFlags) { @@ -1156,16 +1160,21 @@ module ts { return token = scanner.reScanTemplateToken(); } - function lookAheadHelper(callback: () => T, alwaysResetState: boolean): T { + function speculationHelper(callback: () => T, isLookAhead: boolean): T { // Keep track of the state we'll need to rollback to if lookahead fails (or if the // caller asked us to always reset our state). var saveToken = token; var saveParseDiagnosticsLength = sourceFile.parseDiagnostics.length; - var result = callback(); + // If we're only looking ahead, then tell the scanner to only lookahead as well. + // If we're actually speculatively parsing, then tell the scanner to do the same. + var result = isLookAhead + ? scanner.lookAhead(callback) + : scanner.tryScan(callback); - // Now restore as appropriate. - if (!result || alwaysResetState) { + // If our callback returned something 'falsy' or we're just looking ahead, + // then unconditionally restore us to where we were. + if (!result || isLookAhead) { token = saveToken; sourceFile.parseDiagnostics.length = saveParseDiagnosticsLength; } @@ -1173,25 +1182,19 @@ module ts { return result; } + // Invokes the provided callback then unconditionally restores the parser to the state it + // was in immediately prior to invoking the callback. The result of invoking the callback + // is returned from this function. function lookAhead(callback: () => T): T { - var result: T; - scanner.tryScan(() => { - result = lookAheadHelper(callback, /*alwaysResetState:*/ true); - - // Returning false here indicates to the scanner that it should always jump - // back to where it started. This makes sense as 'lookahead' acts as if - // neither the parser nor scanner was affected by the operation. - // - // Note: the rewinding of the parser state is already handled in lookAheadHelper - // (because we passed 'true' for alwaysResetState). - return false; - }); - - return result; + return speculationHelper(callback, /*isLookAhead:*/ true); } - + + // Invokes the provided callback. If the callback returns something falsy, then it restores + // the parser to the state it was in immediately prior to invoking the callback. If the + // callback returns something truthy, then the parser state is not rolled back. The result + // of invoking the callback is returned from this function. function tryParse(callback: () => T): T { - return scanner.tryScan(() => lookAheadHelper(callback, /*alwaysResetState:*/ false)); + return speculationHelper(callback, /*isLookAhead:*/ false); } function isIdentifier(): boolean { @@ -4631,8 +4634,7 @@ module ts { function checkTypeArguments(typeArguments: NodeArray) { return checkForDisallowedTrailingComma(typeArguments) || - checkForAtLeastOneTypeArgument(typeArguments) || - checkForMissingTypeArgument(typeArguments); + checkForAtLeastOneTypeArgument(typeArguments); } function checkForOmittedArgument(arguments: NodeArray) { @@ -4646,17 +4648,6 @@ module ts { } } - function checkForMissingTypeArgument(typeArguments: NodeArray) { - if (typeArguments) { - for (var i = 0, n = typeArguments.length; i < n; i++) { - var arg = typeArguments[i]; - if (arg.kind === SyntaxKind.TypeReference && getFullWidth((arg).typeName) === 0) { - return grammarErrorAtPos(arg.pos, 0, Diagnostics.Type_expected); - } - } - } - } - function checkForAtLeastOneTypeArgument(typeArguments: NodeArray) { if (typeArguments && typeArguments.length === 0) { var start = typeArguments.pos - "<".length; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index fcb8c9b0c31..3493ad7b512 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -29,6 +29,15 @@ module ts { scan(): SyntaxKind; setText(text: string): void; setTextPos(textPos: number): void; + // Invokes the provided callback then unconditionally restores the scanner to the state it + // was in immediately prior to invoking the callback. The result of invoking the callback + // is returned from this function. + lookAhead(callback: () => T): T; + + // Invokes the provided callback. If the callback returns something falsy, then it restores + // the scanner to the state it was in immediately prior to invoking the callback. If the + // callback returns something truthy, then the scanner state is not rolled back. The result + // of invoking the callback is returned from this function. tryScan(callback: () => T): T; } @@ -1174,7 +1183,7 @@ module ts { return token = scanTemplateAndSetTokenValue(); } - function tryScan(callback: () => T): T { + function speculationHelper(callback: () => T, isLookahead: boolean): T { var savePos = pos; var saveStartPos = startPos; var saveTokenPos = tokenPos; @@ -1182,7 +1191,10 @@ module ts { var saveTokenValue = tokenValue; var savePrecedingLineBreak = precedingLineBreak; var result = callback(); - if (!result) { + + // If our callback returned something 'falsy' or we're just looking ahead, + // then unconditionally restore us to where we were. + if (!result || isLookahead) { pos = savePos; startPos = saveStartPos; tokenPos = saveTokenPos; @@ -1193,6 +1205,14 @@ module ts { return result; } + function lookAhead(callback: () => T): T { + return speculationHelper(callback, /*isLookahead:*/ true); + } + + function tryScan(callback: () => T): T { + return speculationHelper(callback, /*isLookahead:*/ false); + } + function setText(newText: string) { text = newText || ""; len = text.length; @@ -1228,6 +1248,7 @@ module ts { setText, setTextPos, tryScan, + lookAhead, }; } } From 5bd57be94713c99003fb22595f026eb7174d7755 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2014 09:19:24 -0800 Subject: [PATCH 3/6] Fix comment. --- src/compiler/parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 42af2b52210..9ec83501e48 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3093,10 +3093,10 @@ module ts { function canFollowTypeArgumentsInExpression(): boolean { switch (token) { case SyntaxKind.OpenParenToken: // foo( - case SyntaxKind.DotToken: // foo. - // These two cases are the only cases where this token can legally follow a - // type argument list. So we definitely want to treat this as a type arg list. + // this case are the only case where this token can legally follow a type argument + // list. So we definitely want to treat this as a type arg list. + case SyntaxKind.DotToken: // foo. case SyntaxKind.CloseParenToken: // foo) case SyntaxKind.CloseBracketToken: // foo] case SyntaxKind.ColonToken: // foo: From 8c0fdbcff0af8aaf8ea0f9813928bc1578b81e97 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2014 09:49:52 -0800 Subject: [PATCH 4/6] Add invariant assert in the parser. --- src/compiler/parser.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9ec83501e48..1763df9cda8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1166,12 +1166,20 @@ module ts { var saveToken = token; var saveParseDiagnosticsLength = sourceFile.parseDiagnostics.length; + // Note: it is not actually necessary to save/restore the context falgs here. That's + // because the saving/restorating of these flags happens naturally through the recursive + // descent nature of our parser. However, we still store this here just so we can + // assert that that invariant holds. + var saveContextFlags = contextFlags; + // If we're only looking ahead, then tell the scanner to only lookahead as well. // If we're actually speculatively parsing, then tell the scanner to do the same. var result = isLookAhead ? scanner.lookAhead(callback) : scanner.tryScan(callback); + Debug.assert(saveContextFlags === contextFlags); + // If our callback returned something 'falsy' or we're just looking ahead, // then unconditionally restore us to where we were. if (!result || isLookAhead) { @@ -1958,6 +1966,9 @@ module ts { return result; } + // We didn't even have an open paren. If the caller requires a complete parameter list, + // we definitely can't provide that. However, if they're ok with an incomplete one, + // then just return an empty set of parameters. return requireCompleteParameterList ? undefined : createMissingList(); } From 6a3222ca30ecbd0728be841828de7d0a93da212f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2014 10:07:44 -0800 Subject: [PATCH 5/6] Inline method. --- src/compiler/parser.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1763df9cda8..f24b07eaf8e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1304,10 +1304,6 @@ module ts { parseErrorAtCurrentToken(diagnosticMessage, arg0); } - return createMissingNodeWithoutError(kind); - } - - function createMissingNodeWithoutError(kind: SyntaxKind) { var result = createNode(kind, scanner.getStartPos()); (result).text = ""; return finishNode(result); From 405508c100934f99d975348015cce735fa10f76c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2014 12:32:16 -0800 Subject: [PATCH 6/6] CR feedback. --- src/compiler/parser.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f24b07eaf8e..565c79f6e74 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1173,7 +1173,8 @@ module ts { var saveContextFlags = contextFlags; // If we're only looking ahead, then tell the scanner to only lookahead as well. - // If we're actually speculatively parsing, then tell the scanner to do the same. + // Otherwise, if we're actually speculatively parsing, then tell the scanner to do the + // same. var result = isLookAhead ? scanner.lookAhead(callback) : scanner.tryScan(callback); @@ -3054,9 +3055,7 @@ module ts { var callExpr = createNode(SyntaxKind.CallExpression, expression.pos); callExpr.expression = expression; callExpr.typeArguments = typeArguments; - if (callExpr.typeArguments || token === SyntaxKind.OpenParenToken) { - callExpr.arguments = parseArgumentList(); - } + callExpr.arguments = parseArgumentList(); expression = finishNode(callExpr); continue; }