Browse Source

Resolve generics for Kotlin Value Boxing inspection.

To introspect value boxing rules, we now resolve Kotlin type parameters.

Closes #2986
pull/3007/head
Mark Paluch 2 years ago
parent
commit
24151566d4
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 51
      src/main/java/org/springframework/data/mapping/model/KotlinValueUtils.java
  2. 29
      src/test/kotlin/org/springframework/data/mapping/model/KotlinValueUtilsUnitTests.kt

51
src/main/java/org/springframework/data/mapping/model/KotlinValueUtils.java

@ -19,6 +19,7 @@ import kotlin.jvm.JvmClassMappingKt; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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;
}
}

29
src/test/kotlin/org/springframework/data/mapping/model/KotlinValueUtilsUnitTests.kt

@ -18,6 +18,7 @@ package org.springframework.data.mapping.model; @@ -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 { @@ -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 { @@ -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)
}

Loading…
Cancel
Save