Make enum body track trailing comma/semi

This commit is contained in:
Clément Fournier
2020-02-07 18:23:42 +01:00
parent 91872dfe60
commit c5fbfe5d87
5 changed files with 121 additions and 9 deletions

View File

@ -923,9 +923,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

@ -57,6 +57,7 @@ public class JavaParser extends JjtreeParserAdapter<ASTCompilationUnit> {
parser.setPreview(checker.isPreviewEnabled());
ASTCompilationUnit acu = parser.CompilationUnit();
acu.setTokenDocument(cs.getTokenDocument());
acu.setNoPmdComments(parser.getSuppressMap());
checker.check(acu);
return acu;

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) {
@ -97,7 +166,8 @@ class ASTEnumConstantTest : ParserTestSpec({
enumBody {
enumConstant("B") {
enumConstant("B") {
val c = it
@ -159,11 +229,11 @@ class ASTEnumConstantTest : ParserTestSpec({
stringLit("\"str\"")
}
it::getAnonymousClass shouldBe null
}
}
it::getAnonymousClass shouldBe null
}
}
}
"enum Foo { B(\"str\") { } }" should parseAs {
enumDecl("Foo") {

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()