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 2020/02/23 11:53:27 UTC

[groovy] branch GROOVY_3_0_X updated: Tweak `classDeclaration` rule

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


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new ea468d3  Tweak `classDeclaration` rule
ea468d3 is described below

commit ea468d342b58ec41fd06d2cd89c0e5b574c702ae
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Feb 23 19:02:18 2020 +0800

    Tweak `classDeclaration` rule
    
    (cherry picked from commit 907ec453f595b7e907b6730cbe53b3b20dd7999f)
---
 src/antlr/GroovyParser.g4                          |  35 +------
 src/test/gls/generics/GenericsUsageTest.groovy     |   4 +-
 .../apache/groovy/parser/antlr4/AstBuilder.java    |  53 +++++++++-
 .../groovy/parser/antlr4/SyntaxErrorTest.groovy    | 107 +++++++++++++++++++++
 4 files changed, 162 insertions(+), 37 deletions(-)

diff --git a/src/antlr/GroovyParser.g4 b/src/antlr/GroovyParser.g4
index f075237..ef6cd79 100644
--- a/src/antlr/GroovyParser.g4
+++ b/src/antlr/GroovyParser.g4
@@ -221,36 +221,11 @@ locals[ int t ]
         |   AT INTERFACE { $t = 3; }
         |   TRAIT { $t = 4; }
         )
-        identifier nls
-
-        (
-            { 3 != $t }?
-            (typeParameters nls)?
-            (
-                { 2 != $t }?
-                EXTENDS nls
-                    (
-                        // Only interface can extend more than one super class
-                        {1 == $t}? scs=typeList
-                    |
-                        sc=type
-                    )
-                nls
-            |
-                /* enum should not have type parameters and extends */
-            )
-
-            (
-                {1 != $t}?
-                IMPLEMENTS nls is=typeList nls
-            |
-                /* interface should not implement other interfaces */
-            )
-        |
-            /* annotation should not have implements and extends*/
-        )
-
-        classBody[$t]
+        identifier
+        (nls typeParameters)?
+        (nls EXTENDS nls scs=typeList)?
+        (nls IMPLEMENTS nls is=typeList)?
+        nls classBody[$t]
     ;
 
 // t    see the comment of classDeclaration
