Skip to content

Commit ea35731

Browse files
committed
Avoid infinite recursion for self-referencing generic type
Closes gh-32282 See gh-30079
1 parent bcf235c commit ea35731

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

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

+24-4
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
import java.lang.reflect.WildcardType;
2929
import java.util.Arrays;
3030
import java.util.Collection;
31+
import java.util.HashSet;
3132
import java.util.IdentityHashMap;
3233
import java.util.Map;
34+
import java.util.Set;
3335
import java.util.StringJoiner;
3436

3537
import org.springframework.core.SerializableTypeWrapper.FieldTypeProvider;
@@ -588,18 +590,28 @@ public boolean hasUnresolvableGenerics() {
588590
if (this == NONE) {
589591
return false;
590592
}
593+
return hasUnresolvableGenerics(null);
594+
}
595+
596+
private boolean hasUnresolvableGenerics(@Nullable Set<Type> alreadySeen) {
591597
Boolean unresolvableGenerics = this.unresolvableGenerics;
592598
if (unresolvableGenerics == null) {
593-
unresolvableGenerics = determineUnresolvableGenerics();
599+
unresolvableGenerics = determineUnresolvableGenerics(alreadySeen);
594600
this.unresolvableGenerics = unresolvableGenerics;
595601
}
596602
return unresolvableGenerics;
597603
}
598604

599-
private boolean determineUnresolvableGenerics() {
605+
private boolean determineUnresolvableGenerics(@Nullable Set<Type> alreadySeen) {
606+
if (alreadySeen != null && alreadySeen.contains(this.type)) {
607+
// Self-referencing generic -> not unresolvable
608+
return false;
609+
}
610+
600611
ResolvableType[] generics = getGenerics();
601612
for (ResolvableType generic : generics) {
602-
if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds() || generic.hasUnresolvableGenerics()) {
613+
if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds() ||
614+
generic.hasUnresolvableGenerics(currentTypeSeen(alreadySeen))) {
603615
return true;
604616
}
605617
}
@@ -619,12 +631,20 @@ private boolean determineUnresolvableGenerics() {
619631
}
620632
Class<?> superclass = resolved.getSuperclass();
621633
if (superclass != null && superclass != Object.class) {
622-
return getSuperType().hasUnresolvableGenerics();
634+
return getSuperType().hasUnresolvableGenerics(currentTypeSeen(alreadySeen));
623635
}
624636
}
625637
return false;
626638
}
627639

640+
private Set<Type> currentTypeSeen(@Nullable Set<Type> alreadySeen) {
641+
if (alreadySeen == null) {
642+
alreadySeen = new HashSet<>(4);
643+
}
644+
alreadySeen.add(this.type);
645+
return alreadySeen;
646+
}
647+
628648
/**
629649
* Determine whether the underlying type is a type variable that
630650
* cannot be resolved through the associated variable resolver.

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

+23-1
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,18 @@ void hasUnresolvableGenericsWhenNested() throws Exception {
13211321
assertThat(type.hasUnresolvableGenerics()).isTrue();
13221322
}
13231323

1324+
@Test
1325+
void hasUnresolvableGenericsWhenSelfReferring() {
1326+
ResolvableType type = ResolvableType.forInstance(new Bar());
1327+
assertThat(type.hasUnresolvableGenerics()).isFalse();
1328+
}
1329+
1330+
@Test
1331+
void hasUnresolvableGenericsWithEnum() {
1332+
ResolvableType type = ResolvableType.forType(SimpleEnum.class.getGenericSuperclass());
1333+
assertThat(type.hasUnresolvableGenerics()).isFalse();
1334+
}
1335+
13241336
@Test
13251337
void spr11219() throws Exception {
13261338
ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class);
@@ -1624,12 +1636,22 @@ interface ListOfGenericArray extends List<List<String>[]> {
16241636
}
16251637

16261638

1627-
public interface ListOfListSupplier<T> {
1639+
interface ListOfListSupplier<T> {
16281640

16291641
List<List<T>> get();
1642+
}
1643+
1644+
1645+
class Foo<T extends Foo<T>> {
1646+
}
16301647

1648+
class Bar extends Foo<Bar> {
16311649
}
16321650

1651+
1652+
enum SimpleEnum { VALUE }
1653+
1654+
16331655
static class EnclosedInParameterizedType<T> {
16341656

16351657
static class InnerRaw {

0 commit comments

Comments
 (0)