You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2017/07/13 20:20:14 UTC

groovy git commit: GROOVY-8236: Report more meaningful error for versions of Groovy not supporting @Repeatable

Repository: groovy
Updated Branches:
  refs/heads/GROOVY_2_4_X 9aae804b0 -> 3f5dc7ea2


GROOVY-8236: Report more meaningful error for versions of Groovy not supporting @Repeatable


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/3f5dc7ea
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/3f5dc7ea
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/3f5dc7ea

Branch: refs/heads/GROOVY_2_4_X
Commit: 3f5dc7ea2052116fd1219cc16601e956b83caefa
Parents: 9aae804
Author: paulk <pa...@asert.com.au>
Authored: Fri Jul 14 06:19:48 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Fri Jul 14 06:19:48 2017 +1000

----------------------------------------------------------------------
 .../groovy/classgen/ExtendedVerifier.java       | 58 +++++++++++++++++---
 src/test/gls/annotations/AnnotationTest.groovy  | 38 +++++++++++++
 2 files changed, 89 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/3f5dc7ea/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java b/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
index 611c67c..6666379 100644
--- a/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
+++ b/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -19,7 +19,9 @@
 package org.codehaus.groovy.classgen;
 
 import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.stmt.ReturnStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.ast.tools.ParameterUtils;
@@ -34,6 +36,7 @@ import org.objectweb.asm.Opcodes;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -135,21 +138,62 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
             addError("Annotations are not supported in the current runtime. " + JVM_ERROR_MESSAGE, node);
             return;
         }
+        Map<String, List<AnnotationNode>> runtimeAnnotations = new LinkedHashMap<String, List<AnnotationNode>>();
         for (AnnotationNode unvisited : node.getAnnotations()) {
             AnnotationNode visited = visitAnnotation(unvisited);
-            boolean isTargetAnnotation = visited.getClassNode().isResolved() &&
-                    visited.getClassNode().getName().equals("java.lang.annotation.Target");
+            String name = visited.getClassNode().getName();
+            if (visited.hasRuntimeRetention()) {
+                List<AnnotationNode> seen = runtimeAnnotations.get(name);
+                if (seen == null) {
+                    seen = new ArrayList<AnnotationNode>();
+                }
+                seen.add(visited);
+                runtimeAnnotations.put(name, seen);
+            }
+            boolean isTargetAnnotation = name.equals("java.lang.annotation.Target");
 
             // Check if the annotation target is correct, unless it's the target annotating an annotation definition
             // defining on which target elements the annotation applies
             if (!isTargetAnnotation && !visited.isTargetAllowed(target)) {
-                addError("Annotation @" + visited.getClassNode().getName()
-                        + " is not allowed on element " + AnnotationNode.targetToName(target),
-                        visited);
+                addError("Annotation @" + name + " is not allowed on element "
+                        + AnnotationNode.targetToName(target), visited);
             }
             visitDeprecation(node, visited);
             visitOverride(node, visited);
         }
+        checkForDuplicateAnnotations(runtimeAnnotations);
+    }
+
+    private void checkForDuplicateAnnotations(Map<String, List<AnnotationNode>> runtimeAnnotations) {
+        for (Map.Entry<String, List<AnnotationNode>> next : runtimeAnnotations.entrySet()) {
+            if (next.getValue().size() > 1) {
+                String repeatableName = null;
+                AnnotationNode repeatee = next.getValue().get(0);
+                List<AnnotationNode> repeateeAnnotations = repeatee.getClassNode().getAnnotations();
+                for (AnnotationNode anno : repeateeAnnotations) {
+                    ClassNode annoClassNode = anno.getClassNode();
+                    if (annoClassNode.getName().equals("java.lang.annotation.Repeatable")) {
+                        Expression value = anno.getMember("value");
+                        if (value instanceof ClassExpression) {
+                            ClassExpression ce = (ClassExpression) value;
+                            if (ce.getType() != null && ce.getType().isAnnotationDefinition()) {
+                                repeatableName = ce.getType().getName();
+                            }
+                        }
+                        break;
+                    }
+                }
+                // TODO: further checks: that repeatableName is valid and has RUNTIME retention?
+                if (repeatableName != null) {
+                    addError("Annotation @" + next.getKey() + " has RUNTIME retention and " + next.getValue().size()
+                            + " occurrences. Automatic repeated annotations are not supported in this version of Groovy. " +
+                            "Consider using the explicit @" + repeatableName + " collector annotation instead.", next.getValue().get(1));
+                } else {
+                    addError("Annotation @" + next.getKey() + " has RUNTIME retention and " + next.getValue().size()
+                            + " occurrences. Duplicate annotations not allowed.", next.getValue().get(1));
+                }
+            }
+        }
     }
 
     private static void visitDeprecation(AnnotatedNode node, AnnotationNode visited) {
@@ -222,7 +266,7 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
             }
             ClassNode superClass = next.getUnresolvedSuperClass();
             if (superClass != null) {
-                next =  correctToGenericsSpecRecurse(updatedGenericsSpec, superClass);
+                next = correctToGenericsSpecRecurse(updatedGenericsSpec, superClass);
             } else {
                 next = null;
             }
@@ -231,7 +275,7 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
     }
 
     private static MethodNode getDeclaredMethodCorrected(Map genericsSpec, MethodNode mn, ClassNode correctedNext) {
-        for (MethodNode orig :  correctedNext.getDeclaredMethods(mn.getName())) {
+        for (MethodNode orig : correctedNext.getDeclaredMethods(mn.getName())) {
             MethodNode method = correctToGenericsSpec(genericsSpec, orig);
             if (ParameterUtils.parametersEqual(method.getParameters(), mn.getParameters())) {
                 return method;

http://git-wip-us.apache.org/repos/asf/groovy/blob/3f5dc7ea/src/test/gls/annotations/AnnotationTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/gls/annotations/AnnotationTest.groovy b/src/test/gls/annotations/AnnotationTest.groovy
index 39a24ae..175adce 100644
--- a/src/test/gls/annotations/AnnotationTest.groovy
+++ b/src/test/gls/annotations/AnnotationTest.groovy
@@ -655,4 +655,42 @@ class AnnotationTest extends CompilableTestSupport {
             }
         '''
     }
+
+    // GROOVY-8236
+    void testAnnotationWithRepeated() {
+        def errorMessage = shouldNotCompile '''
+            import java.lang.annotation.*
+
+            class MyClass {
+                @MyAnnotation(value = "val1")
+                @MyAnnotation(value = "val2")
+                //change annotation to next line and the code will work
+                //@MyAnnotationArray( [@MyAnnotation("val1"), @MyAnnotation("val2")] )
+                String annotatedMethod() {
+                    'foo'
+                }
+                static void main(String... args) {
+                    MyClass myc = new MyClass()
+                    assert 'foo' == myc.annotatedMethod()
+                    def m = myc.getClass().getMethod("annotatedMethod")
+                    List annos = m.getAnnotations()
+                    assert annos.size() == 1
+                }
+            }
+
+            @Target(ElementType.METHOD)
+            @Retention(RetentionPolicy.RUNTIME)
+            @Repeatable(MyAnnotationArray)
+            @interface MyAnnotation {
+                String value() default "val0"
+            }
+
+            @Retention(RetentionPolicy.RUNTIME)
+            @interface MyAnnotationArray {
+                MyAnnotation[] value()
+            }
+        '''
+        assert errorMessage.contains('Automatic repeated annotations are not supported')
+        assert errorMessage.contains('Consider using the explicit @MyAnnotationArray collector annotation')
+    }
 }