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 2022/02/14 07:31:20 UTC
[groovy] branch GROOVY_4_0_X updated: GROOVY-10472: @AutoImplement is failing when covariant returns are involved
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new e597e92 GROOVY-10472: @AutoImplement is failing when covariant returns are involved
e597e92 is described below
commit e597e92f5524543d1a8b6f0136d193b9747b4e70
Author: Paul King <pa...@asert.com.au>
AuthorDate: Fri Feb 11 14:41:19 2022 +1000
GROOVY-10472: @AutoImplement is failing when covariant returns are involved
---
.../apache/groovy/ast/tools/ClassNodeUtils.java | 7 ++
.../transform/AutoImplementASTTransformation.java | 16 ++--
.../transform/AutoImplementTransformTest.groovy | 92 ++++++++++++++++++++++
3 files changed, 108 insertions(+), 7 deletions(-)
diff --git a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
index 993c959..510888e 100644
--- a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
@@ -50,6 +50,7 @@ import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
import static org.codehaus.groovy.ast.ClassHelper.isObjectType;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveBoolean;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements;
import static org.codehaus.groovy.runtime.ArrayTypeUtils.dimension;
import static org.codehaus.groovy.runtime.ArrayTypeUtils.elementType;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
@@ -448,4 +449,10 @@ public class ClassNodeUtils {
return null;
}
+
+ public static boolean isSubtype(ClassNode maybeSuperclassOrInterface, ClassNode candidateChild) {
+ return maybeSuperclassOrInterface.isInterface() || candidateChild.isInterface()
+ ? isOrImplements(candidateChild, maybeSuperclassOrInterface)
+ : candidateChild.isDerivedFrom(maybeSuperclassOrInterface);
+ }
}
diff --git a/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java
index a89a3ca..00db645 100644
--- a/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java
@@ -42,6 +42,7 @@ import java.util.List;
import java.util.Map;
import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.isSubtype;
import static org.apache.groovy.ast.tools.MethodNodeUtils.getPropertyName;
import static org.apache.groovy.ast.tools.MethodNodeUtils.methodDescriptorWithoutReturnType;
import static org.apache.groovy.util.BeanUtils.capitalize;
@@ -161,9 +162,7 @@ public class AutoImplementASTTransformation extends AbstractASTTransformation {
MethodNode found = getDeclaredMethodCorrected(genericsSpec, correctedMethod, correctedClass);
if (found != null) {
String td = methodDescriptorWithoutReturnType(found);
- if (result.containsKey(td) && !result.get(td).isAbstract()) {
- continue;
- }
+ if (result.containsKey(td) && isWeakerCandidate(result.get(td), found)) continue;
result.put(td, found);
}
}
@@ -182,9 +181,7 @@ public class AutoImplementASTTransformation extends AbstractASTTransformation {
MethodNode found = getDeclaredMethodCorrected(updatedGenericsSpec, correctedMethod, correctedInterface);
if (found != null) {
String td = methodDescriptorWithoutReturnType(found);
- if (result.containsKey(td) && !result.get(td).isAbstract()) {
- continue;
- }
+ if (result.containsKey(td) && isWeakerCandidate(result.get(td), found)) continue;
result.put(td, found);
}
}
@@ -221,10 +218,15 @@ public class AutoImplementASTTransformation extends AbstractASTTransformation {
}
}
}
-
return result;
}
+ private static boolean isWeakerCandidate(MethodNode existing, MethodNode found) {
+ return !(existing.isAbstract() && !found.isAbstract()) &&
+ // GROOVY-10472: prefer covariant method with more concrete type
+ isSubtype(found.getReturnType(), existing.getReturnType());
+ }
+
private static MethodNode getDeclaredMethodCorrected(final Map<String, ClassNode> genericsSpec, final MethodNode origMethod, final ClassNode correctedClass) {
for (MethodNode nameMatch : correctedClass.getDeclaredMethods(origMethod.getName())) {
MethodNode correctedMethod = correctToGenericsSpec(genericsSpec, nameMatch);
diff --git a/src/test/org/codehaus/groovy/transform/AutoImplementTransformTest.groovy b/src/test/org/codehaus/groovy/transform/AutoImplementTransformTest.groovy
index ba86e77..05b108f 100644
--- a/src/test/org/codehaus/groovy/transform/AutoImplementTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/AutoImplementTransformTest.groovy
@@ -216,4 +216,96 @@ final class AutoImplementTransformTest {
assert new Foo().compare('bar', 'baz') == 0
'''
}
+
+ @Test // GROOVY-10472
+ void testCovariantReturnTypes() {
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { List findAll() }
+ interface Sub extends Super { Iterable findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { ArrayList findAll() }
+ interface Sub extends Super { Iterable findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { Iterable findAll() }
+ interface Sub extends Super { List findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { Iterable findAll() }
+ interface Sub extends Super { ArrayList findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { AbstractList findAll() }
+ interface Sub extends Super { List findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { List findAll() }
+ interface Sub extends Super { AbstractList findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { AbstractList findAll() }
+ interface Sub extends Super { ArrayList findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ assertScript '''
+ import groovy.transform.AutoImplement
+
+ interface Super { ArrayList findAll() }
+ interface Sub extends Super { AbstractList findAll() }
+
+ @AutoImplement
+ class ThisClassFails implements Sub{}
+
+ assert !(new ThisClassFails().findAll())
+ '''
+ }
}