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 2023/10/24 15:29:52 UTC
[groovy] 01/03: GROOVY-10055, GROOVY-10619: STC: wildcard for self-referential type vars
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
commit 02f50576d3a69690bdff524cb87b411cf978cfc9
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Oct 24 09:37:05 2023 -0500
GROOVY-10055, GROOVY-10619: STC: wildcard for self-referential type vars
3_0_X backport
---
.../transform/stc/StaticTypeCheckingSupport.java | 45 ++++++++++++++--------
.../groovy/transform/stc/GenericsSTCTest.groovy | 17 ++++++++
2 files changed, 46 insertions(+), 16 deletions(-)
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 059a1bf9a6..c5d5e0755b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1957,31 +1957,44 @@ public abstract class StaticTypeCheckingSupport {
if (type.isArray()) {
return boundUnboundedWildcards(type.getComponentType()).makeArray();
}
- ClassNode target = type.redirect();
- if (target == null || type == target || !isUsingGenericsOrIsArrayUsingGenerics(target)) return type;
+ ClassNode redirect = type.redirect();
+ if (redirect == null || redirect == type || !isUsingGenericsOrIsArrayUsingGenerics(redirect)) {
+ return type;
+ }
ClassNode newType = type.getPlainNodeReference();
newType.setGenericsPlaceHolder(type.isGenericsPlaceHolder());
- newType.setGenericsTypes(boundUnboundedWildcards(type.getGenericsTypes(), target.getGenericsTypes()));
+ newType.setGenericsTypes(boundUnboundedWildcards(type.getGenericsTypes(), redirect.getGenericsTypes()));
return newType;
}
- private static GenericsType[] boundUnboundedWildcards(final GenericsType[] usage, final GenericsType[] declaration) {
- GenericsType[] newGts = new GenericsType[usage.length];
- for (int i = 0, n = usage.length; i < n; i += 1) {
- newGts[i] = boundUnboundedWildcard(usage[i], declaration[i]);
+ private static GenericsType[] boundUnboundedWildcards(final GenericsType[] actual, final GenericsType[] declared) {
+ int n = actual.length; GenericsType[] newTypes = new GenericsType[n];
+ for (int i = 0; i < n; i += 1) {
+ newTypes[i] = boundUnboundedWildcard(actual[i], declared[i]);
}
- return newGts;
+ return newTypes;
}
- private static GenericsType boundUnboundedWildcard(final GenericsType gt, final GenericsType spec) {
- if (isUnboundedWildcard(gt)) {
- ClassNode base = makeWithoutCaching("?");
- // The bounds on the declared type are at least as good as the ones on an unbounded wildcard, since it has none!
- GenericsType newGt = new GenericsType(base, spec.getUpperBounds(), spec.getLowerBound());
- newGt.setWildcard(true);
- return newGt;
+ private static GenericsType boundUnboundedWildcard(final GenericsType actual, final GenericsType declared) {
+ if (!isUnboundedWildcard(actual)) return actual;
+ ClassNode lowerBound = declared.getLowerBound();
+ ClassNode[] upperBounds = declared.getUpperBounds();
+ if (lowerBound != null) {
+ assert upperBounds == null;
+ } else if (upperBounds == null) {
+ upperBounds = new ClassNode[]{OBJECT_TYPE};
+ } else if (declared.isPlaceholder()) {
+ upperBounds = upperBounds.clone();
+ for (int i = 0, n = upperBounds.length; i < n; i += 1) {
+ // GROOVY-10055, GROOVY-10619: purge self references
+ if (GenericsUtils.extractPlaceholders(upperBounds[i])
+ .containsKey(new GenericsTypeName(declared.getName())))
+ upperBounds[i] = upperBounds[i].getPlainNodeReference();
+ }
}
- return gt;
+ GenericsType newType = new GenericsType(makeWithoutCaching("?"), upperBounds, lowerBound);
+ newType.setWildcard(true);
+ return newType;
}
public static boolean isUnboundedWildcard(final GenericsType gt) {
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 5e6a8cfb4f..7cd9ae7492 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -5362,6 +5362,23 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10619
+ void testSelfReferentialTypeParameter5() {
+ assertScript '''
+ @Grab('org.springframework.boot:spring-boot-starter-webflux:2.6.7')
+ import static org.springframework.http.MediaType.APPLICATION_JSON
+ import org.springframework.core.ParameterizedTypeReference
+
+ List<BigDecimal> test(org.springframework.web.reactive.function.client.WebClient web) {
+ def response = web.get()
+ .uri('/something?id={id}', 'id').accept(APPLICATION_JSON).retrieve()
+ .toEntity(new ParameterizedTypeReference<List<BigDecimal>>() {})
+ .block(java.time.Duration.ofSeconds(2))
+ return response.getBody() ?: []
+ }
+ '''
+ }
+
// GROOVY-7804
void testParameterlessClosureToGenericSAMTypeArgumentCoercion() {
assertScript '''