Better error recovery when errant semicolon found in a class.

This commit is contained in:
Daniel Rosenwasser
2014-07-15 14:24:44 -07:00
parent 9ecf01b57a
commit cece68098a
11 changed files with 61 additions and 188 deletions

View File

@@ -619,14 +619,14 @@ module ts {
}
// True if positioned at the start of a list element
function isListElement(kind: ParsingContext): boolean {
function isListElement(kind: ParsingContext, inErrorRecovery: boolean): boolean {
switch (kind) {
case ParsingContext.SourceElements:
case ParsingContext.ModuleElements:
return isSourceElement();
return isSourceElement(inErrorRecovery);
case ParsingContext.BlockStatements:
case ParsingContext.SwitchClauseStatements:
return isStatement();
return isStatement(inErrorRecovery);
case ParsingContext.SwitchClauses:
return token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword;
case ParsingContext.TypeMembers:
@@ -650,6 +650,8 @@ module ts {
case ParsingContext.TypeArguments:
return isType();
}
Debug.fail("Non-exhaustive case in 'isListElement'.");
}
// True if positioned at a list terminator
@@ -717,10 +719,12 @@ module ts {
}
// True if positioned at element or terminator of the current list or any enclosing list
function isInParsingContext(): boolean {
function isInSomeParsingContext(): boolean {
for (var kind = 0; kind < ParsingContext.Count; kind++) {
if (parsingContext & (1 << kind)) {
if (isListElement(kind) || isListTerminator(kind)) return true;
if (isListElement(kind, /* inErrorRecovery */ true) || isListTerminator(kind)) {
return true;
}
}
}
@@ -734,12 +738,12 @@ module ts {
var result = <NodeArray<T>>[];
result.pos = getNodePos();
while (!isListTerminator(kind)) {
if (isListElement(kind)) {
if (isListElement(kind, /* inErrorRecovery */ false)) {
result.push(parseElement());
}
else {
error(parsingContextErrors(kind));
if (isInParsingContext()) {
if (isInSomeParsingContext()) {
break;
}
nextToken();
@@ -761,7 +765,7 @@ module ts {
var errorCountBeforeParsingList = file.syntacticErrors.length;
var commaStart = -1; // Meaning the previous token was not a comma
while (true) {
if (isListElement(kind)) {
if (isListElement(kind, /* inErrorRecovery */ false)) {
result.push(parseElement());
commaStart = scanner.getTokenPos();
if (parseOptional(SyntaxKind.CommaToken)) {
@@ -791,7 +795,7 @@ module ts {
}
else {
error(parsingContextErrors(kind));
if (token !== SyntaxKind.CommaToken && isInParsingContext()) {
if (token !== SyntaxKind.CommaToken && isInSomeParsingContext()) {
break;
}
nextToken();
@@ -2066,12 +2070,19 @@ module ts {
return finishNode(node);
}
function isStatement(): boolean {
function isStatement(inErrorRecovery: boolean): boolean {
switch (token) {
case SyntaxKind.SemicolonToken:
// If we're in error recovery, then we don't want to treat ';' as an empty statement.
// The problem is that ';' can show up in far too many contexts, and if we see one
// and assume it's a statement, then we may bail out innapropriately from whatever
// we're parsing. For example, if we have a semicolon in the middle of a class, then
// we really don't want to assume the class is over and we're on a statement in the
// outer module. We just want to consume and move on.
return !inErrorRecovery;
case SyntaxKind.OpenBraceToken:
case SyntaxKind.VarKeyword:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.SemicolonToken:
case SyntaxKind.IfKeyword:
case SyntaxKind.DoKeyword:
case SyntaxKind.WhileKeyword:
@@ -2103,8 +2114,8 @@ module ts {
}
}
function isStatementOrFunction(): boolean {
return token === SyntaxKind.FunctionKeyword || isStatement();
function isStatementOrFunction(inErrorRecovery: boolean): boolean {
return token === SyntaxKind.FunctionKeyword || isStatement(inErrorRecovery);
}
function parseStatement(): Statement {
@@ -2813,8 +2824,8 @@ module ts {
return result;
}
function isSourceElement(): boolean {
return isDeclaration() || isStatement();
function isSourceElement(inErrorRecovery: boolean): boolean {
return isDeclaration() || isStatement(inErrorRecovery);
}
function parseSourceElement() {