Skip to content

Commit

Permalink
Restrict lenient nested matching to immediate type variable
Browse files Browse the repository at this point in the history
Includes fix for matching multiple wildcard bounds properly.

Closes gh-34119
Closes gh-34234
  • Loading branch information
jhoeller committed Jan 15, 2025
1 parent d280358 commit 2273850
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -410,8 +410,9 @@ else if (!exactMatch) {
}
matchedBefore.put(this.type, other.type);
for (int i = 0; i < ourGenerics.length; i++) {
if (!ourGenerics[i].isAssignableFrom(otherGenerics[i],
!other.hasUnresolvableGenerics(), matchedBefore, upUntilUnresolvable)) {
ResolvableType otherGeneric = otherGenerics[i];
if (!ourGenerics[i].isAssignableFrom(otherGeneric,
!otherGeneric.isUnresolvableTypeVariable(), matchedBefore, upUntilUnresolvable)) {
return false;
}
}
Expand Down Expand Up @@ -1729,8 +1730,16 @@ public boolean isSameKind(WildcardBounds bounds) {
* @return {@code true} if these bounds are assignable from all types
*/
public boolean isAssignableFrom(ResolvableType[] types, @Nullable Map<Type, Type> matchedBefore) {
for (ResolvableType type : types) {
if (!isAssignableFrom(type, matchedBefore)) {
for (ResolvableType bound : this.bounds) {
boolean matched = false;
for (ResolvableType type : types) {
if (this.kind == Kind.UPPER ? bound.isAssignableFrom(type, false, matchedBefore, false) :
type.isAssignableFrom(bound, false, matchedBefore, false)) {
matched = true;
break;
}
}
if (!matched) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -1189,10 +1189,11 @@ void isAssignableFromForComplexWildcards() throws Exception {
}

@Test
void isAssignableFromForUnresolvedWildcards() {
void isAssignableFromForUnresolvedWildcard() {
ResolvableType wildcard = ResolvableType.forInstance(new Wildcard<>());
ResolvableType wildcardFixed = ResolvableType.forInstance(new WildcardFixed());
ResolvableType wildcardConcrete = ResolvableType.forClassWithGenerics(Wildcard.class, Number.class);
ResolvableType wildcardConcrete = ResolvableType.forClassWithGenerics(Wildcard.class, CharSequence.class);
ResolvableType wildcardConsumer = ResolvableType.forInstance(new WildcardConsumer<>());

assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
assertThat(wildcard.isAssignableFromResolvedPart(wildcardFixed)).isTrue();
Expand All @@ -1206,6 +1207,38 @@ void isAssignableFromForUnresolvedWildcards() {
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcard)).isTrue();
assertThat(wildcardConcrete.isAssignableFrom(wildcardFixed)).isFalse();
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcardFixed)).isFalse();
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFrom(wildcard)).isFalse();
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFromResolvedPart(wildcard)).isTrue();
}

@Test
void isAssignableFromForUnresolvedDoubleWildcard() {
ResolvableType wildcard = ResolvableType.forInstance(new DoubleWildcard<>());
ResolvableType wildcardFixed = ResolvableType.forInstance(new DoubleWildcardFixed());
ResolvableType wildcardConsumer = ResolvableType.forInstance(new DoubleWildcardConsumer<>());

assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
assertThat(wildcard.isAssignableFromResolvedPart(wildcardFixed)).isTrue();
assertThat(wildcardFixed.isAssignableFrom(wildcard)).isFalse();
assertThat(wildcardFixed.isAssignableFromResolvedPart(wildcard)).isFalse();
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFrom(wildcard)).isTrue();
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFromResolvedPart(wildcard)).isTrue();
}

@Test
void strictGenericsMatching() {
ResolvableType consumerUnresolved = ResolvableType.forClass(Consumer.class);
ResolvableType consumerObject = ResolvableType.forClassWithGenerics(Consumer.class, Object.class);
ResolvableType consumerNestedUnresolved = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClass(Consumer.class));

assertThat(consumerUnresolved.isAssignableFrom(consumerObject)).isTrue();
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerObject)).isTrue();
assertThat(consumerObject.isAssignableFrom(consumerUnresolved)).isTrue();
assertThat(consumerObject.isAssignableFromResolvedPart(consumerUnresolved)).isTrue();
assertThat(consumerUnresolved.isAssignableFrom(consumerNestedUnresolved)).isTrue();
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerNestedUnresolved)).isTrue();
assertThat(consumerObject.isAssignableFrom(consumerNestedUnresolved)).isFalse();
assertThat(consumerObject.isAssignableFromResolvedPart(consumerNestedUnresolved)).isFalse();
}

@Test
Expand Down Expand Up @@ -1752,12 +1785,26 @@ public class MyCollectionSuperclassType extends MySuperclassType<Collection<Stri
}


public class Wildcard<T extends Number> {
public class Wildcard<T extends CharSequence> {
}

public class WildcardFixed extends Wildcard<String> {
}

public class WildcardConsumer<T extends CharSequence & Serializable> implements Consumer<Wildcard<T>> {
}

public class WildcardFixed extends Wildcard<Integer> {

public class DoubleWildcard<T extends CharSequence & Serializable> {
}

public class DoubleWildcardFixed extends DoubleWildcard<String> {
}

public class DoubleWildcardConsumer<T extends CharSequence & Serializable> implements Consumer<DoubleWildcard<T>> {
}



interface VariableNameSwitch<V, K> extends MultiValueMap<K, V> {
}
Expand Down

1 comment on commit 2273850

@bilak
Copy link

@bilak bilak commented on 2273850 Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for commenting directly into closed MR...but will this handle something like this? handle(Wildcard wildcard) when the type is Wildcard<T>? I've seen that issue recently when upgrading spring boot to 3.4.x. My @EventHandlers were not handling this cases.

Please sign in to comment.