59
59
import static com .sun .tools .javac .parser .Tokens .TokenKind .GT ;
60
60
import static com .sun .tools .javac .parser .Tokens .TokenKind .IMPORT ;
61
61
import static com .sun .tools .javac .parser .Tokens .TokenKind .LT ;
62
+ import com .sun .tools .javac .parser .VirtualParser .VirtualScanner ;
62
63
import static com .sun .tools .javac .tree .JCTree .Tag .*;
63
64
import static com .sun .tools .javac .resources .CompilerProperties .Fragments .ImplicitAndExplicitNotAllowed ;
64
65
import static com .sun .tools .javac .resources .CompilerProperties .Fragments .VarAndExplicitNotAllowed ;
@@ -5019,13 +5020,17 @@ protected JCTree methodDeclaratorRest(int pos,
5019
5020
// Parsing formalParameters sets the receiverParam, if present
5020
5021
List <JCVariableDecl > params = List .nil ();
5021
5022
List <JCExpression > thrown = List .nil ();
5023
+ boolean unclosedParameterList ;
5022
5024
if (!isRecord || name != names .init || token .kind == LPAREN ) {
5023
5025
params = formalParameters ();
5026
+ unclosedParameterList = token .pos == endPosTable .errorEndPos ;
5024
5027
if (!isVoid ) type = bracketsOpt (type );
5025
5028
if (token .kind == THROWS ) {
5026
5029
nextToken ();
5027
5030
thrown = qualidentList (true );
5028
5031
}
5032
+ } else {
5033
+ unclosedParameterList = false ;
5029
5034
}
5030
5035
5031
5036
saveDanglingDocComments (dc );
@@ -5039,14 +5044,18 @@ protected JCTree methodDeclaratorRest(int pos,
5039
5044
if (token .kind == DEFAULT ) {
5040
5045
accept (DEFAULT );
5041
5046
defaultValue = annotationValue ();
5047
+ accept (SEMI );
5042
5048
} else {
5043
5049
defaultValue = null ;
5050
+ accept (SEMI , tk -> Errors .Expected2 (LBRACE , SEMI ));
5044
5051
}
5045
- accept (SEMI );
5046
5052
if (token .pos <= endPosTable .errorEndPos ) {
5047
5053
// 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 ) {
5050
5059
body = block ();
5051
5060
}
5052
5061
}
@@ -5063,6 +5072,84 @@ protected JCTree methodDeclaratorRest(int pos,
5063
5072
}
5064
5073
}
5065
5074
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
+
5066
5153
/** QualidentList = [Annotations] Qualident {"," [Annotations] Qualident}
5067
5154
*/
5068
5155
List <JCExpression > qualidentList (boolean allowAnnos ) {
0 commit comments