diff options
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 7acf735a..4b2d7720 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 3e5c8176..d6564343 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 <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()) + } + } + } + } }
\ 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 e88d7353..c459cc34 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 <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()) + } + } + } }
\ No newline at end of file |