From c5fbfe5d873c1e3975ab8bbe0ebbe36d60224d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 7 Feb 2020 18:23:42 +0100 Subject: [PATCH 1/3] Make enum body track trailing comma/semi --- pmd-java/etc/grammar/Java.jjt | 6 +- .../pmd/lang/java/ast/ASTEnumBody.java | 44 ++++++++++- .../pmd/lang/java/ast/JavaParser.java | 1 + .../pmd/lang/java/ast/ASTEnumConstantTest.kt | 78 ++++++++++++++++++- .../pmd/lang/java/ast/TestExtensions.kt | 1 - 5 files changed, 121 insertions(+), 9 deletions(-) diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 716fb96d19..0ffe73cb94 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -923,9 +923,9 @@ void EnumBody(): {} { "{" - [ EnumConstant() ( LOOKAHEAD(2) "," EnumConstant() )* ] - [ "," ] - [ ";" ( ClassOrInterfaceBodyDeclaration() )* ] + [ EnumConstant() ( LOOKAHEAD(2) "," EnumConstant() )* ] + [ "," { jjtThis.setTrailingComma(); } ] + [ ";" { jjtThis.setSeparatorSemi(); } ( ClassOrInterfaceBodyDeclaration() )* ] "}" } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumBody.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumBody.java index a7e41788c8..177b5728be 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumBody.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumBody.java @@ -10,7 +10,7 @@ package net.sourceforge.pmd.lang.java.ast; *
  *
  * 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  void jjtAccept(SideEffectingVisitor 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:
+     * 
{@code
+     * enum Foo { A, B, C, }
+     * enum Bar { , }
+     * }
+ */ + 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: + *
{@code
+     * enum Foo {
+     *   A(2);
+     *
+     *   Foo(int i) {...}
+     * }
+     *
+     * enum Bar { A; }
+     * enum Baz { ; }
+     * }
+ */ + public boolean hasSeparatorSemi() { + return separatorSemi; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java index a87490e60e..d37ccad92a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java @@ -57,6 +57,7 @@ public class JavaParser extends JjtreeParserAdapter { parser.setPreview(checker.isPreviewEnabled()); ASTCompilationUnit acu = parser.CompilationUnit(); + acu.setTokenDocument(cs.getTokenDocument()); acu.setNoPmdComments(parser.getSuppressMap()); checker.check(acu); return acu; diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt index 2a7b399333..baac4c9d56 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt @@ -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 { + child {} + } + } + } + } + } + } + + 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") { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt index c2367a45e8..d23cf9f487 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt @@ -124,7 +124,6 @@ fun TreeNodeWrapper.enumDecl(name: String, spec: NodeSpec.enumBody(contents: NodeSpec = EmptyAssertions) = child { contents() From 3ec340cc92c1b734a71b4cf2927ef894db730b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 8 Feb 2020 01:17:20 +0100 Subject: [PATCH 2/3] Fix compil --- .../main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java index d37ccad92a..a87490e60e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java @@ -57,7 +57,6 @@ public class JavaParser extends JjtreeParserAdapter { parser.setPreview(checker.isPreviewEnabled()); ASTCompilationUnit acu = parser.CompilationUnit(); - acu.setTokenDocument(cs.getTokenDocument()); acu.setNoPmdComments(parser.getSuppressMap()); checker.check(acu); return acu; From 4b4f76b62fdf826e242f35d34a43abb7b0f0cf37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 8 Feb 2020 01:45:20 +0100 Subject: [PATCH 3/3] Fix forgotten rbrace --- .../pmd/lang/java/ast/ASTEnumConstantTest.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt index baac4c9d56..054ebb103c 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt @@ -63,7 +63,7 @@ class ASTEnumConstantTest : ParserTestSpec({ "enum Foo { A, }" should parseAs { enumDecl("Foo") { - modifiers { } + modifiers { } enumBody { it::hasTrailingComma shouldBe true enumConstant("A") @@ -74,7 +74,7 @@ class ASTEnumConstantTest : ParserTestSpec({ "enum Foo { , }" should parseAs { enumDecl("Foo") { - modifiers { } + modifiers { } enumBody { it::hasTrailingComma shouldBe true @@ -85,7 +85,7 @@ class ASTEnumConstantTest : ParserTestSpec({ "enum Foo { ,; }" should parseAs { enumDecl("Foo") { - modifiers { } + modifiers { } enumBody { it::hasTrailingComma shouldBe true it::hasSeparatorSemi shouldBe true @@ -98,7 +98,7 @@ class ASTEnumConstantTest : ParserTestSpec({ "enum Foo { ; }" should parseAs { enumDecl("Foo") { - modifiers { } + modifiers { } enumBody { it::hasTrailingComma shouldBe false it::hasSeparatorSemi shouldBe true @@ -109,7 +109,7 @@ class ASTEnumConstantTest : ParserTestSpec({ "enum Foo { ;; }" should parseAs { enumDecl("Foo") { - modifiers { } + modifiers { } enumBody { it::hasTrailingComma shouldBe false @@ -165,9 +165,7 @@ class ASTEnumConstantTest : ParserTestSpec({ it::getModifiers shouldBe modifiers {} enumBody { - - - enumConstant("B") { + enumConstant("B") { val c = it @@ -229,11 +227,12 @@ class ASTEnumConstantTest : ParserTestSpec({ stringLit("\"str\"") } - it::getAnonymousClass shouldBe null + it::getAnonymousClass shouldBe null + } + } } } - } "enum Foo { B(\"str\") { } }" should parseAs { enumDecl("Foo") {