Resolve generics for Kotlin Value Boxing inspection.
To introspect value boxing rules, we now resolve Kotlin type parameters. Closes #2986
This commit is contained in:
@@ -19,6 +19,7 @@ import kotlin.jvm.JvmClassMappingKt;
|
||||
import kotlin.jvm.internal.Reflection;
|
||||
import kotlin.reflect.KCallable;
|
||||
import kotlin.reflect.KClass;
|
||||
import kotlin.reflect.KClassifier;
|
||||
import kotlin.reflect.KFunction;
|
||||
import kotlin.reflect.KParameter;
|
||||
import kotlin.reflect.KProperty;
|
||||
@@ -109,7 +110,7 @@ class KotlinValueUtils {
|
||||
|
||||
KType copyType = expandUnderlyingType(type);
|
||||
|
||||
if (copyType.getClassifier()instanceof KClass<?> kc && kc.isValue() || copyType.isMarkedNullable()) {
|
||||
if (copyType.getClassifier() instanceof KClass<?> kc && kc.isValue() || copyType.isMarkedNullable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -118,7 +119,7 @@ class KotlinValueUtils {
|
||||
|
||||
private static KType expandUnderlyingType(KType kotlinType) {
|
||||
|
||||
if (!(kotlinType.getClassifier()instanceof KClass<?> kc) || !kc.isValue()) {
|
||||
if (!(kotlinType.getClassifier() instanceof KClass<?> kc) || !kc.isValue()) {
|
||||
return kotlinType;
|
||||
}
|
||||
|
||||
@@ -196,7 +197,43 @@ class KotlinValueUtils {
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private ValueBoxing(BoxingRules rules, KParameter parameter) {
|
||||
this(rules, parameter.getType(), (KClass<?>) parameter.getType().getClassifier(), parameter.isOptional());
|
||||
this(rules, parameter.getType(), resolveClass(parameter.getType()), parameter.isOptional());
|
||||
}
|
||||
|
||||
private static KClass<?> resolveClass(KType type) {
|
||||
|
||||
if (type instanceof KClass<?> kc) {
|
||||
return kc;
|
||||
}
|
||||
|
||||
if (type instanceof KTypeParameter ktp) {
|
||||
return resolveClass(ktp.getUpperBounds().get(0));
|
||||
}
|
||||
|
||||
KClassifier classifier = type.getClassifier();
|
||||
|
||||
if (classifier != null) {
|
||||
return resolveClass(classifier);
|
||||
}
|
||||
|
||||
return JvmClassMappingKt.getKotlinClass(Object.class);
|
||||
}
|
||||
|
||||
private static KClass<?> resolveClass(KClassifier classifier) {
|
||||
|
||||
if (classifier instanceof KClass<?> kc) {
|
||||
return kc;
|
||||
}
|
||||
|
||||
if (classifier instanceof KTypeParameter ktp) {
|
||||
return resolveClass(ktp.getUpperBounds().get(0));
|
||||
}
|
||||
|
||||
if (classifier instanceof KType ktp) {
|
||||
return resolveClass(ktp);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(String.format("Unsupported KClassifier: %s", classifier));
|
||||
}
|
||||
|
||||
private ValueBoxing(BoxingRules rules, KType type, KClass<?> kClass, boolean optional) {
|
||||
@@ -216,7 +253,7 @@ class KotlinValueUtils {
|
||||
KClass<?> nestedClass;
|
||||
|
||||
// bound flattening
|
||||
if (nestedType.getClassifier()instanceof KTypeParameter ktp) {
|
||||
if (nestedType.getClassifier() instanceof KTypeParameter ktp) {
|
||||
nestedClass = getUpperBound(ktp);
|
||||
} else {
|
||||
nestedClass = (KClass<?>) nestedType.getClassifier();
|
||||
@@ -239,7 +276,7 @@ class KotlinValueUtils {
|
||||
|
||||
for (KType upperBound : typeParameter.getUpperBounds()) {
|
||||
|
||||
if (upperBound.getClassifier()instanceof KClass<?> kc) {
|
||||
if (upperBound.getClassifier() instanceof KClass<?> kc) {
|
||||
return kc;
|
||||
}
|
||||
}
|
||||
@@ -249,11 +286,11 @@ class KotlinValueUtils {
|
||||
|
||||
static KType resolveType(KType type) {
|
||||
|
||||
if (type.getClassifier()instanceof KTypeParameter ktp) {
|
||||
if (type.getClassifier() instanceof KTypeParameter ktp) {
|
||||
|
||||
for (KType upperBound : ktp.getUpperBounds()) {
|
||||
|
||||
if (upperBound.getClassifier()instanceof KClass<?> kc) {
|
||||
if (upperBound.getClassifier() instanceof KClass<?> kc) {
|
||||
return upperBound;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.mapping.model;
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.full.memberFunctions
|
||||
import kotlin.reflect.jvm.javaConstructor
|
||||
|
||||
/**
|
||||
@@ -204,6 +205,30 @@ class KotlinValueUtilsUnitTests {
|
||||
assertThat(pand.appliesBoxing()).isFalse
|
||||
}
|
||||
|
||||
@Test // GH-2986
|
||||
internal fun considersGenerics() {
|
||||
|
||||
val copyFunction =
|
||||
WithGenericsInConstructor::class.memberFunctions.first { it.name == "copy" }
|
||||
|
||||
val vh = KotlinValueUtils.getCopyValueHierarchy(
|
||||
copyFunction.parameters.get(1)
|
||||
)
|
||||
assertThat(vh.actualType).isEqualTo(Object::class.java)
|
||||
}
|
||||
|
||||
@Test // GH-2986
|
||||
internal fun considersGenericsWithBounds() {
|
||||
|
||||
val copyFunction =
|
||||
WithGenericsInConstructor::class.memberFunctions.first { it.name == "copy" }
|
||||
|
||||
val vh = KotlinValueUtils.getCopyValueHierarchy(
|
||||
copyFunction.parameters.get(1)
|
||||
)
|
||||
assertThat(vh.actualType).isEqualTo(Object::class.java)
|
||||
}
|
||||
|
||||
@Test // GH-1947
|
||||
internal fun inlinesGenericTypesConstructorRules() {
|
||||
|
||||
@@ -247,5 +272,9 @@ class KotlinValueUtilsUnitTests {
|
||||
assertThat(recursive.appliesBoxing()).isFalse
|
||||
}
|
||||
|
||||
data class WithGenericsInConstructor<T>(val bar: T? = null)
|
||||
|
||||
data class WithGenericBoundInConstructor<T : CharSequence>(val bar: T? = null)
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user