Skip to content

Commit

Permalink
8324859: Improve error recovery
Browse files Browse the repository at this point in the history
Reviewed-by: mcimadamore
  • Loading branch information
lahodaj committed Aug 29, 2024
1 parent 1383fec commit 0b4a7d5
Show file tree
Hide file tree
Showing 2 changed files with 622 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import static com.sun.tools.javac.parser.Tokens.TokenKind.GT;
import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
import static com.sun.tools.javac.parser.Tokens.TokenKind.LT;
import com.sun.tools.javac.parser.VirtualParser.VirtualScanner;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.ImplicitAndExplicitNotAllowed;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
Expand Down Expand Up @@ -5019,13 +5020,17 @@ protected JCTree methodDeclaratorRest(int pos,
// Parsing formalParameters sets the receiverParam, if present
List<JCVariableDecl> params = List.nil();
List<JCExpression> thrown = List.nil();
boolean unclosedParameterList;
if (!isRecord || name != names.init || token.kind == LPAREN) {
params = formalParameters();
unclosedParameterList = token.pos == endPosTable.errorEndPos;
if (!isVoid) type = bracketsOpt(type);
if (token.kind == THROWS) {
nextToken();
thrown = qualidentList(true);
}
} else {
unclosedParameterList = false;
}

saveDanglingDocComments(dc);
Expand All @@ -5039,14 +5044,18 @@ protected JCTree methodDeclaratorRest(int pos,
if (token.kind == DEFAULT) {
accept(DEFAULT);
defaultValue = annotationValue();
accept(SEMI);
} else {
defaultValue = null;
accept(SEMI, tk -> Errors.Expected2(LBRACE, SEMI));
}
accept(SEMI);
if (token.pos <= endPosTable.errorEndPos) {
// error recovery
skip(false, true, false, false);
if (token.kind == LBRACE) {
// look if there is a probable missing opening brace,
// and if yes, parse as a block
boolean parseAsBlock = openingBraceMissing(unclosedParameterList);

if (parseAsBlock) {
body = block();
}
}
Expand All @@ -5063,6 +5072,84 @@ protected JCTree methodDeclaratorRest(int pos,
}
}

/**
* After seeing a method header, and not seeing an opening left brace,
* attempt to estimate if acting as if the left brace was present and
* parsing the upcoming code will get better results than not parsing
* the code as a block.
*
* The estimate is as follows:
* - tokens are skipped until member, statement or identifier is found,
* - then, if there is a left brace, parse as a block,
* - otherwise, if the head was broken, do not parse as a block,
* - otherwise, look at the next token and:
* - if it definitelly starts a statement, parse as a block,
* - otherwise, if it is a closing/right brace, count opening and closing
* braces in the rest of the file, to see if imaginarily "adding" an opening
* brace would lead to a balanced count - if yes, parse as a block,
* - otherwise, speculatively parse the following code as a block, and if
* it contains statements that cannot be members, parse as a block,
* - otherwise, don't parse as a block.
*
* @param unclosedParameterList whether there was a serious problem in the
* parameters list
* @return true if and only if the following code should be parsed as a block.
*/
private boolean openingBraceMissing(boolean unclosedParameterList) {
skip(false, true, !unclosedParameterList, !unclosedParameterList);

if (token.kind == LBRACE) {
return true;
} else if (unclosedParameterList) {
return false;
} else {
return switch (token.kind) {
//definitelly sees a statement:
case CASE, DEFAULT, IF, FOR, WHILE, DO, TRY, SWITCH,
RETURN, THROW, BREAK, CONTINUE, ELSE, FINALLY,
CATCH, THIS, SUPER, NEW -> true;
case RBRACE -> {
//check if adding an opening brace would balance out
//the opening and closing braces:
int braceBalance = 1;
VirtualScanner virtualScanner = new VirtualScanner(S);

virtualScanner.nextToken();

while (virtualScanner.token().kind != TokenKind.EOF) {
switch (virtualScanner.token().kind) {
case LBRACE -> braceBalance++;
case RBRACE -> braceBalance--;
}
virtualScanner.nextToken();
}

yield braceBalance == 0;
}
default -> {
//speculatively try to parse as a block, and check
//if the result would suggest there is a block
//e.g.: it contains a statement that is not
//a member declaration
JavacParser speculative = new VirtualParser(this);
JCBlock speculativeResult =
speculative.block();
if (!speculativeResult.stats.isEmpty()) {
JCStatement last = speculativeResult.stats.last();
yield !speculativeResult.stats.stream().allMatch(s -> s.hasTag(VARDEF) ||
s.hasTag(CLASSDEF) ||
s.hasTag(BLOCK) ||
s == last) ||
!(last instanceof JCExpressionStatement exprStatement &&
exprStatement.expr.hasTag(ERRONEOUS));
} else {
yield false;
}
}
};
}
}

/** QualidentList = [Annotations] Qualident {"," [Annotations] Qualident}
*/
List<JCExpression> qualidentList(boolean allowAnnos) {
Expand Down
Loading

0 comments on commit 0b4a7d5

Please sign in to comment.