Skip to content

Commit

Permalink
Resolve generics for Kotlin Value Boxing inspection.
Browse files Browse the repository at this point in the history
To introspect value boxing rules, we now resolve Kotlin type parameters.

Closes #2986
  • Loading branch information
mp911de committed Dec 14, 2023
1 parent ff1abf0 commit 2415156
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand Down Expand Up @@ -109,7 +110,7 @@ public boolean shouldApplyBoxing(KType type, boolean optional, KParameter compon

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;
}

Expand All @@ -118,7 +119,7 @@ public boolean shouldApplyBoxing(KType type, boolean optional, KParameter compon

private static KType expandUnderlyingType(KType kotlinType) {

if (!(kotlinType.getClassifier()instanceof KClass<?> kc) || !kc.isValue()) {
if (!(kotlinType.getClassifier() instanceof KClass<?> kc) || !kc.isValue()) {
return kotlinType;
}

Expand Down Expand Up @@ -196,7 +197,43 @@ static class ValueBoxing {
*/
@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) {
Expand All @@ -216,7 +253,7 @@ private ValueBoxing(BoxingRules rules, KType type, KClass<?> kClass, boolean opt
KClass<?> nestedClass;

// bound flattening
if (nestedType.getClassifier()instanceof KTypeParameter ktp) {
if (nestedType.getClassifier() instanceof KTypeParameter ktp) {
nestedClass = getUpperBound(ktp);
} else {
nestedClass = (KClass<?>) nestedType.getClassifier();
Expand All @@ -239,7 +276,7 @@ private static KClass<?> getUpperBound(KTypeParameter typeParameter) {

for (KType upperBound : typeParameter.getUpperBounds()) {

if (upperBound.getClassifier()instanceof KClass<?> kc) {
if (upperBound.getClassifier() instanceof KClass<?> kc) {
return kc;
}
}
Expand All @@ -249,11 +286,11 @@ private static KClass<?> getUpperBound(KTypeParameter typeParameter) {

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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down Expand Up @@ -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() {

Expand Down Expand Up @@ -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)

}

0 comments on commit 2415156

Please sign in to comment.