From 7ecb369daa72b9ccef50e10ee71a607f76f8e8f8 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Tue, 29 Mar 2022 20:22:18 +0300 Subject: [PATCH] Fix type parameter annotation regression (#2388) --- .../psi/DefaultPsiToDocumentableTranslator.kt | 23 ++++--- .../JavaAnnotationsForParametersTest.kt | 61 ++++++++++++++++++- .../KotlinAnnotationsForParametersTest.kt | 56 +++++++++++++++-- 3 files changed, 126 insertions(+), 14 deletions(-) diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 7acf735aee..4b2d7720f6 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -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) } @@ -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 ?: "" diff --git a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt index 3e5c817688..d6564343de 100644 --- a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt +++ b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt @@ -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") { @@ -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 void foo(java.util.List<@Hello T> param) {} + |} + """.trimIndent() + ) { + with((this / "java" / "Test").cast()) { + with((this / "foo").cast()) { + val paramAnnotations = parameters.first() + .type.cast() + .projections.first().cast() + .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 param) {} + |} + """.trimIndent() + ) { + with((this / "java" / "Test").cast()) { + with((this / "foo").cast()) { + val paramAnnotations = parameters.first() + .type.cast() + .projections.first().cast() + .annotations() + + assertTrue(paramAnnotations.isEmpty()) + } + } + } + } } \ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/annotations/KotlinAnnotationsForParametersTest.kt b/plugins/base/src/test/kotlin/model/annotations/KotlinAnnotationsForParametersTest.kt index e88d7353ac..c459cc3490 100644 --- a/plugins/base/src/test/kotlin/model/annotations/KotlinAnnotationsForParametersTest.kt +++ b/plugins/base/src/test/kotlin/model/annotations/KotlinAnnotationsForParametersTest.kt @@ -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 @@ -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 foo(param: List<@Hello T>) {} + """.trimIndent() + ) { + with((this / "annotations" / "foo").cast()) { + val paramAnnotations = parameters.first() + .type.cast() + .projections + .first().cast>() + .inner.cast() + .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) {} + """.trimIndent() + ) { + with((this / "annotations" / "foo").cast()) { + val paramAnnotations = parameters.first() + .type.cast() + .projections.first().cast>() + .inner.cast() + .annotations() + + assertTrue(paramAnnotations.isEmpty()) + } + } + } } \ No newline at end of file