Skip to content

Commit 2273850

Browse files
committed
Restrict lenient nested matching to immediate type variable
Includes fix for matching multiple wildcard bounds properly. Closes gh-34119 Closes gh-34234
1 parent d280358 commit 2273850

File tree

2 files changed

+66
-10
lines changed

2 files changed

+66
-10
lines changed

spring-core/src/main/java/org/springframework/core/ResolvableType.java

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -410,8 +410,9 @@ else if (!exactMatch) {
410410
}
411411
matchedBefore.put(this.type, other.type);
412412
for (int i = 0; i < ourGenerics.length; i++) {
413-
if (!ourGenerics[i].isAssignableFrom(otherGenerics[i],
414-
!other.hasUnresolvableGenerics(), matchedBefore, upUntilUnresolvable)) {
413+
ResolvableType otherGeneric = otherGenerics[i];
414+
if (!ourGenerics[i].isAssignableFrom(otherGeneric,
415+
!otherGeneric.isUnresolvableTypeVariable(), matchedBefore, upUntilUnresolvable)) {
415416
return false;
416417
}
417418
}
@@ -1729,8 +1730,16 @@ public boolean isSameKind(WildcardBounds bounds) {
17291730
* @return {@code true} if these bounds are assignable from all types
17301731
*/
17311732
public boolean isAssignableFrom(ResolvableType[] types, @Nullable Map<Type, Type> matchedBefore) {
1732-
for (ResolvableType type : types) {
1733-
if (!isAssignableFrom(type, matchedBefore)) {
1733+
for (ResolvableType bound : this.bounds) {
1734+
boolean matched = false;
1735+
for (ResolvableType type : types) {
1736+
if (this.kind == Kind.UPPER ? bound.isAssignableFrom(type, false, matchedBefore, false) :
1737+
type.isAssignableFrom(bound, false, matchedBefore, false)) {
1738+
matched = true;
1739+
break;
1740+
}
1741+
}
1742+
if (!matched) {
17341743
return false;
17351744
}
17361745
}

spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java

+52-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1189,10 +1189,11 @@ void isAssignableFromForComplexWildcards() throws Exception {
11891189
}
11901190

11911191
@Test
1192-
void isAssignableFromForUnresolvedWildcards() {
1192+
void isAssignableFromForUnresolvedWildcard() {
11931193
ResolvableType wildcard = ResolvableType.forInstance(new Wildcard<>());
11941194
ResolvableType wildcardFixed = ResolvableType.forInstance(new WildcardFixed());
1195-
ResolvableType wildcardConcrete = ResolvableType.forClassWithGenerics(Wildcard.class, Number.class);
1195+
ResolvableType wildcardConcrete = ResolvableType.forClassWithGenerics(Wildcard.class, CharSequence.class);
1196+
ResolvableType wildcardConsumer = ResolvableType.forInstance(new WildcardConsumer<>());
11961197

11971198
assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
11981199
assertThat(wildcard.isAssignableFromResolvedPart(wildcardFixed)).isTrue();
@@ -1206,6 +1207,38 @@ void isAssignableFromForUnresolvedWildcards() {
12061207
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcard)).isTrue();
12071208
assertThat(wildcardConcrete.isAssignableFrom(wildcardFixed)).isFalse();
12081209
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcardFixed)).isFalse();
1210+
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFrom(wildcard)).isFalse();
1211+
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFromResolvedPart(wildcard)).isTrue();
1212+
}
1213+
1214+
@Test
1215+
void isAssignableFromForUnresolvedDoubleWildcard() {
1216+
ResolvableType wildcard = ResolvableType.forInstance(new DoubleWildcard<>());
1217+
ResolvableType wildcardFixed = ResolvableType.forInstance(new DoubleWildcardFixed());
1218+
ResolvableType wildcardConsumer = ResolvableType.forInstance(new DoubleWildcardConsumer<>());
1219+
1220+
assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
1221+
assertThat(wildcard.isAssignableFromResolvedPart(wildcardFixed)).isTrue();
1222+
assertThat(wildcardFixed.isAssignableFrom(wildcard)).isFalse();
1223+
assertThat(wildcardFixed.isAssignableFromResolvedPart(wildcard)).isFalse();
1224+
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFrom(wildcard)).isTrue();
1225+
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFromResolvedPart(wildcard)).isTrue();
1226+
}
1227+
1228+
@Test
1229+
void strictGenericsMatching() {
1230+
ResolvableType consumerUnresolved = ResolvableType.forClass(Consumer.class);
1231+
ResolvableType consumerObject = ResolvableType.forClassWithGenerics(Consumer.class, Object.class);
1232+
ResolvableType consumerNestedUnresolved = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClass(Consumer.class));
1233+
1234+
assertThat(consumerUnresolved.isAssignableFrom(consumerObject)).isTrue();
1235+
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerObject)).isTrue();
1236+
assertThat(consumerObject.isAssignableFrom(consumerUnresolved)).isTrue();
1237+
assertThat(consumerObject.isAssignableFromResolvedPart(consumerUnresolved)).isTrue();
1238+
assertThat(consumerUnresolved.isAssignableFrom(consumerNestedUnresolved)).isTrue();
1239+
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerNestedUnresolved)).isTrue();
1240+
assertThat(consumerObject.isAssignableFrom(consumerNestedUnresolved)).isFalse();
1241+
assertThat(consumerObject.isAssignableFromResolvedPart(consumerNestedUnresolved)).isFalse();
12091242
}
12101243

12111244
@Test
@@ -1752,12 +1785,26 @@ public class MyCollectionSuperclassType extends MySuperclassType<Collection<Stri
17521785
}
17531786

17541787

1755-
public class Wildcard<T extends Number> {
1788+
public class Wildcard<T extends CharSequence> {
1789+
}
1790+
1791+
public class WildcardFixed extends Wildcard<String> {
1792+
}
1793+
1794+
public class WildcardConsumer<T extends CharSequence & Serializable> implements Consumer<Wildcard<T>> {
17561795
}
17571796

1758-
public class WildcardFixed extends Wildcard<Integer> {
1797+
1798+
public class DoubleWildcard<T extends CharSequence & Serializable> {
17591799
}
17601800

1801+
public class DoubleWildcardFixed extends DoubleWildcard<String> {
1802+
}
1803+
1804+
public class DoubleWildcardConsumer<T extends CharSequence & Serializable> implements Consumer<DoubleWildcard<T>> {
1805+
}
1806+
1807+
17611808

17621809
interface VariableNameSwitch<V, K> extends MultiValueMap<K, V> {
17631810
}

0 commit comments

Comments
 (0)