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 2022/06/30 21:33:32 UTC

[groovy] branch master updated: GROOVY-10651, GROOVY-10671: wildcard implicit bounding

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 03886c5fe8 GROOVY-10651, GROOVY-10671: wildcard implicit bounding
03886c5fe8 is described below

commit 03886c5fe82cb8664da223f8cd82ada2c2c8b3c7
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Jun 30 15:35:25 2022 -0500

    GROOVY-10651, GROOVY-10671: wildcard implicit bounding
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   | 45 ++++++++++++++++------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 12 ++++++
 2 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 51275fb1fb..f8d376171c 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -130,14 +130,29 @@ public class GenericsUtils {
     }
 
     /**
-     * Generates a wildcard generic type in order to be used for checks against class nodes.
-     * See {@link GenericsType#isCompatibleWith(org.codehaus.groovy.ast.ClassNode)}.
+     * Generates a wildcard generic type with implicit upper and optional lower
+     * bounds. The result provides "?" or "? super T" (no "extends") semantics.
+     */
+    private static GenericsType boundWildcardType(final ClassNode implicit, final ClassNode lower) {
+        ClassNode base = ClassHelper.makeWithoutCaching("?");
+        base.setGenericsPlaceHolder(true);
+        base.setRedirect(implicit);
+
+        GenericsType gt = new GenericsType(base, null, lower);
+        gt.setPlaceholder(false);
+        gt.setWildcard(true);
+        return gt;
+    }
+
+    /**
+     * Generates a wildcard generic type in order to be used for checks against
+     * class nodes. See {@link GenericsType#isCompatibleWith(ClassNode)}.
      *
-     * @param types the type to be used as the wildcard upper bound
-     * @return a wildcard generics type
+     * @param types the type(s) to be used as the wildcard's upper bound
      */
     public static GenericsType buildWildcardType(final ClassNode... types) {
         ClassNode base = ClassHelper.makeWithoutCaching("?");
+
         GenericsType gt = new GenericsType(base, types, null);
         gt.setWildcard(true);
         return gt;
@@ -170,31 +185,37 @@ public class GenericsUtils {
         }
 
         if (!type.isUsingGenerics() || !type.isRedirectNode()) return;
-        GenericsType[] parameterized = type.getGenericsTypes(); int n;
-        if (parameterized == null || (n = parameterized.length) == 0) return;
+        GenericsType[] genericsTypes = type.getGenericsTypes(); int n;
+        if (genericsTypes == null || (n = genericsTypes.length) == 0) return;
 
         // GROOVY-8609, GROOVY-10067, etc.
         if (type.isGenericsPlaceHolder()) {
-            GenericsType gt = parameterized[0];
+            GenericsType gt = genericsTypes[0];
             placeholders.putIfAbsent(new GenericsType.GenericsTypeName(gt.getName()), gt);
             return;
         }
 
         GenericsType[] redirectGenericsTypes = type.redirect().getGenericsTypes();
         if (redirectGenericsTypes == null) {
-            redirectGenericsTypes = parameterized;
+            redirectGenericsTypes = genericsTypes;
         } else if (redirectGenericsTypes.length != n) {
             throw new GroovyBugError("Expected earlier checking to detect generics parameter arity mismatch" +
                     "\nExpected: " + type.getName() + toGenericTypesString(redirectGenericsTypes) +
-                    "\nSupplied: " + type.getName() + toGenericTypesString(parameterized));
+                    "\nSupplied: " + type.getName() + toGenericTypesString(genericsTypes));
         }
 
         List<GenericsType> typeArguments = new ArrayList<>(n);
         for (int i = 0; i < n; i += 1) {
             GenericsType rgt = redirectGenericsTypes[i];
-            if (rgt.isPlaceholder()) {
-                GenericsType typeArgument = parameterized[i];
-                placeholders.computeIfAbsent(new GenericsType.GenericsTypeName(rgt.getName()), name -> {
+            if (rgt.isPlaceholder()) { // type parameter
+                GenericsType typeArgument = genericsTypes[i];
+                placeholders.computeIfAbsent(new GenericsType.GenericsTypeName(rgt.getName()), x -> {
+                    if (typeArgument.isWildcard() && typeArgument.getUpperBounds() == null) {
+                        ClassNode[] implicitBounds = rgt.getUpperBounds();//GROOVY-10651,GROOVY-10671
+                        if (implicitBounds != null && !ClassHelper.isObjectType(implicitBounds[0])) {
+                            return boundWildcardType(implicitBounds[0],typeArgument.getLowerBound());
+                        }
+                    }
                     typeArguments.add(typeArgument);
                     return typeArgument;
                 });
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index a5762380cf..dd92c842a4 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -4824,6 +4824,18 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-10671
+    void testAssertJ() {
+        assertScript '''
+            @Grab('org.assertj:assertj-core:3.23.1')
+            import static org.assertj.core.api.Assertions.assertThat
+
+            def strings = (Collection<String>) ['a','b']
+            assertThat(strings).as('assertion description')
+                .containsExactlyInAnyOrderElementsOf(['a','b'])
+        '''
+    }
+
     //--------------------------------------------------------------------------
 
     static class MyList