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')
+ }
}