diff options
-rw-r--r-- | core/src/main/kotlin/links/DRI.kt | 4 | ||||
-rw-r--r-- | kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt | 28 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/basic/DRITest.kt | 35 |
3 files changed, 50 insertions, 17 deletions
diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt index e73c2faa..8dfac80c 100644 --- a/core/src/main/kotlin/links/DRI.kt +++ b/core/src/main/kotlin/links/DRI.kt @@ -68,8 +68,8 @@ data class TypeConstructor( (if (params.isNotEmpty()) "[${params.joinToString(",")}]" else "") } -object SelfType : TypeReference() { - override fun toString() = "^" +data class RecursiveType(val rank: Int): TypeReference() { + override fun toString() = "^".repeat(rank + 1) } data class Nullable(val wrapped: TypeReference) : TypeReference() { diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt index e07672d4..aae1f189 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.types.TypeProjection fun TypeReference.Companion.from(d: ReceiverParameterDescriptor): TypeReference? = when (d.value) { - is ExtensionReceiver -> fromPossiblyNullable(d.type) + is ExtensionReceiver -> fromPossiblyNullable(d.type, emptyList()) else -> run { println("Unknown value type for $d") null @@ -20,31 +20,35 @@ fun TypeReference.Companion.from(d: ReceiverParameterDescriptor): TypeReference? } fun TypeReference.Companion.from(d: ValueParameterDescriptor): TypeReference? = - fromPossiblyNullable(d.type) + fromPossiblyNullable(d.type, emptyList()) fun TypeReference.Companion.from(p: PsiClass) = TypeReference -private fun TypeReference.Companion.fromPossiblyNullable(t: KotlinType, self: KotlinType? = null): TypeReference = - from(t, self).let { if (t.isMarkedNullable) Nullable(it) else it } +private fun TypeReference.Companion.fromPossiblyNullable(t: KotlinType, paramTrace: List<KotlinType>): TypeReference = + fromPossiblyRecursive(t, paramTrace).let { if (t.isMarkedNullable) Nullable(it) else it } -private fun TypeReference.Companion.from(t: KotlinType, self: KotlinType? = null): TypeReference = - if (self is KotlinType && self.constructor == t.constructor && self.arguments == t.arguments) - SelfType - else when (val d = t.constructor.declarationDescriptor) { +private fun TypeReference.Companion.fromPossiblyRecursive(t: KotlinType, paramTrace: List<KotlinType>): TypeReference = + paramTrace.indexOfFirst { it.constructor == t.constructor && it.arguments == t.arguments } + .takeIf { it >= 0 } + ?.let(::RecursiveType) + ?: from(t, paramTrace) + +private fun TypeReference.Companion.from(t: KotlinType, paramTrace: List<KotlinType>): TypeReference = + when (val d = t.constructor.declarationDescriptor) { is TypeParameterDescriptor -> TypeParam( - d.upperBounds.map { fromPossiblyNullable(it, self ?: t) } + d.upperBounds.map { fromPossiblyNullable(it, paramTrace + t) } ) else -> TypeConstructor( t.constructorName.orEmpty(), - t.arguments.map { fromProjection(it, self) } + t.arguments.map { fromProjection(it, paramTrace) } ) } -private fun TypeReference.Companion.fromProjection(t: TypeProjection, r: KotlinType? = null): TypeReference = +private fun TypeReference.Companion.fromProjection(t: TypeProjection, paramTrace: List<KotlinType>): TypeReference = if (t.isStarProjection) { StarProjection } else { - fromPossiblyNullable(t.type, r) + fromPossiblyNullable(t.type, paramTrace) } private val KotlinType.constructorName diff --git a/plugins/base/src/test/kotlin/basic/DRITest.kt b/plugins/base/src/test/kotlin/basic/DRITest.kt index 7083609a..4e805f48 100644 --- a/plugins/base/src/test/kotlin/basic/DRITest.kt +++ b/plugins/base/src/test/kotlin/basic/DRITest.kt @@ -38,7 +38,7 @@ class DRITest : AbstractCoreTest() { val expected = TypeConstructor( "kotlin.Function1", listOf( TypeParam(listOf(Nullable(TypeConstructor("kotlin.Any", emptyList())))), - Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType))))) + Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(RecursiveType(0)))))) ) ) val actual = module.packages.single() @@ -70,7 +70,7 @@ class DRITest : AbstractCoreTest() { configuration ) { documentablesMergingStage = { module -> - val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType))))) + val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(RecursiveType(0)))))) val actual = module.packages.single() .functions.single() .dri.callable?.params?.single() @@ -100,7 +100,7 @@ class DRITest : AbstractCoreTest() { configuration ) { documentablesMergingStage = { module -> - val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType))))) + val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(RecursiveType(0)))))) val actual = module.packages.single() .functions.single() .dri.callable?.receiver @@ -314,4 +314,33 @@ class DRITest : AbstractCoreTest() { } } } + + @Test + fun `deep recursive typebound #1342`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/Test.kt + |package example + | + | fun <T, S, R> recursiveBound(t: T, s: S, r: R) where T: List<S>, S: List<R>, R: List<S> = Unit + | + """.trimMargin(), + configuration + ) { + documentablesMergingStage = { module -> + val function = module.dfs { it.name == "recursiveBound" } + assertEquals( + "example//recursiveBound/#TypeParam(bounds=[kotlin.collections.List[TypeParam(bounds=[kotlin.collections.List[TypeParam(bounds=[kotlin.collections.List[^^]])]])]])#TypeParam(bounds=[kotlin.collections.List[TypeParam(bounds=[kotlin.collections.List[^]])]])#TypeParam(bounds=[kotlin.collections.List[TypeParam(bounds=[kotlin.collections.List[^]])]])/PointingToDeclaration/", + function?.dri?.toString(), + ) + } + } + } } |