You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2019/11/05 12:44:16 UTC

[groovy] 01/04: GROOVY-8507, GROOVY-9301: support comma on last enum value in more cases

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit d6a306779a2a00520da523b96870d8fcf81c0cdf
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Nov 3 12:15:26 2019 -0600

    GROOVY-8507, GROOVY-9301: support comma on last enum value in more cases
    
    and reduce ambiguity within (antlr2) enumConstants loop
    
    (cherry picked from commit 91a7dfad2583780fcf429fee50764aaee0122bc0)
---
 src/main/antlr2/org/codehaus/groovy/antlr/groovy.g |  15 +-
 .../groovy/antlr/EnumSourceParsingTest.java        | 284 +++++++++++++++++----
 2 files changed, 240 insertions(+), 59 deletions(-)

diff --git a/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g b/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g
index 3f29054..41d5e29 100644
--- a/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g
+++ b/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g
@@ -1128,26 +1128,33 @@ enumBlock  {Token first = LT(1);}
         {#enumBlock = #(create(OBJBLOCK,"OBJBLOCK",first,LT(1)), #enumBlock);}
     ;
 
-/** Guard for enumConstants.  */
+/** Guard for enumConstants. */
 enumConstantsStart
     :   annotationsOpt IDENT (LCURLY | LPAREN | nls (SEMI | COMMA | declarationStart | RCURLY))
     ;
 
-/** Comma-separated list of one or more enum constant definitions.  */
+/** Comma-separated list of one or more enum constant definitions. */
 enumConstants
     :
         enumConstant
         ( options {generateAmbigWarnings=false;} :
-            (nls ( options {generateAmbigWarnings=false;} : SEMI! | RCURLY | declarationStart | constructorStart)) => {break;}
+            (nls (SEMI | enumConstantsEnd)) => {break;} // GROOVY-4438, GROOVY-9184
         |
             nls! COMMA! (
+                (nls enumConstantsEnd) => {break;} // GROOVY-8507, GROOVY-9301
+            |
                 (nls annotationsOpt IDENT) => nls! enumConstant
             |
-                (nls (SEMI! | RCURLY | classField)) => {break;}
+                (nls classField) => {break;}
             )
         )*
     ;
 
+enumConstantsEnd
+    options {generateAmbigWarnings=false;}
+    :   RCURLY | declarationStart | constructorStart | typeDefinitionStart
+    ;
+
 // An annotation field
 annotationField!  {Token first = LT(1);}
     :   mods:modifiersOpt!
diff --git a/src/test/org/codehaus/groovy/antlr/EnumSourceParsingTest.java b/src/test/org/codehaus/groovy/antlr/EnumSourceParsingTest.java
index 8a6d2a3..fd24cb4 100644
--- a/src/test/org/codehaus/groovy/antlr/EnumSourceParsingTest.java
+++ b/src/test/org/codehaus/groovy/antlr/EnumSourceParsingTest.java
@@ -28,93 +28,116 @@ public final class EnumSourceParsingTest extends SourceParserTest {
     public void testParseEnumConstants() {
         parse(getMethodName(), new StringReader(
             "enum E {\n" +
-            "    ONE, TWO, THREE\n" +
-            "}"));
+            "    X, Y, Z\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumConstantsMultiLine() {
         parse(getMethodName(), new StringReader(
             "enum E {\n" +
-            "    ONE,\n" +
-            "    TWO,\n" +
-            "    THREE,\n" +
-            "}"));
+            "    X,\n" +
+            "    Y,\n" +
+            "    Z,\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumConstantsMultiLine2() {
         parse(getMethodName(), new StringReader(
-            "enum ParseCode\n" +
+            "enum E\n" +
             "{\n" +
-            "    COMPLETE,\n" +
-            "    INCOMPLETE,\n" +
-            "    ERROR\n" +
-            "}"));
+            "    X,\n" +
+            "    Y,\n" +
+            "    Z\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumImplementsMultiLine() {
         parse(getMethodName(), new StringReader(
-            "enum ParseCode implements I\n" +
+            "enum E implements I\n" +
             "{\n" +
-            "    COMPLETE,\n" +
-            "    INCOMPLETE,\n" +
-            "    ERROR\n" +
-            "}"));
+            "    X,\n" +
+            "    Y,\n" +
+            "    Z\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumImplementsMultiLine2() {
         parse(getMethodName(), new StringReader(
-            "enum ParseCode\n" +
+            "enum E\n" +
             "implements I\n" +
             "{\n" +
-            "    COMPLETE,\n" +
-            "    INCOMPLETE,\n" +
-            "    ERROR\n" +
-            "}"));
+            "    X,\n" +
+            "    Y,\n" +
+            "    Z\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumConstantsOneLiner() {
         parse(getMethodName(), new StringReader(
-            "enum One { ONE, TWO, THREE }"));
+            "enum E { X, Y, Z }\n")
+        );
     }
 
     public void testParseEnumImplements() {
         parse(getMethodName(), new StringReader(
-            "enum Two implements I1 {\n" +
-            "ONE, TWO, THREE\n" +
-            "}"));
+            "enum E implements I {\n" +
+            "    X, Y, Z\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumWithValues() {
         parse(getMethodName(), new StringReader(
-            "enum Three1 {\n" +
-            "    ONE(1), TWO(2)\n\n" +
-            "    Three1(val) {\n" +
-            "        value = val\n" +
+            "enum E {\n" +
+            "    X(1), Y(2)\n\n" +
+            "    E(value) {\n" +
+            "        this.value = value\n" +
             "    }\n\n" +
-            "    private final int value" +
-            "}"));
+            "    private final int value\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumWithValues2() {
         parse(getMethodName(), new StringReader(
-            "enum Three1 {\n" +
-            "    @Annotation ONE(1), TWO(2)\n\n" +
-            "    Three1(val) {\n" +
-            "        value = val\n" +
+            "enum E {\n" +
+            "    @Annotation X(1), Y(2)\n\n" +
+            "    E(value) {\n" +
+            "        this.value = value\n" +
             "    }\n\n" +
-            "    private final int value" +
-            "}"));
+            "    private final int value\n" +
+            "}\n")
+        );
     }
 
     public void testParseEnumWithValues3() {
         parse(getMethodName(), new StringReader(
-            "enum NonFinal {\n" +
-            "    One(1), Two(2)\n" +
+            "enum E {\n" +
+            "    X(1), Y(2)\n" +
             "    Object value\n" + // different parsing without leading keyword
-            "    NonFinal(value) {\n" +
+            "    E(value) {\n" +
+            "        this.value = value\n" +
+            "    }\n" +
+            "}\n")
+        );
+    }
+
+    // GROOVY-9301
+    public void testParseEnumWithValues3a() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X(1), Y(2),\n" + // trailing comma
+            "    Object value\n" +
+            "    E(value) {\n" +
             "        this.value = value\n" +
             "    }\n" +
