Skip to content

Commit

Permalink
Fix type parameter annotation regression (#2388)
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnatBeresnev committed Mar 29, 2022
1 parent 42aeb06 commit 7ecb369
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 14 deletions.
Expand Up @@ -209,7 +209,13 @@ class DefaultPsiToDocumentableTranslator(
return AncestryNode(
typeConstructor = GenericTypeConstructor(
DRI.from(psiClass),
psiClass.typeParameters.map(::getProjection)
psiClass.typeParameters.map { typeParameter ->
TypeParameter(
dri = DRI.from(typeParameter),
name = typeParameter.name.orEmpty(),
extra = typeParameter.annotations()
)
}
),
superclass = classes.singleOrNull()?.first?.let(::traversePsiClassForAncestorsAndInheritedMembers),
interfaces = interfaces.map { traversePsiClassForAncestorsAndInheritedMembers(it.first) }
Expand Down Expand Up @@ -439,20 +445,19 @@ class DefaultPsiToDocumentableTranslator(
PropertyContainer.withAll(annotations.toSourceSetDependent().toAnnotations())
} ?: PropertyContainer.empty()

private fun getProjection(type: PsiTypeParameter) =
TypeParameter(
dri = DRI.from(type),
name = type.name.orEmpty(),
extra = type.annotations()
)

private fun getBound(type: PsiType): Bound {
fun bound() = when (type) {
is PsiClassReferenceType ->
type.resolve()?.let { resolved ->
when {
resolved.qualifiedName == "java.lang.Object" -> JavaObject(type.annotations())
resolved is PsiTypeParameter -> getProjection(resolved)
resolved is PsiTypeParameter -> {
TypeParameter(
dri = DRI.from(resolved),
name = resolved.name.orEmpty(),
extra = type.annotations()
)
}
Regex("kotlin\\.jvm\\.functions\\.Function.*").matches(resolved.qualifiedName ?: "") ||
Regex("java\\.util\\.function\\.Function.*").matches(
resolved.qualifiedName ?: ""
Expand Down
@@ -1,11 +1,13 @@
package model.annotations

import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.utilities.cast
import org.junit.jupiter.api.Test
import utils.AbstractModelTest
import kotlin.test.Ignore
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class JavaAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {

Expand Down Expand Up @@ -114,4 +116,61 @@ class JavaAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/jav
}
}
}

@Test
fun `type parameter annotations should be visible even if type declaration has none`() {
inlineModelTest(
"""
|@Retention(RetentionPolicy.RUNTIME)
|@Target(ElementType.PARAMETER)
|public @interface Hello {
| public String bar() default "";
|}
|public class Test {
| public <T> void foo(java.util.List<@Hello T> param) {}
|}
""".trimIndent()
) {
with((this / "java" / "Test").cast<DClass>()) {
with((this / "foo").cast<DFunction>()) {
val paramAnnotations = parameters.first()
.type.cast<GenericTypeConstructor>()
.projections.first().cast<TypeParameter>()
.annotations()
.values
.flatten()

assertEquals(1, paramAnnotations.size)
assertEquals(DRI("java", "Hello"), paramAnnotations[0].dri)
}
}
}
}

@Test
fun `type parameter annotations should not be propagated from resolved type`() {
inlineModelTest(
"""
|@Retention(RetentionPolicy.RUNTIME)
|@Target(ElementType.PARAMETER)
|public @interface Hello {
| public String bar() default "";
|}
|public class Test {
| public <@Hello T> void foo(java.util.List<T> param) {}
|}
""".trimIndent()
) {
with((this / "java" / "Test").cast<DClass>()) {
with((this / "foo").cast<DFunction>()) {
val paramAnnotations = parameters.first()
.type.cast<GenericTypeConstructor>()
.projections.first().cast<TypeParameter>()
.annotations()

assertTrue(paramAnnotations.isEmpty())
}
}
}
}
}
@@ -1,13 +1,13 @@
package model.annotations

import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.Annotations
import org.jetbrains.dokka.model.DFunction
import org.jetbrains.dokka.model.DProperty
import org.jetbrains.dokka.model.GenericTypeConstructor
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.utilities.cast
import org.junit.jupiter.api.Test
import utils.AbstractModelTest
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class KotlinAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/annotations/Test.kt", "annotations") {
@Test
Expand Down Expand Up @@ -50,4 +50,52 @@ class KotlinAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/a
}
}
}

@Test
fun `type parameter annotations should be visible even if type declaration has none`() {
inlineModelTest(
"""
|@Target(AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.TYPE)
|annotation class Hello
|
|fun <T> foo(param: List<@Hello T>) {}
""".trimIndent()
) {
with((this / "annotations" / "foo").cast<DFunction>()) {
val paramAnnotations = parameters.first()
.type.cast<GenericTypeConstructor>()
.projections
.first().cast<Invariance<TypeParameter>>()
.inner.cast<TypeParameter>()
.annotations()
.values
.flatten()

assertEquals(1, paramAnnotations.size)
assertEquals(DRI("annotations", "Hello"), paramAnnotations[0].dri)
}
}
}

@Test
fun `type parameter annotations should not be propagated from resolved type`() {
inlineModelTest(
"""
|@Target(AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.TYPE)
|annotation class Hello
|
|fun <@Hello T> foo(param: List<T>) {}
""".trimIndent()
) {
with((this / "annotations" / "foo").cast<DFunction>()) {
val paramAnnotations = parameters.first()
.type.cast<GenericTypeConstructor>()
.projections.first().cast<Invariance<TypeParameter>>()
.inner.cast<TypeParameter>()
.annotations()

assertTrue(paramAnnotations.isEmpty())
}
}
}
}

0 comments on commit 7ecb369

Please sign in to comment.