This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-10570 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 56e6aad03bae776f4886896fe69bd9be0c10e4c9 Author: Eric Milles <[email protected]> AuthorDate: Fri Apr 15 11:02:26 2022 -0500 GROOVY-10570: `@AnnotationCollector`: better error for missing `value()` --- .../transform/AnnotationCollectorTransform.java | 39 +++++++++++++--------- .../transform/AnnotationCollectorTest.groovy | 31 ++++++++++++++++- src/test/groovy/transform/Groovy10570.java | 27 +++++++++++++++ src/test/groovy/transform/Groovy10570emu.java | 34 +++++++++++++++++++ 4 files changed, 115 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/transform/AnnotationCollectorTransform.java b/src/main/java/org/codehaus/groovy/transform/AnnotationCollectorTransform.java index 18b7b31be3..1eaa870932 100644 --- a/src/main/java/org/codehaus/groovy/transform/AnnotationCollectorTransform.java +++ b/src/main/java/org/codehaus/groovy/transform/AnnotationCollectorTransform.java @@ -18,6 +18,7 @@ */ package org.codehaus.groovy.transform; +import groovy.lang.GroovyRuntimeException; import groovy.transform.AnnotationCollector; import org.apache.groovy.ast.tools.ClassNodeUtils; import org.codehaus.groovy.GroovyBugError; @@ -40,6 +41,7 @@ import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.control.SourceUnit; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -268,36 +270,43 @@ public class AnnotationCollectorTransform { } } - private static List<AnnotationNode> getTargetListFromClass(ClassNode alias) { - alias = getSerializeClass(alias); - Class<?> c = alias.getTypeClass(); + private static List<AnnotationNode> getTargetListFromClass(final ClassNode alias) { + ClassNode cn = getSerializeClass(alias); + Class<?> c = cn.getTypeClass(); Object[][] data; try { Method m = c.getMethod("value"); + if (!Modifier.isStatic(m.getModifiers())) + throw new NoSuchMethodException("non-static value()"); + data = (Object[][]) m.invoke(null); + return makeListOfAnnotations(data); + } catch (NoSuchMethodException | ClassCastException e) { + throw new GroovyRuntimeException("Expecting static method `Object[][] value()`" + + " in " + cn.toString(false) + ". Was it compiled from a Java source?"); } catch (Exception e) { throw new GroovyBugError(e); } - return makeListOfAnnotations(data); } // 2.5.3 and above gets from annotation attribute otherwise self - private static ClassNode getSerializeClass(ClassNode alias) { - List<AnnotationNode> annotations = alias.getAnnotations(ClassHelper.make(AnnotationCollector.class)); - if (!annotations.isEmpty()) { - AnnotationNode annotationNode = annotations.get(0); - Expression member = annotationNode.getMember("serializeClass"); - if (member instanceof ClassExpression) { - ClassExpression ce = (ClassExpression) member; - if (!ce.getType().getName().equals(AnnotationCollector.class.getName())) { - alias = ce.getType(); + private static ClassNode getSerializeClass(final ClassNode alias) { + List<AnnotationNode> collectors = alias.getAnnotations(ClassHelper.make(AnnotationCollector.class)); + if (!collectors.isEmpty()) { + assert collectors.size() == 1; + AnnotationNode collectorNode = collectors.get(0); + Expression serializeClass = collectorNode.getMember("serializeClass"); + if (serializeClass instanceof ClassExpression) { + ClassNode serializeClassType = serializeClass.getType(); + if (!serializeClassType.getName().equals(AnnotationCollector.class.getName())) { + return serializeClassType; } } } return alias; } - private static List<AnnotationNode> makeListOfAnnotations(Object[][] data) { + private static List<AnnotationNode> makeListOfAnnotations(final Object[][] data) { if (data.length == 0) { return Collections.emptyList(); } @@ -321,7 +330,7 @@ public class AnnotationCollectorTransform { return ret; } - private static Expression makeExpression(Object o) { + private static Expression makeExpression(final Object o) { if (o instanceof Class) { return new ClassExpression(ClassHelper.make((Class<?>) o)); } diff --git a/src/test/groovy/transform/AnnotationCollectorTest.groovy b/src/test/groovy/transform/AnnotationCollectorTest.groovy index 86968306a2..3af76052ef 100644 --- a/src/test/groovy/transform/AnnotationCollectorTest.groovy +++ b/src/test/groovy/transform/AnnotationCollectorTest.groovy @@ -257,10 +257,39 @@ class AnnotationCollectorTest extends GroovyTestCase { assert Foo.class.annotations.size() == 3 assert new Foo(a: 1, b: 2).toString() == "Foo(2)" ''', { ex -> - assert ex.message.contains("Could not find class for Transformation Processor MyProcessor declared by Alias") + assert ex.message.contains('Could not find class for Transformation Processor MyProcessor declared by Alias') } } + // GROOVY-10570 + void testCollectorOnJavaAnno() { + shouldNotCompile ''' + @groovy.transform.Groovy10570 // Java @interface with @AnnotationCollector + class Foo { + def bar + } + ''', { ex -> + assert ex.message.contains('Expecting static method `Object[][] value()` in groovy.transform.Groovy10570. Was it compiled from a Java source?') + } + } + + // GROOVY-10570 + void testCollectorOnJavaAnno2() { + assertScript ''' + @groovy.transform.Groovy10570emu // Java @interface with @AnnotationCollector and value array + class Foo { + def bar + } + assert Foo.class.annotations.size() == 1 + assert Foo.class.annotations[0].annotationType().name == 'groovy.transform.EqualsAndHashCode' + + // test application of "@EqualsAndHashCode(canEqual=false)" + groovy.test.GroovyAssert.shouldFail NoSuchMethodException,{ + Foo.class.getDeclaredMethod('canEqual', Object) + } + ''' + } + void testAnnotationOnAnnotation() { assertScript ''' import groovy.transform.* diff --git a/src/test/groovy/transform/Groovy10570.java b/src/test/groovy/transform/Groovy10570.java new file mode 100644 index 0000000000..6e99249e83 --- /dev/null +++ b/src/test/groovy/transform/Groovy10570.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import java.lang.annotation.*; + +@EqualsAndHashCode +@AnnotationCollector +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface Groovy10570 { } diff --git a/src/test/groovy/transform/Groovy10570emu.java b/src/test/groovy/transform/Groovy10570emu.java new file mode 100644 index 0000000000..de9ce9d073 --- /dev/null +++ b/src/test/groovy/transform/Groovy10570emu.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import java.lang.annotation.*; + +import static java.util.Collections.singletonMap; + +@AnnotationCollector(serializeClass=Groovy10570emu.Data.class) +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface Groovy10570emu { + class Data { + public static Object[][] value() { + return new Object[][] {{EqualsAndHashCode.class, singletonMap("useCanEqual", Boolean.FALSE)}}; + } + } +}