diff --git a/src/test/gls/generics/GenericsUsageTest.groovy b/src/test/gls/generics/GenericsUsageTest.groovy
index 3b07463..8ef2e56 100644
--- a/src/test/gls/generics/GenericsUsageTest.groovy
+++ b/src/test/gls/generics/GenericsUsageTest.groovy
@@ -214,7 +214,7 @@ final class GenericsUsageTest extends CompilableTestSupport {
 
             shouldFailCompilationWithMessage """
                 abstract class ArrayList2<E> extends AbstractList<E implements List<E> {}
-            """, "Unexpected input: 'AbstractList<E implements'"
+            """, "Unexpected input: '<'"
 
             shouldFailCompilationWithMessage """
                 abstract class ArrayList3<E> extends AbstractList<E> implements List<E {}
@@ -312,7 +312,7 @@ final class GenericsUsageTest extends CompilableTestSupport {
         } else {
             shouldFailCompilationWithMessages '''
                 class MyList extends ArrayList<> {}
-            ''', ['Unexpected input: \'ArrayList<>\'']
+            ''', ['Unexpected input: \'<\'']
         }
 
         shouldFailCompilationWithMessages '''
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 9b2780e..82baee7 100644
--- a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -1082,6 +1082,39 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
             throw createParsingFailedException("var cannot be used for type declarations", ctx.identifier());
         }
 
+        boolean isAnnotation = asBoolean(ctx.AT());
+        if (isAnnotation) {
+            if (asBoolean(ctx.typeParameters())) {
+                throw createParsingFailedException("annotation declaration cannot have type parameters", ctx.typeParameters());
+            }
+
+            if (asBoolean(ctx.EXTENDS())) {
+                throw createParsingFailedException("No extends clause allowed for annotation declaration", ctx.EXTENDS());
+            }
+
+            if (asBoolean(ctx.IMPLEMENTS())) {
+                throw createParsingFailedException("No implements clause allowed for annotation declaration", ctx.IMPLEMENTS());
+            }
+        }
+
+        boolean isEnum = asBoolean(ctx.ENUM());
+        if (isEnum) {
+            if (asBoolean(ctx.typeParameters())) {
+                throw createParsingFailedException("enum declaration cannot have type parameters", ctx.typeParameters());
+            }
+
+            if (asBoolean(ctx.EXTENDS())) {
+                throw createParsingFailedException("No extends clause allowed for enum declaration", ctx.EXTENDS());
+            }
+        }
+
+        boolean isInterface = (asBoolean(ctx.INTERFACE()) && !isAnnotation);
+        if (isInterface) {
+            if (asBoolean(ctx.IMPLEMENTS())) {
+                throw createParsingFailedException("No implements clause allowed for interface declaration", ctx.IMPLEMENTS());
+            }
+        }
+
         List<ModifierNode> modifierNodeList = ctx.getNodeMetaData(TYPE_DECLARATION_MODIFIERS);
         Objects.requireNonNull(modifierNodeList, "modifierNodeList should not be null");
         ModifierManager modifierManager = new ModifierManager(this, modifierNodeList);
@@ -1091,7 +1124,8 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         modifiers &= ~Opcodes.ACC_SYNTHETIC;
 
         ClassNode classNode, outerClass = classNodeStack.peek();
-        if (asBoolean(ctx.ENUM())) {
+
+        if (isEnum) {
             classNode = EnumHelper.makeEnumNode(
                     asBoolean(outerClass) ? className : packageName + className,
                     modifiers,
@@ -1117,7 +1151,6 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         configureAST(classNode, ctx);
         classNode.setSyntheticPublic(syntheticPublic);
         classNode.setGenericsTypes(this.visitTypeParameters(ctx.typeParameters()));
-        boolean isInterface = (asBoolean(ctx.INTERFACE()) && !asBoolean(ctx.AT()));
         boolean isInterfaceWithDefaultMethods = (isInterface && this.containsDefaultMethods(ctx));
 
         if (isInterfaceWithDefaultMethods || asBoolean(ctx.TRAIT())) {
@@ -1131,7 +1164,17 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         classNode.putNodeMetaData(CLASS_NAME, className);
 
         if (asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT()) || isInterfaceWithDefaultMethods) {
-            classNode.setSuperClass(this.visitType(ctx.sc));
+            ClassNode superClass;
+            if (asBoolean(ctx.scs)) {
+                ClassNode[] scs = this.visitTypeList(ctx.scs);
+                if (scs.length > 1) {
+                    throw createParsingFailedException("Cannot extend multiple classes", ctx.EXTENDS());
+                }
+                superClass = scs[0];
+            } else {
+                superClass = ClassHelper.OBJECT_TYPE;
+            }
+            classNode.setSuperClass(superClass);
             classNode.setInterfaces(this.visitTypeList(ctx.is));
             this.initUsingGenerics(classNode);
 
@@ -1142,12 +1185,12 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
             this.initUsingGenerics(classNode);
             this.hackMixins(classNode);
 
-        } else if (asBoolean(ctx.ENUM())) {
+        } else if (isEnum) {
             classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_ENUM | Opcodes.ACC_FINAL);
             classNode.setInterfaces(this.visitTypeList(ctx.is));
             this.initUsingGenerics(classNode);
 
-        } else if (asBoolean(ctx.AT())) {
+        } else if (isAnnotation) {
             classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_ANNOTATION);
             classNode.addInterface(ClassHelper.Annotation_TYPE);
             this.hackMixins(classNode);
diff --git a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
index 98d2a1c..fc7cebd 100644
--- a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
+++ b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
@@ -284,6 +284,113 @@ test.groovy: 2: Missing ')' @ line 2, column 22.
 '''
     }
 
+
+    void "test groovy core - AnnotationDeclaration 1"() {
+        def err = expectFail '''\
+@interface A<T> {}
+        '''
+
+        assert err == '''\
+startup failed:
+test.groovy: 1: annotation declaration cannot have type parameters @ line 1, column 13.
+   @interface A<T> {}
+               ^
+
+1 error
+'''
+    }
+
+    void "test groovy core - AnnotationDeclaration 2"() {
+        def err = expectFail '''\
+@interface A extends Object {}
+        '''
+
+        assert err == '''\
+startup failed:
+test.groovy: 1: No extends clause allowed for annotation declaration @ line 1, column 14.
+   @interface A extends Object {}
+                ^
+
+1 error
+'''
+    }
+
+    void "test groovy core - AnnotationDeclaration 3"() {
+        def err = expectFail '''\
+@interface A implements Serializable {}
+        '''
+
+        assert err == '''\
+startup failed:
+test.groovy: 1: No implements clause allowed for annotation declaration @ line 1, column 14.
+   @interface A implements Serializable {}
+                ^
+
+1 error
+'''
+    }
+
+    void "test groovy core - EnumDeclaration 1"() {
+        def err = expectFail '''\
+enum E<T> {}
+        '''
+
+        assert err == '''\
+startup failed:
+test.groovy: 1: enum declaration cannot have type parameters @ line 1, column 7.
+   enum E<T> {}
+         ^
+
+1 error
+'''
+    }
+
+    void "test groovy core - EnumDeclaration 2"() {
+        def err = expectFail '''\
+enum E extends Object {}
+        '''
+
+        assert err == '''\
+startup failed:
+test.groovy: 1: No extends clause allowed for enum declaration @ line 1, column 8.
+   enum E extends Object {}
+          ^
+
+1 error
+'''
+    }
+
+    void "test groovy core - InterfaceDeclaration 1"() {
+        def err = expectFail '''\
+interface I implements Serializable {}
+        '''
+
+        assert err == '''\
+startup failed:
+test.groovy: 1: No implements clause allowed for interface declaration @ line 1, column 13.
+   interface I implements Serializable {}
+               ^
+
+1 error
+'''
+    }
+
+    void "test test groovy core - ClassDeclaration 1"() {
+        def err = expectFail '''\
+class C extends Object, Number {}
+        '''
+
+        assert err == '''\
+startup failed:
+test.groovy: 1: Cannot extend multiple classes @ line 1, column 9.
+   class C extends Object, Number {}
+           ^
+
+1 error
+'''
+    }
+
+
     //--------------------------------------------------------------------------
     def expectFail(String code) {
         try {