-            "}\n"));
+            "}\n")
+        );
     }
 
     public void testParseEnumWithValues4() {
@@ -127,26 +150,55 @@ public final class EnumSourceParsingTest extends SourceParserTest {
             "    CLUBS(Color.BLACK),\n" +
             "    DIAMONDS(Color.RED),\n" +
             "    HEARTS(Color.RED),\n" +
-            "    SPADES(Color.BLACK),\n" +
+            "    SPADES(Color.BLACK),\n" + // trailing comma
             "    \n" +
             "    final Color color\n" +
             "    Suit(Color color) {\n" +
             "        this.color = color\n" +
             "    }\n" +
-            "}\n"));
+            "}\n")
+        );
     }
 
-    public void testParseEnumWithMethodDefinitions() {
+    public void testParseEnumWithMethodDefinitions1() {
         parse(getMethodName(), new StringReader(
-            "enum Four {\n" +
-            "    ONE, TWO, THREE\n\n" +
-            "    def someMethod() { }\n" +
+            "enum E {\n" +
+            "    X, Y, Z\n\n" +
+            "    def m1() { }\n" +
             "    public m2(args) { }\n" +
             "    int m3(String arg) { }\n" +
-            "}"));
+            "}\n")
+        );
+    }
+
+    public void testParseEnumWithMethodDefinitions2() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n\n" +
+            "    def <T> T m() { }\n" +
+            "}\n")
+        );
+    }
+
+    public void testParseEnumWithMethodDefinitions2a() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n\n" +
+            "    final <T> T m() { }\n" +
+            "}\n")
+        );
     }
 
