5959import static com .sun .tools .javac .parser .Tokens .TokenKind .GT ;
6060import static com .sun .tools .javac .parser .Tokens .TokenKind .IMPORT ;
6161import static com .sun .tools .javac .parser .Tokens .TokenKind .LT ;
62+ import com .sun .tools .javac .parser .VirtualParser .VirtualScanner ;
6263import static com .sun .tools .javac .tree .JCTree .Tag .*;
6364import static com .sun .tools .javac .resources .CompilerProperties .Fragments .ImplicitAndExplicitNotAllowed ;
6465import static com .sun .tools .javac .resources .CompilerProperties .Fragments .VarAndExplicitNotAllowed ;
@@ -5019,13 +5020,17 @@ protected JCTree methodDeclaratorRest(int pos,
50195020 // Parsing formalParameters sets the receiverParam, if present
50205021 List <JCVariableDecl > params = List .nil ();
50215022 List <JCExpression > thrown = List .nil ();
5023+ boolean unclosedParameterList ;
50225024 if (!isRecord || name != names .init || token .kind == LPAREN ) {
50235025 params = formalParameters ();
5026+ unclosedParameterList = token .pos == endPosTable .errorEndPos ;
50245027 if (!isVoid ) type = bracketsOpt (type );
50255028 if (token .kind == THROWS ) {
50265029 nextToken ();
50275030 thrown = qualidentList (true );
50285031 }
5032+ } else {
5033+ unclosedParameterList = false ;
50295034 }
50305035
50315036 saveDanglingDocComments (dc );
@@ -5039,14 +5044,18 @@ protected JCTree methodDeclaratorRest(int pos,
50395044 if (token .kind == DEFAULT ) {
50405045 accept (DEFAULT );
50415046 defaultValue = annotationValue ();
5047+ accept (SEMI );
50425048 } else {
50435049 defaultValue = null ;
5050+ accept (SEMI , tk -> Errors .Expected2 (LBRACE , SEMI ));
50445051 }
5045- accept (SEMI );
50465052 if (token .pos <= endPosTable .errorEndPos ) {
50475053 // error recovery
5048- skip (false , true , false , false );
5049- if (token .kind == LBRACE ) {
5054+ // look if there is a probable missing opening brace,
5055+ // and if yes, parse as a block
5056+ boolean parseAsBlock = openingBraceMissing (unclosedParameterList );
5057+
5058+ if (parseAsBlock ) {
50505059 body = block ();
50515060 }
50525061 }
@@ -5063,6 +5072,84 @@ protected JCTree methodDeclaratorRest(int pos,
50635072 }
50645073 }
50655074
5075+ /**
5076+ * After seeing a method header, and not seeing an opening left brace,
5077+ * attempt to estimate if acting as if the left brace was present and
5078+ * parsing the upcoming code will get better results than not parsing
5079+ * the code as a block.
5080+ *
5081+ * The estimate is as follows:
5082+ * - tokens are skipped until member, statement or identifier is found,
5083+ * - then, if there is a left brace, parse as a block,
5084+ * - otherwise, if the head was broken, do not parse as a block,
5085+ * - otherwise, look at the next token and:
5086+ * - if it definitelly starts a statement, parse as a block,
5087+ * - otherwise, if it is a closing/right brace, count opening and closing
5088+ * braces in the rest of the file, to see if imaginarily "adding" an opening
5089+ * brace would lead to a balanced count - if yes, parse as a block,
5090+ * - otherwise, speculatively parse the following code as a block, and if
5091+ * it contains statements that cannot be members, parse as a block,
5092+ * - otherwise, don't parse as a block.
5093+ *
5094+ * @param unclosedParameterList whether there was a serious problem in the
5095+ * parameters list
5096+ * @return true if and only if the following code should be parsed as a block.
5097+ */
5098+ private boolean openingBraceMissing (boolean unclosedParameterList ) {
5099+ skip (false , true , !unclosedParameterList , !unclosedParameterList );
5100+
5101+ if (token .kind == LBRACE ) {
5102+ return true ;
5103+ } else if (unclosedParameterList ) {
5104+ return false ;
5105+ } else {
5106+ return switch (token .kind ) {
5107+ //definitelly sees a statement:
5108+ case CASE , DEFAULT , IF , FOR , WHILE , DO , TRY , SWITCH ,
5109+ RETURN , THROW , BREAK , CONTINUE , ELSE , FINALLY ,
5110+ CATCH , THIS , SUPER , NEW -> true ;
5111+ case RBRACE -> {
5112+ //check if adding an opening brace would balance out
5113+ //the opening and closing braces:
5114+ int braceBalance = 1 ;
5115+ VirtualScanner virtualScanner = new VirtualScanner (S );
5116+
5117+ virtualScanner .nextToken ();
5118+
5119+ while (virtualScanner .token ().kind != TokenKind .EOF ) {
5120+ switch (virtualScanner .token ().kind ) {
5121+ case LBRACE -> braceBalance ++;
5122+ case RBRACE -> braceBalance --;
5123+ }
5124+ virtualScanner .nextToken ();
5125+ }
5126+
5127+ yield braceBalance == 0 ;
5128+ }
5129+ default -> {
5130+ //speculatively try to parse as a block, and check
5131+ //if the result would suggest there is a block
5132+ //e.g.: it contains a statement that is not
5133+ //a member declaration
5134+ JavacParser speculative = new VirtualParser (this );
5135+ JCBlock speculativeResult =
5136+ speculative .block ();
5137+ if (!speculativeResult .stats .isEmpty ()) {
5138+ JCStatement last = speculativeResult .stats .last ();
5139+ yield !speculativeResult .stats .stream ().allMatch (s -> s .hasTag (VARDEF ) ||
5140+ s .hasTag (CLASSDEF ) ||
5141+ s .hasTag (BLOCK ) ||
5142+ s == last ) ||
5143+ !(last instanceof JCExpressionStatement exprStatement &&
5144+ exprStatement .expr .hasTag (ERRONEOUS ));
5145+ } else {
5146+ yield false ;
5147+ }
5148+ }
5149+ };
5150+ }
5151+ }
5152+
50665153 /** QualidentList = [Annotations] Qualident {"," [Annotations] Qualident}
50675154 */
50685155 List <JCExpression > qualidentList (boolean allowAnnos ) {
0 commit comments