protected IdentifierNode handleMissingIdentifier(RecognitionException ex)
{
consumeParsingError(ex); //we don't want to drop a semicolon here
//now let's guard against the case where the very next token is an identifier.
//if we produce an identifier here, everything downstream might fail
final IdentifierNode node;
ASToken current = buffer.previous();
ASToken la2 = LT(1 + 1);
ASToken la1 = LT(1);
if (current.getType() == ASTokenTypes.TOKEN_RESERVED_WORD_EXTENDS)
{
// Fix for CMP-1087: the following two branches are too greedy.
// i.e.
// public class T extends implements MyInterface
// ^
// The "extends" keyword expects an identifier. However, instead of
// reporting the missing identifier and continue with "implements",
// the following recovery logic throws away "implements" keyword and
// sends back "MyInterface".
node = IdentifierNode.createEmptyIdentifierNodeAfterToken(current);
}
else if (la2.getType() == ASTokenTypes.TOKEN_IDENTIFIER && la2.getLine() == current.getLine())
{
//let's make sure this is on the same line, avoiding possibly going past the end of the statement
//since this is all repair code anyway, this produces a much "saner" repaired tree
ASToken token = LT(1 + 1);
node = new IdentifierNode(token.getText(), (IASToken)token);
consume(); //consume error token
consume(); //act as match() for identifier, which consumes it
}
else if (la1.isKeywordOrContextualReservedWord() && la1.getLine() == current.getLine())
{
// If it's a keyword, repair by making an identifier node with the text of the keyword
// This makes a more sensible tree - the user may be in the middle of typing an identifier that
// starts with a keyword. They probably meant "f.is" rather than "(f.)is" for example
node = new IdentifierNode(la1.getText(), (IASToken)la1);
consume();
}
else
{
node = IdentifierNode.createEmptyIdentifierNodeAfterToken(current);