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/05/17 15:26:17 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-10365: type argument/parameter relationships: no type param bound
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new d9f6e9870a GROOVY-10365: type argument/parameter relationships: no type param bound
d9f6e9870a is described below
commit d9f6e9870a2f3cedb1eba8748ba75d9ef5288952
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue May 17 09:45:48 2022 -0500
GROOVY-10365: type argument/parameter relationships: no type param bound
3_0_X backport
---
.../java/org/codehaus/groovy/ast/GenericsType.java | 4 +-
.../codehaus/groovy/ast/tools/GenericsUtils.java | 64 +++++++++++++---------
.../groovy/transform/stc/GenericsSTCTest.groovy | 21 +++++++
3 files changed, 63 insertions(+), 26 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index ccef754fb4..97749967b4 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -115,6 +115,8 @@ public class GenericsType extends ASTNode {
private static StringBuilder appendName(final ClassNode theType, final StringBuilder sb) {
if (theType.isArray()) {
appendName(theType.getComponentType(), sb).append("[]");
+ } else if (theType.isGenericsPlaceHolder()) {
+ sb.append(theType.getUnresolvedName());
} else if (theType.getOuterClass() != null) {
String parentClassNodeName = theType.getOuterClass().getName();
if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) {
@@ -125,7 +127,7 @@ public class GenericsType extends ASTNode {
sb.append('.');
sb.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length());
} else {
- sb.append(theType.isGenericsPlaceHolder() ? theType.getUnresolvedName() : theType.getName());
+ sb.append(theType.getName());
}
return sb;
}
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 ff571acdb8..35390dc31b 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -40,6 +40,7 @@ import org.codehaus.groovy.runtime.memoize.ConcurrentSoftCache;
import org.codehaus.groovy.runtime.memoize.EvictableCache;
import java.lang.ref.SoftReference;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -141,6 +142,11 @@ public class GenericsUtils {
return gt;
}
+ /**
+ * Returns the type parameter/argument relationships of the specified type.
+ *
+ * @param type the class node to check
+ */
public static Map<GenericsType.GenericsTypeName, GenericsType> extractPlaceholders(final ClassNode type) {
Map<GenericsType.GenericsTypeName, GenericsType> placeholders = new HashMap<>();
extractPlaceholders(type, placeholders);
@@ -148,8 +154,8 @@ public class GenericsUtils {
}
/**
- * For a given classnode, fills in the supplied map with the parameterized
- * types it defines.
+ * Populates the supplied map with the type parameter/argument relationships
+ * of the specified type.
*
* @param type the class node to check
* @param placeholders the generics type information collector
@@ -163,45 +169,53 @@ public class GenericsUtils {
}
if (!type.isUsingGenerics() || !type.isRedirectNode()) return;
- GenericsType[] parameterized = type.getGenericsTypes();
- if (parameterized == null || parameterized.length == 0) return;
+ GenericsType[] parameterized = type.getGenericsTypes(); int n;
+ if (parameterized == null || (n = parameterized.length) == 0) return;
+
+ // GROOVY-8609, GROOVY-10067, etc.
+ if (type.isGenericsPlaceHolder()) {
+ GenericsType gt = parameterized[0];
+ placeholders.putIfAbsent(new GenericsType.GenericsTypeName(gt.getName()), gt);
+ return;
+ }
+
GenericsType[] redirectGenericsTypes = type.redirect().getGenericsTypes();
- if (redirectGenericsTypes == null || (type.isGenericsPlaceHolder() // GROOVY-8609
- && redirectGenericsTypes.length != parameterized.length)) {
+ if (redirectGenericsTypes == null) {
redirectGenericsTypes = parameterized;
- } else if (redirectGenericsTypes.length != parameterized.length) {
+ } 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));
}
- List<GenericsType> valueList = new LinkedList<>();
- for (int i = 0, n = redirectGenericsTypes.length; i < n; i += 1) {
+ List<GenericsType> typeArguments = new ArrayList<>(n);
+ for (int i = 0; i < n; i += 1) {
GenericsType rgt = redirectGenericsTypes[i];
if (rgt.isPlaceholder()) {
- GenericsType.GenericsTypeName name = new GenericsType.GenericsTypeName(rgt.getName());
- if (!placeholders.containsKey(name)) {
- GenericsType value = parameterized[i];
- placeholders.put(name, value);
- valueList.add(value);
- }
+ GenericsType typeArgument = parameterized[i];
+ placeholders.computeIfAbsent(new GenericsType.GenericsTypeName(rgt.getName()), name -> {
+ typeArguments.add(typeArgument);
+ return typeArgument;
+ });
}
}
- for (GenericsType value : valueList) {
- if (value.isWildcard()) {
- ClassNode lowerBound = value.getLowerBound();
+ // examine non-placeholder type args
+ for (GenericsType gt : typeArguments) {
+ if (gt.isWildcard()) {
+ ClassNode lowerBound = gt.getLowerBound();
if (lowerBound != null) {
extractPlaceholders(lowerBound, placeholders);
- }
- ClassNode[] upperBounds = value.getUpperBounds();
- if (upperBounds != null) {
- for (ClassNode upperBound : upperBounds) {
- extractPlaceholders(upperBound, placeholders);
+ } else {
+ ClassNode[] upperBounds = gt.getUpperBounds();
+ if (upperBounds != null) {
+ for (ClassNode upperBound : upperBounds) {
+ extractPlaceholders(upperBound, placeholders);
+ }
}
}
- } else if (!value.isPlaceholder()) {
- extractPlaceholders(value.getType(), placeholders);
+ } else if (!gt.isPlaceholder()) {
+ extractPlaceholders(gt.getType(), placeholders);
}
}
}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 137b1568ab..c593d72357 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2819,6 +2819,27 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10365
+ void testCorrectlyBoundedTypeParameterTypeArgument() {
+ assertScript '''
+ interface I {
+ }
+ class A<T extends Number, Y> implements I {
+ double m(Integer i) {
+ i.doubleValue()
+ }
+ }
+ class B<T extends I> {
+ public int f
+ double test(A<Float, ? extends T> a) { // "T" is re-purposed
+ a.m(f) // Cannot call A#m(java.lang.Integer) with arguments [int]
+ }
+ }
+ double result = new B<I>(f:2).test(new A<>())
+ assert result == 2.0d
+ '''
+ }
+
void testOutOfBoundsByExtendsGenericParameterType() {
shouldFailWithMessages '''
class Foo {