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) {
|
List<JCTree> enumBody(Name enumName) {
|
||||||
accept(LBRACE);
|
accept(LBRACE);
|
||||||
ListBuffer<JCTree> defs = new ListBuffer<>();
|
ListBuffer<JCTree> defs = new ListBuffer<>();
|
||||||
|
boolean wasSemi = false;
|
||||||
|
boolean hasStructuralErrors = false;
|
||||||
|
boolean wasError = false;
|
||||||
if (token.kind == COMMA) {
|
if (token.kind == COMMA) {
|
||||||
nextToken();
|
nextToken();
|
||||||
} else if (token.kind != RBRACE && token.kind != SEMI) {
|
if (token.kind == SEMI) {
|
||||||
defs.append(enumeratorDeclaration(enumName));
|
wasSemi = true;
|
||||||
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)));
|
|
||||||
nextToken();
|
nextToken();
|
||||||
|
} else if (token.kind != RBRACE) {
|
||||||
|
reportSyntaxError(S.prevToken().endPos,
|
||||||
|
Errors.Expected2(RBRACE, SEMI));
|
||||||
|
wasError = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (token.kind == SEMI) {
|
while (token.kind != RBRACE && token.kind != EOF) {
|
||||||
nextToken();
|
if (token.kind == SEMI) {
|
||||||
while (token.kind != RBRACE && token.kind != EOF) {
|
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,
|
defs.appendList(classOrInterfaceBodyDeclaration(enumName,
|
||||||
false));
|
false));
|
||||||
if (token.pos <= endPosTable.errorEndPos) {
|
if (token.pos <= endPosTable.errorEndPos) {
|
||||||
|
@ -3785,6 +3821,28 @@ public class JavacParser implements Parser {
|
||||||
return defs.toList();
|
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 "}" ]
|
/** EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ]
|
||||||
*/
|
*/
|
||||||
JCTree enumeratorDeclaration(Name enumName) {
|
JCTree enumeratorDeclaration(Name enumName) {
|
||||||
|
|
|
@ -2158,6 +2158,12 @@ compiler.err.expected3=\
|
||||||
compiler.err.premature.eof=\
|
compiler.err.premature.eof=\
|
||||||
reached end of file while parsing
|
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.
|
## The following are related in form, but do not easily fit the above paradigm.
|
||||||
compiler.err.expected.module=\
|
compiler.err.expected.module=\
|
||||||
''module'' expected
|
''module'' expected
|
||||||
|
|
|
@ -10,5 +10,8 @@ public enum T4994049 {
|
||||||
FOO {
|
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
|
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:16: compiler.err.expected: ')'
|
||||||
EnumMembersOrder.java:11:17: compiler.err.expected3: ',', '}', ';'
|
EnumMembersOrder.java:11:18: compiler.err.expected3: ',', '}', ';'
|
||||||
EnumMembersOrder.java:11:18: compiler.err.expected: '}'
|
EnumMembersOrder.java:11:20: compiler.err.enum.constant.expected
|
||||||
EnumMembersOrder.java:11:31: compiler.err.expected3: class, interface, enum
|
3 errors
|
||||||
EnumMembersOrder.java:17:13: compiler.err.expected3: class, interface, enum
|
|
||||||
EnumMembersOrder.java:19:1: compiler.err.expected3: class, interface, enum
|
|
||||||
6 errors
|
|
||||||
|
|
|
@ -1291,6 +1291,179 @@ public class JavacParserTest extends TestCase {
|
||||||
assertTrue("testAnalyzeParensWithComma2", found[0]);
|
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 {
|
void run(String[] args) throws Exception {
|
||||||
int passed = 0, failed = 0;
|
int passed = 0, failed = 0;
|
||||||
final Pattern p = (args != null && args.length > 0)
|
final Pattern p = (args != null && args.length > 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue