Merge branch 'pr-2272' into java-grammar

[java] Make enum body track trailing comma/semi
This commit is contained in:
Andreas Dangel
2020-02-18 19:23:17 +01:00
4 changed files with 116 additions and 6 deletions

View File

@ -912,9 +912,9 @@ void EnumBody():
{}
{
"{"
[ EnumConstant() ( LOOKAHEAD(2) "," EnumConstant() )* ]
[ "," ]
[ ";" ( ClassOrInterfaceBodyDeclaration() )* ]
[ EnumConstant() ( LOOKAHEAD(2) "," EnumConstant() )* ]
[ "," { jjtThis.setTrailingComma(); } ]
[ ";" { jjtThis.setSeparatorSemi(); } ( ClassOrInterfaceBodyDeclaration() )* ]
"}"
}

View File

@ -10,7 +10,7 @@ package net.sourceforge.pmd.lang.java.ast;
* <pre class="grammar">
*
* EnumBody ::= "{"
* [( {@link ASTAnnotation Annotation} )* {@link ASTEnumConstant EnumConstant} ( "," ( {@link ASTAnnotation Annotation} )* {@link ASTEnumConstant EnumConstant} )* ]
* [ {@link ASTEnumConstant EnumConstant} ( "," ( {@link ASTEnumConstant EnumConstant} )* ]
* [ "," ]
* [ ";" ( {@link ASTClassOrInterfaceBodyDeclaration ClassOrInterfaceBodyDeclaration} )* ]
* "}"
@ -21,6 +21,9 @@ package net.sourceforge.pmd.lang.java.ast;
*/
public final class ASTEnumBody extends AbstractJavaNode implements ASTTypeBody {
private boolean trailingComma;
private boolean separatorSemi;
ASTEnumBody(int id) {
super(id);
}
@ -35,4 +38,43 @@ public final class ASTEnumBody extends AbstractJavaNode implements ASTTypeBody {
public <T> void jjtAccept(SideEffectingVisitor<T> visitor, T data) {
visitor.visit(this, data);
}
void setTrailingComma() {
this.trailingComma = true;
}
void setSeparatorSemi() {
this.separatorSemi = true;
}
/**
* Returns true if the last enum constant has a trailing comma.
* For example:
* <pre>{@code
* enum Foo { A, B, C, }
* enum Bar { , }
* }</pre>
*/
public boolean hasTrailingComma() {
return trailingComma;
}
/**
* Returns true if the last enum constant has a trailing semi-colon.
* This semi is not optional when the enum has other members.
* For example:
* <pre>{@code
* enum Foo {
* A(2);
*
* Foo(int i) {...}
* }
*
* enum Bar { A; }
* enum Baz { ; }
* }</pre>
*/
public boolean hasSeparatorSemi() {
return separatorSemi;
}
}

View File

@ -55,6 +55,75 @@ class ASTEnumConstantTest : ParserTestSpec({
}
parserTest("Corner cases with separators") {
inContext(TopLevelTypeDeclarationParsingCtx) {
"enum Foo { A, }" should parseAs {
enumDecl("Foo") {
modifiers { }
enumBody {
it::hasTrailingComma shouldBe true
enumConstant("A")
}
}
}
"enum Foo { , }" should parseAs {
enumDecl("Foo") {
modifiers { }
enumBody {
it::hasTrailingComma shouldBe true
}
}
}
"enum Foo { ,; }" should parseAs {
enumDecl("Foo") {
modifiers { }
enumBody {
it::hasTrailingComma shouldBe true
it::hasSeparatorSemi shouldBe true
}
}
}
"enum Foo { ,, }" shouldNot parse()
"enum Foo { ; }" should parseAs {
enumDecl("Foo") {
modifiers { }
enumBody {
it::hasTrailingComma shouldBe false
it::hasSeparatorSemi shouldBe true
}
}
}
"enum Foo { ;; }" should parseAs {
enumDecl("Foo") {
modifiers { }
enumBody {
it::hasTrailingComma shouldBe false
it::hasSeparatorSemi shouldBe true
child<ASTClassOrInterfaceBodyDeclaration> {
child<ASTEmptyDeclaration> {}
}
}
}
}
}
}
parserTest("Enum constants should have an anonymous class node") {
inContext(TopLevelTypeDeclarationParsingCtx) {
@ -96,7 +165,6 @@ class ASTEnumConstantTest : ParserTestSpec({
it::getModifiers shouldBe modifiers {}
enumBody {
enumConstant("B") {
val c = it
@ -160,6 +228,7 @@ class ASTEnumConstantTest : ParserTestSpec({
}
it::getAnonymousClass shouldBe null
}
}
}

View File

@ -124,7 +124,6 @@ fun TreeNodeWrapper<Node, *>.enumDecl(name: String, spec: NodeSpec<ASTEnumDeclar
spec()
}
fun TreeNodeWrapper<Node, *>.enumBody(contents: NodeSpec<ASTEnumBody> = EmptyAssertions) =
child<ASTEnumBody> {
contents()