From 5db51fead829628bf7237b565097127b2798bbec Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Dec 2014 15:35:09 -0800 Subject: [PATCH] Better error recovery when encountering an errant comma in a semicolon delimited list. --- src/compiler/parser.ts | 37 +++++++++++++------ .../parserCommaInTypeMemberList2.errors.txt | 24 ++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8c331a89e0d..dc873755cdf 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1338,15 +1338,17 @@ module ts { return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.EndOfFileToken || scanner.hasPrecedingLineBreak(); } - function parseSemicolon(diagnosticMessage?: DiagnosticMessage): void { + function parseSemicolon(diagnosticMessage?: DiagnosticMessage): boolean { if (canParseSemicolon()) { if (token === SyntaxKind.SemicolonToken) { // consume the semicolon if it was explicitly provided. nextToken(); } + + return true; } else { - parseExpected(SyntaxKind.SemicolonToken, diagnosticMessage); + return parseExpected(SyntaxKind.SemicolonToken, diagnosticMessage); } } @@ -2052,13 +2054,27 @@ module ts { return requireCompleteParameterList ? undefined : createMissingList(); } + function parseTypeMemberSemicolon() { + // Try to parse out an explicit or implicit (ASI) semicolon for a type member. If we + // don't have one, then an appropriate error will be reported. + if (parseSemicolon()) { + return; + } + + // If we don't have a semicolon, then the user may have written a comma instead + // accidently (pretty easy to do since commas are so prevalent as list separators). So + // just consume the comma and keep going. Note: we'll have already reported the error + // about the missing semicolon above. + parseOptional(SyntaxKind.CommaToken); + } + function parseSignatureMember(kind: SyntaxKind): SignatureDeclaration { var node = createNode(kind); if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); - parseSemicolon(); + parseTypeMemberSemicolon(); return finishNode(node); } @@ -2130,11 +2146,11 @@ module ts { setModifiers(node, modifiers); node.parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); node.type = parseTypeAnnotation(); - parseSemicolon(); + parseTypeMemberSemicolon(); return finishNode(node) } - function parsePropertyOrMethod(): Declaration { + function parsePropertyOrMethodSignature(): Declaration { var fullStart = scanner.getStartPos(); var name = parsePropertyName(); var questionToken = parseOptionalToken(SyntaxKind.QuestionToken); @@ -2147,8 +2163,7 @@ module ts { // Method signatues don't exist in expression contexts. So they have neither // [Yield] nor [GeneratorParameter] fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, method); - - parseSemicolon(); + parseTypeMemberSemicolon(); return finishNode(method); } else { @@ -2156,7 +2171,7 @@ module ts { property.name = name; property.questionToken = questionToken; property.type = parseTypeAnnotation(); - parseSemicolon(); + parseTypeMemberSemicolon(); return finishNode(property); } } @@ -2188,7 +2203,7 @@ module ts { return parseSignatureMember(SyntaxKind.CallSignature); case SyntaxKind.OpenBracketToken: // Indexer or computed property - return isIndexSignature() ? parseIndexSignatureDeclaration(scanner.getStartPos(), /*modifiers:*/ undefined) : parsePropertyOrMethod(); + return isIndexSignature() ? parseIndexSignatureDeclaration(scanner.getStartPos(), /*modifiers:*/ undefined) : parsePropertyOrMethodSignature(); case SyntaxKind.NewKeyword: if (lookAhead(isStartOfConstructSignature)) { return parseSignatureMember(SyntaxKind.ConstructSignature); @@ -2196,10 +2211,10 @@ module ts { // fall through. case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: - return parsePropertyOrMethod(); + return parsePropertyOrMethodSignature(); default: if (isIdentifierOrKeyword()) { - return parsePropertyOrMethod(); + return parsePropertyOrMethodSignature(); } } } diff --git a/tests/baselines/reference/parserCommaInTypeMemberList2.errors.txt b/tests/baselines/reference/parserCommaInTypeMemberList2.errors.txt index 025febbc9a5..fb563542961 100644 --- a/tests/baselines/reference/parserCommaInTypeMemberList2.errors.txt +++ b/tests/baselines/reference/parserCommaInTypeMemberList2.errors.txt @@ -1,29 +1,11 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,38): error TS1134: Variable declaration expected. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,60): error TS1005: ';' expected. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,70): error TS1128: Declaration or statement expected. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,53): error TS1005: ';' expected. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,9): error TS2304: Cannot find name '$'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,31): error TS2304: Cannot find name 'any'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,50): error TS2304: Cannot find name 'any'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,55): error TS2304: Cannot find name 'width'. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts(1,62): error TS2304: Cannot find name 'string'. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts (8 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserCommaInTypeMemberList2.ts (2 errors) ==== var s = $.extend< { workItem: any }, { workItem: any, width: string }>({ workItem: this._workItem }, {}); - ~ -!!! error TS1134: Variable declaration expected. - ~ + ~ !!! error TS1005: ';' expected. - ~ -!!! error TS1128: Declaration or statement expected. ~ !!! error TS2304: Cannot find name '$'. - ~~~ -!!! error TS2304: Cannot find name 'any'. - ~~~ -!!! error TS2304: Cannot find name 'any'. - ~~~~~ -!!! error TS2304: Cannot find name 'width'. - ~~~~~~ -!!! error TS2304: Cannot find name 'string'. \ No newline at end of file