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>'
+ ]
+
+ }
}