mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8228647: Broken enum produce inconvenient errors and AST
Improving error recovery for misplace members in enums. Reviewed-by: vromero
This commit is contained in:
parent
6e86f5b47b
commit
36ae680f2a
8 changed files with 316 additions and 20 deletions
|
@ -3756,23 +3756,59 @@ public class JavacParser implements Parser {
|
|||
List<JCTree> enumBody(Name enumName) {
|
||||
accept(LBRACE);
|
||||
ListBuffer<JCTree> defs = new ListBuffer<>();
|
||||
boolean wasSemi = false;
|
||||
boolean hasStructuralErrors = false;
|
||||
boolean wasError = false;
|
||||
if (token.kind == COMMA) {
|
||||
nextToken();
|
||||
} else if (token.kind != RBRACE && token.kind != SEMI) {
|
||||
defs.append(enumeratorDeclaration(enumName));
|
||||
while (token.kind == COMMA) {
|
||||
nextToken();
|
||||
if (token.kind == RBRACE || token.kind == SEMI) break;
|
||||
defs.append(enumeratorDeclaration(enumName));
|
||||
}
|
||||
if (token.kind != SEMI && token.kind != RBRACE) {
|
||||
defs.append(syntaxError(token.pos, Errors.Expected3(COMMA, RBRACE, SEMI)));
|
||||
if (token.kind == SEMI) {
|
||||
wasSemi = true;
|
||||
nextToken();
|
||||
} else if (token.kind != RBRACE) {
|
||||
reportSyntaxError(S.prevToken().endPos,
|
||||
Errors.Expected2(RBRACE, SEMI));
|
||||
wasError = true;
|
||||
}
|
||||
}
|
||||
if (token.kind == SEMI) {
|
||||
nextToken();
|
||||
while (token.kind != RBRACE && token.kind != EOF) {
|
||||
while (token.kind != RBRACE && token.kind != EOF) {
|
||||
if (token.kind == SEMI) {
|
||||
accept(SEMI);
|
||||
wasSemi = true;
|
||||
if (token.kind == RBRACE || token.kind == EOF) break;
|
||||
}
|
||||
EnumeratorEstimate memberType = estimateEnumeratorOrMember(enumName);
|
||||
if (memberType == EnumeratorEstimate.UNKNOWN) {
|
||||
memberType = wasSemi ? EnumeratorEstimate.MEMBER
|
||||
: EnumeratorEstimate.ENUMERATOR;
|
||||
}
|
||||
if (memberType == EnumeratorEstimate.ENUMERATOR) {
|
||||
wasError = false;
|
||||
if (wasSemi && !hasStructuralErrors) {
|
||||
reportSyntaxError(token.pos, Errors.EnumConstantNotExpected);
|
||||
hasStructuralErrors = true;
|
||||
}
|
||||
defs.append(enumeratorDeclaration(enumName));
|
||||
if (token.pos <= endPosTable.errorEndPos) {
|
||||
// error recovery
|
||||
skip(false, true, true, false);
|
||||
} else {
|
||||
if (token.kind != RBRACE && token.kind != SEMI && token.kind != EOF) {
|
||||
if (token.kind == COMMA) {
|
||||
nextToken();
|
||||
} else {
|
||||
setErrorEndPos(token.pos);
|
||||
reportSyntaxError(S.prevToken().endPos,
|
||||
Errors.Expected3(COMMA, RBRACE, SEMI));
|
||||
wasError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!wasSemi && !hasStructuralErrors && !wasError) {
|
||||
reportSyntaxError(token.pos, Errors.EnumConstantExpected);
|
||||
hasStructuralErrors = true;
|
||||
}
|
||||
wasError = false;
|
||||
defs.appendList(classOrInterfaceBodyDeclaration(enumName,
|
||||
false));
|
||||
if (token.pos <= endPosTable.errorEndPos) {
|
||||
|
@ -3785,6 +3821,28 @@ public class JavacParser implements Parser {
|
|||
return defs.toList();
|
||||
}
|
||||
|
||||
private EnumeratorEstimate estimateEnumeratorOrMember(Name enumName) {
|
||||
if (token.kind == TokenKind.IDENTIFIER && token.name() != enumName) {
|
||||
Token next = S.token(1);
|
||||
switch (next.kind) {
|
||||
case LPAREN: case LBRACE: case COMMA: case SEMI:
|
||||
return EnumeratorEstimate.ENUMERATOR;
|
||||
}
|
||||
}
|
||||
switch (token.kind) {
|
||||
case IDENTIFIER: case MONKEYS_AT: case LT:
|
||||
return EnumeratorEstimate.UNKNOWN;
|
||||
default:
|
||||
return EnumeratorEstimate.MEMBER;
|
||||
}
|
||||
}
|
||||
|
||||
private enum EnumeratorEstimate {
|
||||
ENUMERATOR,
|
||||
MEMBER,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
/** EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ]
|
||||
*/
|
||||
JCTree enumeratorDeclaration(Name enumName) {
|
||||
|
|
|
@ -2158,6 +2158,12 @@ compiler.err.expected3=\
|
|||
compiler.err.premature.eof=\
|
||||
reached end of file while parsing
|
||||
|
||||
compiler.err.enum.constant.expected=\
|
||||
enum constant expected here
|
||||
|
||||
compiler.err.enum.constant.not.expected=\
|
||||
enum constant not expected here
|
||||
|
||||
## The following are related in form, but do not easily fit the above paradigm.
|
||||
compiler.err.expected.module=\
|
||||
''module'' expected
|
||||
|
|
|
@ -10,5 +10,8 @@ public enum T4994049 {
|
|||
FOO {
|
||||
}
|
||||
|
||||
BAR;
|
||||
BAR1,
|
||||
BAR2(),
|
||||
BAR3 {},
|
||||
BAR4() {};
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
T4994049.java:13:5: compiler.err.expected3: ',', '}', ';'
|
||||
T4994049.java:11:6: compiler.err.expected3: ',', '}', ';'
|
||||
1 error
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
// key: compiler.err.enum.constant.expected
|
||||
|
||||
enum EnumConstantExpected {
|
||||
A,
|
||||
void test() {}
|
||||
;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
// key: compiler.err.enum.constant.not.expected
|
||||
|
||||
enum EnumConstantNotExpected {
|
||||
;
|
||||
A;
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
EnumMembersOrder.java:11:16: compiler.err.expected: ')'
|
||||
EnumMembersOrder.java:11:17: compiler.err.expected3: ',', '}', ';'
|
||||
EnumMembersOrder.java:11:18: compiler.err.expected: '}'
|
||||
EnumMembersOrder.java:11:31: compiler.err.expected3: class, interface, enum
|
||||
EnumMembersOrder.java:17:13: compiler.err.expected3: class, interface, enum
|
||||
EnumMembersOrder.java:19:1: compiler.err.expected3: class, interface, enum
|
||||
6 errors
|
||||
EnumMembersOrder.java:11:18: compiler.err.expected3: ',', '}', ';'
|
||||
EnumMembersOrder.java:11:20: compiler.err.enum.constant.expected
|
||||
3 errors
|
||||
|
|
|
@ -1291,6 +1291,179 @@ public class JavacParserTest extends TestCase {
|
|||
assertTrue("testAnalyzeParensWithComma2", found[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrokenEnum1() throws IOException {
|
||||
assert tool != null;
|
||||
|
||||
String code = "package test; class Test { enum E { A, B, C. D, E, F; } }";
|
||||
StringWriter output = new StringWriter();
|
||||
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(output, fm, null, List.of("-XDrawDiagnostics"),
|
||||
null, Arrays.asList(new MyFileObject(code)));
|
||||
CompilationUnitTree cut = ct.parse().iterator().next();
|
||||
List<String> actual = List.of(output.toString().split("\r?\n"));
|
||||
List<String> expected = List.of("Test.java:1:44: compiler.err.expected3: ',', '}', ';'");
|
||||
|
||||
assertEquals("The expected and actual errors do not match, actual errors: " + actual,
|
||||
actual,
|
||||
expected);
|
||||
|
||||
String actualAST = cut.toString().replaceAll("\r*\n", "\n");
|
||||
String expectedAST = "package test;\n" +
|
||||
"\n" +
|
||||
"class Test {\n" +
|
||||
" \n" +
|
||||
" enum E {\n" +
|
||||
" /*public static final*/ A /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ B /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ C /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ D /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ E /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ F /* = new E() */ /*enum*/ ;\n" +
|
||||
" (ERROR) <error>;\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
assertEquals("The expected and actual AST do not match, actual AST: " + actualAST,
|
||||
actualAST,
|
||||
expectedAST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrokenEnum2() throws IOException {
|
||||
assert tool != null;
|
||||
|
||||
String code = "package test; class Test { enum E { A, B, C void t() {} } }";
|
||||
StringWriter output = new StringWriter();
|
||||
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(output, fm, null, List.of("-XDrawDiagnostics"),
|
||||
null, Arrays.asList(new MyFileObject(code)));
|
||||
CompilationUnitTree cut = ct.parse().iterator().next();
|
||||
List<String> actual = List.of(output.toString().split("\r?\n"));
|
||||
List<String> expected = List.of("Test.java:1:44: compiler.err.expected3: ',', '}', ';'");
|
||||
|
||||
assertEquals("The expected and actual errors do not match, actual errors: " + actual,
|
||||
actual,
|
||||
expected);
|
||||
|
||||
String actualAST = cut.toString().replaceAll("\r*\n", "\n");
|
||||
String expectedAST = "package test;\n" +
|
||||
"\n" +
|
||||
"class Test {\n" +
|
||||
" \n" +
|
||||
" enum E {\n" +
|
||||
" /*public static final*/ A /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ B /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ C /* = new E() */ /*enum*/ ;\n" +
|
||||
" \n" +
|
||||
" void t() {\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
assertEquals("The expected and actual AST do not match, actual AST: " + actualAST,
|
||||
actualAST,
|
||||
expectedAST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrokenEnum3() throws IOException {
|
||||
assert tool != null;
|
||||
|
||||
String code = "package test; class Test { enum E { , void t() {} } }";
|
||||
StringWriter output = new StringWriter();
|
||||
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(output, fm, null, List.of("-XDrawDiagnostics"),
|
||||
null, Arrays.asList(new MyFileObject(code)));
|
||||
CompilationUnitTree cut = ct.parse().iterator().next();
|
||||
List<String> actual = List.of(output.toString().split("\r?\n"));
|
||||
List<String> expected = List.of("Test.java:1:38: compiler.err.expected2: '}', ';'");
|
||||
|
||||
assertEquals("The expected and actual errors do not match, actual errors: " + actual,
|
||||
actual,
|
||||
expected);
|
||||
|
||||
String actualAST = cut.toString().replaceAll("\r*\n", "\n");
|
||||
String expectedAST = "package test;\n" +
|
||||
"\n" +
|
||||
"class Test {\n" +
|
||||
" \n" +
|
||||
" enum E {\n" +
|
||||
";\n" +
|
||||
" \n" +
|
||||
" void t() {\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
assertEquals("The expected and actual AST do not match, actual AST: " + actualAST,
|
||||
actualAST,
|
||||
expectedAST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrokenEnum4() throws IOException {
|
||||
assert tool != null;
|
||||
|
||||
String code = "package test; class Test { enum E { A, B, C, void t() {} } }";
|
||||
StringWriter output = new StringWriter();
|
||||
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(output, fm, null, List.of("-XDrawDiagnostics"),
|
||||
null, Arrays.asList(new MyFileObject(code)));
|
||||
CompilationUnitTree cut = ct.parse().iterator().next();
|
||||
List<String> actual = List.of(output.toString().split("\r?\n"));
|
||||
List<String> expected = List.of("Test.java:1:46: compiler.err.enum.constant.expected");
|
||||
|
||||
assertEquals("The expected and actual errors do not match, actual errors: " + actual,
|
||||
actual,
|
||||
expected);
|
||||
|
||||
String actualAST = cut.toString().replaceAll("\r*\n", "\n");
|
||||
String expectedAST = "package test;\n" +
|
||||
"\n" +
|
||||
"class Test {\n" +
|
||||
" \n" +
|
||||
" enum E {\n" +
|
||||
" /*public static final*/ A /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ B /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ C /* = new E() */ /*enum*/ ;\n" +
|
||||
" \n" +
|
||||
" void t() {\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
assertEquals("The expected and actual AST do not match, actual AST: " + actualAST,
|
||||
actualAST,
|
||||
expectedAST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrokenEnum5() throws IOException {
|
||||
assert tool != null;
|
||||
|
||||
String code = "package test; class Test { enum E { A; void t() {} B; } }";
|
||||
StringWriter output = new StringWriter();
|
||||
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(output, fm, null, List.of("-XDrawDiagnostics"),
|
||||
null, Arrays.asList(new MyFileObject(code)));
|
||||
CompilationUnitTree cut = ct.parse().iterator().next();
|
||||
List<String> actual = List.of(output.toString().split("\r?\n"));
|
||||
List<String> expected = List.of("Test.java:1:52: compiler.err.enum.constant.not.expected");
|
||||
|
||||
assertEquals("The expected and actual errors do not match, actual errors: " + actual,
|
||||
actual,
|
||||
expected);
|
||||
|
||||
String actualAST = cut.toString().replaceAll("\r*\n", "\n");
|
||||
String expectedAST = "package test;\n" +
|
||||
"\n" +
|
||||
"class Test {\n" +
|
||||
" \n" +
|
||||
" enum E {\n" +
|
||||
" /*public static final*/ A /* = new E() */ /*enum*/ ,\n" +
|
||||
" /*public static final*/ B /* = new E() */ /*enum*/ ;\n" +
|
||||
" \n" +
|
||||
" void t() {\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
assertEquals("The expected and actual AST do not match, actual AST: " + actualAST,
|
||||
actualAST,
|
||||
expectedAST);
|
||||
}
|
||||
|
||||
void run(String[] args) throws Exception {
|
||||
int passed = 0, failed = 0;
|
||||
final Pattern p = (args != null && args.length > 0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue