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 2019/04/30 04:22:33 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-8990: expecting compilation error for mismatched generics in the presence of multiple bounds

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

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new f32387f  GROOVY-8990: expecting compilation error for mismatched generics in the presence of multiple bounds
f32387f is described below

commit f32387f902338e196f84b503f180b07465d46f0c
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Apr 29 22:50:25 2019 +1000

    GROOVY-8990: expecting compilation error for mismatched generics in the presence of multiple bounds
---
 .../codehaus/groovy/control/GenericsVisitor.java   | 31 +++++++++++++++----
 .../transform/stc/StaticTypeCheckingSupport.java   |  2 +-
 src/test/gls/generics/GenericsTest.groovy          | 36 ++++++++++++++++++++++
 3 files changed, 62 insertions(+), 7 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java b/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java
index 022cbff..0231cb5 100644
--- a/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java
@@ -30,6 +30,8 @@ import org.codehaus.groovy.ast.expr.DeclarationExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.TupleExpression;
 
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUnboundedWildcard;
+
 /**
  * Verify correct usage of generics.
  * This includes:
@@ -163,12 +165,29 @@ public class GenericsVisitor extends ClassCodeVisitorSupport {
             ClassNode cnType = cnTypes[i].getType();
             // check nested type parameters
             checkGenericsUsage(nType, nType.redirect());
-            // check bounds
-            if (!nType.isDerivedFrom(cnType)) {
-                if (cnType.isInterface() && nType.implementsInterface(cnType)) continue;
-                addError("The type " + nTypes[i].getName() +
-                        " is not a valid substitute for the bounded parameter <" +
-                        getPrintName(cnTypes[i]) + ">", n);
+            // check bounds: unbounded wildcard (aka "?") is universal substitute
+            if (!isUnboundedWildcard(nTypes[i])) {
+                // check upper bound(s)
+                ClassNode[] bounds = cnTypes[i].getUpperBounds();
+
+                // first can be class or interface
+                boolean valid = nType.isDerivedFrom(cnType) || (cnType.isInterface() && nType.implementsInterface(cnType));
+
+                // subsequent bounds if present can be interfaces
+                if (valid && bounds != null && bounds.length > 1) {
+                    for (int j = 1; j < bounds.length; j++) {
+                        ClassNode bound = bounds[j];
+                        if (!nType.implementsInterface(bound)) {
+                            valid = false;
+                            break;
+                        }
+                    }
+                }
+
+                if (!valid) {
+                    addError("The type " + nTypes[i].getName() + " is not a valid substitute for the bounded parameter <" +
+                            getPrintName(cnTypes[i]) + ">", nTypes[i]);
+                }
             }
         }
     }
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index ee0ce49..5a4c2ee 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -2044,7 +2044,7 @@ public abstract class StaticTypeCheckingSupport {
         return gt;
     }
 
-    private static boolean isUnboundedWildcard(GenericsType gt) {
+    public static boolean isUnboundedWildcard(GenericsType gt) {
         if (gt.isWildcard() && gt.getLowerBound() == null) {
             ClassNode[] upperBounds = gt.getUpperBounds();
             return upperBounds == null ||
diff --git a/src/test/gls/generics/GenericsTest.groovy b/src/test/gls/generics/GenericsTest.groovy
index ae4258f..d401213 100644
--- a/src/test/gls/generics/GenericsTest.groovy
+++ b/src/test/gls/generics/GenericsTest.groovy
@@ -508,4 +508,40 @@ class ThreadId {
             List<String> ss = new LinkedList<>()
         '''
     }
+
+    // GROOVY-8990
+    void testCompilationErrorForMismatchedGenericsWithMultipleBounds() {
+        shouldFailCompilationWithMessages '''
+            class C1<T> {}
+
+            interface I1 {}
+            class C2 implements I1 {}
+
+            class C3<T extends I1> {}
+
+            class C4 extends C1<String> {} // String matches T
+
+            class C5 extends C3<String> {} // String not an I1
+
+            class C6 extends C3<C2> {} // C2 is an I1
+
+            class C7<T extends Number & I1> {}
+
+            class C8 extends C7<C2> {} // C2 not a Number
+            class C9 extends C7<Integer> {} // Integer not an I1
+
+            class C10 extends Number implements I1 {}
+            class C11 extends C7<C10> {} // C10 is a Number and implements I1
+
+            interface I2<T extends Number> {}
+            class C12 implements I2<String> {} // String not a Number
+            class C13 implements I2<C10> {} // C10 is a Number
+        ''', [
+            'The type String is not a valid substitute for the bounded parameter <T extends I1>',
+            'The type C2 is not a valid substitute for the bounded parameter <T extends java.lang.Number & I1>',
+            'The type Integer is not a valid substitute for the bounded parameter <T extends java.lang.Number & I1>',
+            'The type String is not a valid substitute for the bounded parameter <T extends java.lang.Number>'
+        ]
+
+    }
 }