-    public void testParseEnumWithAnnotatedMethodDefinition() {
+    public void testParseEnumWithMethodDefinitions2b() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n\n" +
+            "    public <T> T m() { }\n" +
+            "}\n")
+        );
+    }
+
+    public void testParseEnumWithAnnotatedMethodDefinition1() {
         parse(getMethodName(), new StringReader(
             "enum Orientation {\n" +
             "    LANDSCAPE, PORTRAIT\n" +
@@ -155,16 +207,138 @@ public final class EnumSourceParsingTest extends SourceParserTest {
             "    String toString() {\n" +
             "        name().toLowerCase().capitalize()\n" +
             "    }\n" +
-            "}\n"));
+            "}\n")
+        );
+    }
+
+    // GROOVY-9301
+    public void testParseEnumWithAnnotatedMethodDefinition2() {
+        parse(getMethodName(), new StringReader(
+            "enum Orientation {\n" +
+            "    LANDSCAPE, PORTRAIT,\n" + // trailing comma
+            "    \n" +
+            "    @Override\n" +
+            "    String toString() {\n" +
+            "        name().toLowerCase().capitalize()\n" +
+            "    }\n" +
+            "}\n")
+        );
+    }
+
+    // GROOVY-9301
+    public void testParseEnumWithAnnotatedMethodDefinition3() {
+        parse(getMethodName(), new StringReader(
+            "enum Orientation {\n" +
+            "    LANDSCAPE, PORTRAIT,\n" + // trailing comma
+            "    \n" +
+            "    @Deprecated <T> T whatever() {\n" +
+            "    }\n" +
+            "}\n")
+        );
     }
 
     public void testParseCompleteEnum() {
         parse(getMethodName(), new StringReader(
-            "enum Five {\n" +
-            "    ONE { double eval(int v) { return (double) v } }, \n" +
-            "    TWO {\n" +
+            "enum E {\n" +
+            "    X { double eval(int v) { return (double) v } }, \n" +
+            "    Y {\n" +
             "        double eval(int v) { return (double) v + 1 }\n" +
-            "    }, THREE\n" +
-            "}"));
+            "    }, Z\n" +
+            "}")
+        );
+    }
+
+    public void testParseEnumWithInnerClass1() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n" +
+            "    class C { }\n" +
+            "}\n")
+        );
+    }
+
+    // GROOVY-8507
+    public void testParseEnumWithInnerClass1a() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z,\n" + // trailing comma
+            "    class C { }\n" +
+            "}\n")
+        );
+    }
+
+    public void testParseEnumWithInnerClass2() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n" +
+            "    enum E2 { A, B, C }\n" +
+            "}\n")
+        );
+    }
+
+    // GROOVY-8507
+    public void testParseEnumWithInnerClass2a() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z,\n" + // trailing comma
+            "    enum Another { A, B, C }\n" +
+            "}\n")
+        );
+    }
+
+    public void testParseEnumWithInnerClass3() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n" +
+            "    interface I { }\n" +
+            "}\n")
+        );
+    }
+
+    // GROOVY-8507
+    public void testParseEnumWithInnerClass3a() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z,\n" + // trailing comma
+            "    interface I { }\n" +
+            "}\n")
+        );
+    }
+
+    public void _FIXME_testParseEnumWithInnerClass4() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n" +
+            "    @interface A { }\n" +
+            "}\n")
+        );
+    }
+
+    public void _FIXME_testParseEnumWithInnerClass4a() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z,\n" + // trailing comma
+            "    @interface A { }\n" +
+            "}\n")
+        );
+    }
+
+    public void testParseEnumWithInnerClass5() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z\n" +
+            "    trait T { }\n" +
+            "}\n")
+        );
+    }
+
+    // GROOVY-8507
+    public void testParseEnumWithInnerClass5a() {
+        parse(getMethodName(), new StringReader(
+            "enum E {\n" +
+            "    X, Y, Z,\n" + // trailing comma
+            "    trait T { }\n" +
+            "}\n")
+        );
     }
 }