You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2021/09/28 20:18:29 UTC

[groovy] 01/01: GROOVY-10234: handle raw types in class signature without looping

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY-10234
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit e1280c3be1facfefe6df78ab9c73a69c5d06ef6f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Sep 28 15:02:32 2021 -0500

    GROOVY-10234: handle raw types in class signature without looping
    
      interface ConversionService<Impl extends ConversionService>
    
    https://github.com/micronaut-projects/micronaut-core/blob/3.0.x/core/src/main/java/io/micronaut/core/convert/ConversionService.java
---
 .../groovy/ast/decompiled/DecompiledClassNode.java | 16 ++++----
 .../groovy/ast/decompiled/TypeSignatureParser.java | 43 ++++++++++++++--------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 40 ++++++++++++++++++++
 3 files changed, 76 insertions(+), 23 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java b/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java
index f6a81ff..8156670 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java
@@ -45,9 +45,9 @@ public class DecompiledClassNode extends ClassNode {
     private volatile boolean supersInitialized;
     private volatile boolean membersInitialized;
 
-    public DecompiledClassNode(ClassStub data, AsmReferenceResolver resolver) {
-        super(data.className, getFullModifiers(data), null, null, MixinNode.EMPTY_ARRAY);
-        classData = data;
+    public DecompiledClassNode(final ClassStub classData, final AsmReferenceResolver resolver) {
+        super(classData.className, getFullModifiers(classData), null, null, MixinNode.EMPTY_ARRAY);
+        this.classData = classData;
         this.resolver = resolver;
         isPrimaryNode = false;
     }
@@ -168,6 +168,10 @@ public class DecompiledClassNode extends ClassNode {
         throw new UnsupportedOperationException();
     }
 
+    public boolean isParameterized() {
+        return (classData.signature != null && classData.signature.charAt(0) == '<');
+    }
+
     @Override
     public boolean isResolved() {
         return true;
@@ -204,12 +208,11 @@ public class DecompiledClassNode extends ClassNode {
 
         synchronized (lazyInitLock) {
             if (!supersInitialized) {
-                ClassSignatureParser.configureClass(this, this.classData, this.resolver);
+                ClassSignatureParser.configureClass(this, classData, resolver);
                 addAnnotations(classData, this);
                 supersInitialized = true;
             }
         }
-
     }
 
     private void lazyInitMembers() {
@@ -284,5 +287,4 @@ public class DecompiledClassNode extends ClassNode {
         }
         return node;
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java b/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java
index 691d7e8..43cc00e 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java
@@ -58,7 +58,7 @@ abstract class TypeSignatureParser extends SignatureVisitor {
         final TypeSignatureParser outer = this;
         return new TypeSignatureParser(resolver) {
             @Override
-            void finished(ClassNode result) {
+            void finished(final ClassNode result) {
                 outer.finished(result.makeArray());
             }
         };
@@ -91,14 +91,6 @@ abstract class TypeSignatureParser extends SignatureVisitor {
         };
     }
 
-    private static GenericsType createWildcard(final ClassNode[] upper, final ClassNode lower) {
-        ClassNode base = ClassHelper.makeWithoutCaching("?");
-        base.setRedirect(ClassHelper.OBJECT_TYPE);
-        GenericsType t = new GenericsType(base, upper, lower);
-        t.setWildcard(true);
-        return t;
-    }
-
     @Override
     public void visitInnerClassType(final String name) {
         baseName += "$" + name;
@@ -107,14 +99,33 @@ abstract class TypeSignatureParser extends SignatureVisitor {
 
     @Override
     public void visitEnd() {
-        ClassNode base = resolver.resolveClass(baseName);
-        if (arguments.isEmpty()) {
-            finished(base);
-            return;
+        ClassNode baseType = resolver.resolveClass(baseName);
+        if (arguments.isEmpty() && isNotParameterized(baseType)) {
+            finished(baseType);
+        } else {
+            ClassNode parameterizedType = baseType.getPlainNodeReference();
+            if (!arguments.isEmpty()) { // else GROOVY-10234: no type arguments -> raw type
+                parameterizedType.setGenericsTypes(arguments.toArray(GenericsType.EMPTY_ARRAY));
+            }
+            finished(parameterizedType);
         }
+    }
 
-        ClassNode bound = base.getPlainNodeReference();
-        bound.setGenericsTypes(arguments.toArray(GenericsType.EMPTY_ARRAY));
-        finished(bound);
+    //--------------------------------------------------------------------------
+
+    private static GenericsType createWildcard(final ClassNode[] upper, final ClassNode lower) {
+        ClassNode base = ClassHelper.makeWithoutCaching("?");
+        base.setRedirect(ClassHelper.OBJECT_TYPE);
+        GenericsType t = new GenericsType(base, upper, lower);
+        t.setWildcard(true);
+        return t;
+    }
+
+    private static boolean isNotParameterized(final ClassNode cn) {
+        // DecompiledClassNode may not have generics initialized
+        if (cn instanceof DecompiledClassNode) {
+            return !((DecompiledClassNode) cn).isParameterized();
+        }
+        return (cn.getGenericsTypes() == null);
     }
 }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 5a8e324..76edb23 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -3634,6 +3634,46 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         }
     }
 
+    // GROOVY-10234
+    void testSelfReferentialTypeParameter() {
+        config.with {
+            targetDirectory = File.createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+        File parentDir = File.createTempDir()
+        try {
+            def a = new File(parentDir, 'Main.groovy')
+            a.write '''
+                def <T> T getBean(Class<T> beanType) {
+                    { obj, Class target -> Optional.of(obj.toString()) } as Service
+                }
+
+                def result = getBean(Service).convert(new ArrayList(), String)
+                assert result.get() == '[]'
+            '''
+            def b = new File(parentDir, 'Service.java')
+            b.write '''
+                import java.util.Optional;
+                import java.util.function.Function;
+
+                public interface Service<Impl extends Service> {
+                    <T> Optional<T> convert(Object object, Class<T> targetType);
+                    <S, T> Impl addConverter(Class<S> sourceType, Class<T> targetType, Function<S, T> typeConverter);
+                }
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b)
+            cu.compile()
+
+            loader.loadClass('Main').main()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
     // GROOVY-7804
     void testParameterlessClosureToGenericSAMTypeArgumentCoercion() {
         assertScript '''