aboutsummaryrefslogtreecommitdiff
path: root/plugins/base
diff options
context:
space:
mode:
authorMarcin Aman <marcin.aman@gmail.com>2021-02-05 14:55:45 +0100
committerGitHub <noreply@github.com>2021-02-05 14:55:45 +0100
commit70000c87a37caa2a6b518a555f53c98514434403 (patch)
tree3a0e7a2b797b6edfffc0396bb5f9d35708120e9d /plugins/base
parentb44e41ec8e3e26c73affaaa98bbd170fde352d96 (diff)
downloaddokka-70000c87a37caa2a6b518a555f53c98514434403.tar.gz
dokka-70000c87a37caa2a6b518a555f53c98514434403.tar.bz2
dokka-70000c87a37caa2a6b518a555f53c98514434403.zip
Annotations for parameters (#1710)
* Annotations for parameters * Annotations for parameters
Diffstat (limited to 'plugins/base')
-rw-r--r--plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt18
-rw-r--r--plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt25
-rw-r--r--plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt5
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt83
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt70
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt54
-rw-r--r--plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt35
-rw-r--r--plugins/base/src/test/kotlin/model/annotations/KotlinAnnotationsForParametersTest.kt23
8 files changed, 237 insertions, 76 deletions
diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
index 3851b39c..a5aa6879 100644
--- a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
+++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
@@ -6,25 +6,25 @@ import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
-import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.driOrNull
import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.drisOfAllNestedBounds
+import org.jetbrains.dokka.model.AnnotationTarget
interface JvmSignatureUtils {
- fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: Documentable)
+ fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: AnnotationTarget)
- fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: Documentable)
+ fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: AnnotationTarget)
fun <T : Documentable> WithExtraProperties<T>.modifiers(): SourceSetDependent<Set<ExtraModifiers>>
fun Collection<ExtraModifiers>.toSignatureString(): String =
joinToString("") { it.name.toLowerCase() + " " }
- fun <T : Documentable> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> =
+ fun <T : AnnotationTarget> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> =
extra[Annotations]?.directAnnotations ?: emptyMap()
private fun PageContentBuilder.DocumentableContentBuilder.annotations(
- d: Documentable,
+ d: AnnotationTarget,
ignored: Set<Annotations.Annotation>,
styles: Set<Style>,
operation: PageContentBuilder.DocumentableContentBuilder.(Annotations.Annotation) -> Unit
@@ -40,6 +40,10 @@ interface JvmSignatureUtils {
is DEnumEntry -> d.annotations()
is DTypeAlias -> d.annotations()
is DParameter -> d.annotations()
+ is TypeParameter -> d.annotations()
+ is GenericTypeConstructor -> d.annotations()
+ is FunctionalTypeConstructor -> d.annotations()
+ is JavaObject -> d.annotations()
else -> null
}?.let {
it.entries.forEach {
@@ -104,7 +108,7 @@ interface JvmSignatureUtils {
}
fun PageContentBuilder.DocumentableContentBuilder.annotationsBlockWithIgnored(
- d: Documentable,
+ d: AnnotationTarget,
ignored: Set<Annotations.Annotation>,
renderAtStrategy: AtStrategy,
listBrackets: Pair<Char, Char>,
@@ -118,7 +122,7 @@ interface JvmSignatureUtils {
}
fun PageContentBuilder.DocumentableContentBuilder.annotationsInlineWithIgnored(
- d: Documentable,
+ d: AnnotationTarget,
ignored: Set<Annotations.Annotation>,
renderAtStrategy: AtStrategy,
listBrackets: Pair<Char, Char>,
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
index 7f29e032..4eac9d3f 100644
--- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
+++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
@@ -164,6 +164,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
link(c.name!!, c.dri)
if (c is WithGenerics) {
list(c.generics, prefix = "<", suffix = ">") {
+ annotationsInline(it)
+buildSignature(it)
}
}
@@ -219,6 +220,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
text(p.modifiers()[it]?.toSignatureString() ?: "")
p.setter?.let { text("var ") } ?: text("val ")
list(p.generics, prefix = "<", suffix = "> ") {
+ annotationsInline(it)
+buildSignature(it)
}
p.receiver?.also {
@@ -252,6 +254,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
text("fun ")
val usedGenerics = if (f.isConstructor) f.generics.filter { f uses it } else f.generics
list(usedGenerics, prefix = "<", suffix = "> ") {
+ annotationsInline(it)
+buildSignature(it)
}
f.receiver?.also {
@@ -328,19 +331,23 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection(
p: Projection, showFullyQualifiedName: Boolean = false
- ): Unit =
- when (p) {
- is TypeParameter -> link(p.name, p.dri)
-
- is FunctionalTypeConstructor ->
+ ): Unit {
+ return when (p) {
+ is TypeParameter -> {
+ annotationsInline(p)
+ link(p.name, p.dri)
+ }
+ is FunctionalTypeConstructor -> {
+ annotationsInline(p)
+funType(mainDRI.single(), mainSourcesetData, p)
-
+ }
is GenericTypeConstructor ->
group(styles = emptySet()) {
val linkText = if (showFullyQualifiedName && p.dri.packageName != null) {
"${p.dri.packageName}.${p.dri.classNames.orEmpty()}"
} else p.dri.classNames.orEmpty()
if (p.presentableName != null) text(p.presentableName + ": ")
+ annotationsInline(p)
link(linkText, p.dri)
list(p.projections, prefix = "<", suffix = ">") {
signatureForProjection(it, showFullyQualifiedName)
@@ -360,12 +367,16 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
}
is TypeAliased -> signatureForProjection(p.typeAlias)
- is JavaObject -> link("Any", DriOfAny)
+ is JavaObject -> {
+ annotationsInline(p)
+ link("Any", DriOfAny)
+ }
is Void -> link("Unit", DriOfUnit)
is PrimitiveJavaType -> signatureForProjection(p.translateToKotlin(), showFullyQualifiedName)
is Dynamic -> text("dynamic")
is UnresolvedBound -> text(p.name)
}
+ }
private fun funType(dri: DRI, sourceSets: Set<DokkaSourceSet>, type: FunctionalTypeConstructor) =
contentBuilder.contentFor(dri, sourceSets, ContentKind.Main) {
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
index 2266b691..bb350b34 100644
--- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
+++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
@@ -5,6 +5,7 @@ import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.DriOfAny
import org.jetbrains.dokka.links.DriOfUnit
import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.AnnotationTarget
import org.jetbrains.dokka.model.properties.WithExtraProperties
object KotlinSignatureUtils : JvmSignatureUtils {
@@ -18,10 +19,10 @@ object KotlinSignatureUtils : JvmSignatureUtils {
)
- override fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: Documentable) =
+ override fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: AnnotationTarget) =
annotationsBlockWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension)
- override fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: Documentable) =
+ override fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: AnnotationTarget) =
annotationsInlineWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension)
override fun <T : Documentable> WithExtraProperties<T>.modifiers() =
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
index 18231601..51389f37 100644
--- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
@@ -14,6 +14,7 @@ import org.jetbrains.dokka.base.translators.unquotedValue
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.links.Callable
import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.AnnotationTarget
import org.jetbrains.dokka.model.Nullable
import org.jetbrains.dokka.model.TypeConstructor
import org.jetbrains.dokka.model.doc.*
@@ -114,7 +115,7 @@ private class DokkaDescriptorVisitor(
}
}
- private fun <T> T.toSourceSetDependent() = if(this != null) mapOf(sourceSet to this) else emptyMap()
+ private fun <T> T.toSourceSetDependent() = if (this != null) mapOf(sourceSet to this) else emptyMap()
suspend fun visitPackageFragmentDescriptor(
descriptor: PackageFragmentDescriptor,
@@ -489,7 +490,8 @@ private class DokkaDescriptorVisitor(
extra = PropertyContainer.withAll(
InheritedMember(inheritedFrom.toSourceSetDependent()),
descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(),
- (descriptor.getAnnotations() + descriptor.fileLevelAnnotations()).toSourceSetDependent().toAnnotations(),
+ (descriptor.getAnnotations() + descriptor.fileLevelAnnotations()).toSourceSetDependent()
+ .toAnnotations(),
)
)
}
@@ -784,36 +786,45 @@ private class DokkaDescriptorVisitor(
)
private suspend fun org.jetbrains.kotlin.descriptors.annotations.Annotations.getPresentableName(): String? =
- map { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name")
+ mapNotNull { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name")
.safeAs<StringValue>()?.value?.let { unquotedValue(it) }
- private suspend fun KotlinType.toBound(): Bound = when (this) {
-
- is DynamicType -> Dynamic
- is AbbreviatedType -> TypeAliased(
- abbreviation.toBound(),
- expandedType.toBound()
- )
- else -> when (val ctor = constructor.declarationDescriptor) {
- is TypeParameterDescriptor -> TypeParameter(
- dri = DRI.from(ctor),
- name = ctor.name.asString(),
- presentableName = annotations.getPresentableName()
- )
- is FunctionClassDescriptor -> FunctionalTypeConstructor(
- DRI.from(ctor),
- arguments.map { it.toProjection() },
- isExtensionFunction = isExtensionFunctionType || isBuiltinExtensionFunctionalType,
- isSuspendable = isSuspendFunctionTypeOrSubtype,
- presentableName = annotations.getPresentableName()
+ private suspend fun KotlinType.toBound(): Bound {
+ suspend fun <T : AnnotationTarget> annotations(): PropertyContainer<T> =
+ getAnnotations().takeIf { it.isNotEmpty() }?.let { annotations ->
+ PropertyContainer.withAll(annotations.toSourceSetDependent().toAnnotations())
+ } ?: PropertyContainer.empty()
+
+ return when (this) {
+ is DynamicType -> Dynamic
+ is AbbreviatedType -> TypeAliased(
+ abbreviation.toBound(),
+ expandedType.toBound()
)
- else -> GenericTypeConstructor(
- DRI.from(ctor!!), // TODO: remove '!!'
- arguments.map { it.toProjection() },
- annotations.getPresentableName()
- )
- }.let {
- if (isMarkedNullable) Nullable(it) else it
+ else -> when (val ctor = constructor.declarationDescriptor) {
+ is TypeParameterDescriptor -> TypeParameter(
+ dri = DRI.from(ctor),
+ name = ctor.name.asString(),
+ presentableName = annotations.getPresentableName(),
+ extra = annotations()
+ )
+ is FunctionClassDescriptor -> FunctionalTypeConstructor(
+ DRI.from(ctor),
+ arguments.map { it.toProjection() },
+ isExtensionFunction = isExtensionFunctionType || isBuiltinExtensionFunctionalType,
+ isSuspendable = isSuspendFunctionTypeOrSubtype,
+ presentableName = annotations.getPresentableName(),
+ extra = annotations()
+ )
+ else -> GenericTypeConstructor(
+ DRI.from(ctor!!), // TODO: remove '!!'
+ arguments.map { it.toProjection() },
+ annotations.getPresentableName(),
+ extra = annotations()
+ )
+ }.let {
+ if (isMarkedNullable) Nullable(it) else it
+ }
}
}
@@ -823,7 +834,7 @@ private class DokkaDescriptorVisitor(
private suspend fun TypeProjection.formPossiblyVariant(): Projection =
type.toBound().wrapWithVariance(projectionKind)
- private suspend fun TypeParameterDescriptor.variantTypeParameter(type: TypeParameter) =
+ private fun TypeParameterDescriptor.variantTypeParameter(type: TypeParameter) =
type.wrapWithVariance(variance)
private fun <T : Bound> T.wrapWithVariance(variance: org.jetbrains.kotlin.types.Variance) =
@@ -909,7 +920,7 @@ private class DokkaDescriptorVisitor(
private suspend fun Annotated.getAnnotations() = annotations.parallelMapNotNull { it.toAnnotation() }
- private suspend fun ConstantValue<*>.toValue(): AnnotationParameterValue? = when (this) {
+ private fun ConstantValue<*>.toValue(): AnnotationParameterValue? = when (this) {
is ConstantsAnnotationValue -> value.toAnnotation()?.let { AnnotationValue(it) }
is ConstantsArrayValue -> ArrayValue(value.mapNotNull { it.toValue() })
is ConstantsEnumValue -> EnumValue(
@@ -933,7 +944,7 @@ private class DokkaDescriptorVisitor(
else -> StringValue(unquotedValue(toString()))
}
- private suspend fun AnnotationDescriptor.toAnnotation(scope: Annotations.AnnotationScope = Annotations.AnnotationScope.DIRECT): Annotations.Annotation =
+ private fun AnnotationDescriptor.toAnnotation(scope: Annotations.AnnotationScope = Annotations.AnnotationScope.DIRECT): Annotations.Annotation =
Annotations.Annotation(
DRI.from(annotationClass as DeclarationDescriptor),
allValueArguments.map { it.key.asString() to it.value.toValue() }.filter {
@@ -944,7 +955,8 @@ private class DokkaDescriptorVisitor(
)
private fun AnnotationDescriptor.mustBeDocumented(): Boolean =
- annotationClass?.annotations?.hasAnnotation(FqName("kotlin.annotation.MustBeDocumented")) ?: false
+ if (source.toString() == "NO_SOURCE") false
+ else annotationClass?.annotations?.hasAnnotation(FqName("kotlin.annotation.MustBeDocumented")) ?: false
private suspend fun PropertyDescriptor.getAnnotationsWithBackingField(): List<Annotations.Annotation> =
getAnnotations() + (backingField?.getAnnotations() ?: emptyList())
@@ -1010,13 +1022,14 @@ private class DokkaDescriptorVisitor(
private fun ConstantsEnumValue.fullEnumEntryName() =
"${this.enumClassId.relativeClassName.asString()}.${this.enumEntryName.identifier}"
- private fun DeclarationDescriptorWithSource.ktFile(): KtFile? = (source.containingFile as? PsiSourceFile)?.psiFile as? KtFile
+ private fun DeclarationDescriptorWithSource.ktFile(): KtFile? =
+ (source.containingFile as? PsiSourceFile)?.psiFile as? KtFile
private suspend fun DeclarationDescriptorWithSource.fileLevelAnnotations() = ktFile()
?.let { file -> resolutionFacade.resolveSession.getFileAnnotations(file) }
?.toList()
+ ?.parallelMap { it.toAnnotation(scope = Annotations.AnnotationScope.FILE) }
.orEmpty()
- .parallelMap { it.toAnnotation(scope = Annotations.AnnotationScope.FILE) }
}
private data class AncestryLevel(
diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
index 5b913dcb..c09d1156 100644
--- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
@@ -22,9 +22,11 @@ import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.nextTarget
import org.jetbrains.dokka.links.withClass
import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.AnnotationTarget
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.dokka.model.doc.Param
import org.jetbrains.dokka.model.properties.PropertyContainer
+import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.querySingle
@@ -200,8 +202,15 @@ class DefaultPsiToDocumentableTranslator(
parseSupertypes(superTypes)
val (regularFunctions, accessors) = splitFunctionsAndAccessors()
val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent()
- val allFunctions = async { regularFunctions.parallelMapNotNull { if (!it.isConstructor) parseFunction(it, parentDRI = dri) else null } +
- superMethods.parallelMap { parseFunction(it.first, inheritedFrom = it.second) } }
+ val allFunctions = async {
+ regularFunctions.parallelMapNotNull {
+ if (!it.isConstructor) parseFunction(
+ it,
+ parentDRI = dri
+ ) else null
+ } +
+ superMethods.parallelMap { parseFunction(it.first, inheritedFrom = it.second) }
+ }
val source = PsiDocumentableSource(this).toSourceSetDependent()
val classlikes = async { innerClasses.asIterable().parallelMap { parseClasslike(it, dri) } }
val visibility = getVisibility().toSourceSetDependent()
@@ -406,40 +415,61 @@ class DefaultPsiToDocumentableTranslator(
Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap())
}
- private fun getBound(type: PsiType): Bound =
- cachedBounds.getOrPut(type.canonicalText) {
- when (type) {
- is PsiClassReferenceType -> {
- val resolved: PsiClass = type.resolve()
- ?: return UnresolvedBound(type.presentableText)
+ private fun getBound(type: PsiType): Bound {
+ fun annotationsFromType(): List<Annotations.Annotation> = type.annotations.toList().toListOfAnnotations()
+
+ fun <T : AnnotationTarget> annotations(): PropertyContainer<T> =
+ annotationsFromType().takeIf { it.isNotEmpty() }?.let { annotations ->
+ PropertyContainer.withAll(annotations.toSourceSetDependent().toAnnotations())
+ } ?: PropertyContainer.empty()
+
+ fun bound() = when (type) {
+ is PsiClassReferenceType ->
+ type.resolve()?.let { resolved ->
when {
- resolved.qualifiedName == "java.lang.Object" -> JavaObject
+ resolved.qualifiedName == "java.lang.Object" -> JavaObject(annotations())
resolved is PsiTypeParameter ->
TypeParameter(
dri = DRI.from(resolved),
- name = resolved.name.orEmpty()
+ name = resolved.name.orEmpty(),
+ extra = annotations()
)
Regex("kotlin\\.jvm\\.functions\\.Function.*").matches(resolved.qualifiedName ?: "") ||
Regex("java\\.util\\.function\\.Function.*").matches(
resolved.qualifiedName ?: ""
) -> FunctionalTypeConstructor(
DRI.from(resolved),
- type.parameters.map { getProjection(it) }
+ type.parameters.map { getProjection(it) },
+ extra = annotations()
)
else -> GenericTypeConstructor(
DRI.from(resolved),
- type.parameters.map { getProjection(it) })
+ type.parameters.map { getProjection(it) },
+ extra = annotations()
+ )
}
- }
- is PsiArrayType -> GenericTypeConstructor(
- DRI("kotlin", "Array"),
- listOf(getProjection(type.componentType))
- )
- is PsiPrimitiveType -> if (type.name == "void") Void else PrimitiveJavaType(type.name)
- is PsiImmediateClassType -> JavaObject
- else -> throw IllegalStateException("${type.presentableText} is not supported by PSI parser")
+ } ?: UnresolvedBound(type.presentableText)
+ is PsiArrayType -> GenericTypeConstructor(
+ DRI("kotlin", "Array"),
+ listOf(getProjection(type.componentType)),
+ extra = annotations()
+ )
+ is PsiPrimitiveType -> if (type.name == "void") Void else PrimitiveJavaType(type.name)
+ is PsiImmediateClassType -> JavaObject(annotations())
+ else -> throw IllegalStateException("${type.presentableText} is not supported by PSI parser")
+ }
+
+ //We would like to cache most of the bounds since it is not common to annotate them,
+ //but if this is the case, we treat them as 'one of'
+ return if (annotationsFromType().isEmpty()) {
+ cachedBounds.getOrPut(type.canonicalText) {
+ bound()
}
+ } else {
+ bound()
}
+ }
+
private fun getVariance(type: PsiWildcardType): Projection = when {
type.extendsBound != PsiType.NULL -> Covariance(getBound(type.extendsBound))
diff --git a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
index 164e5f4a..0b9e2390 100644
--- a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
+++ b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
@@ -5,11 +5,15 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.Annotations
import org.jetbrains.dokka.model.StringValue
+import org.jetbrains.dokka.model.dfs
import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.ContentText
+import org.jetbrains.dokka.pages.MemberPageNode
import org.jetbrains.dokka.pages.PackagePageNode
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.junit.jupiter.api.Test
import utils.ParamAttributes
+import utils.assertNotNull
import utils.bareSignature
import utils.propertySignature
import kotlin.test.assertEquals
@@ -227,7 +231,7 @@ class ContentForAnnotationsTest : BaseAbstractTest() {
}
@Test
- fun `JvmName for property with setter and getter`(){
+ fun `JvmName for property with setter and getter`() {
testInline(
"""
|/src/main/kotlin/test/source.kt
@@ -237,7 +241,8 @@ class ContentForAnnotationsTest : BaseAbstractTest() {
|var property: String
| get() = ""
| set(value) {}
- """.trimIndent(), testConfiguration) {
+ """.trimIndent(), testConfiguration
+ ) {
documentablesCreationStage = { modules ->
fun expectedAnnotation(name: String) = Annotations.Annotation(
dri = DRI("kotlin.jvm", "JvmName"),
@@ -264,4 +269,49 @@ class ContentForAnnotationsTest : BaseAbstractTest() {
}
}
}
+
+ @Test
+ fun `annotated bounds in Kotlin`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |@MustBeDocumented
+ |@Target(AnnotationTarget.TYPE_PARAMETER)
+ |annotation class Hello(val bar: String)
+ |fun <T: @Hello("abc") String> foo(arg: String): List<T> = TODO()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { root ->
+ val fooPage = root.dfs { it.name == "foo" } as MemberPageNode
+ fooPage.content.dfs { it is ContentText && it.text == "Hello" }.assertNotNull()
+ }
+ }
+ }
+
+ @Test
+ fun `annotated bounds in Java`() {
+ testInline(
+ """
+ |/src/main/java/demo/AnnotationTest.java
+ |package demo;
+ |import java.lang.annotation.*;
+ |import java.util.List;
+ |@Documented
+ |@Target({ElementType.TYPE_USE, ElementType.TYPE})
+ |@interface Hello {
+ | public String bar() default "";
+ |}
+ |public class AnnotationTest {
+ | public <T extends @Hello(bar = "baz") String> List<T> foo() {
+ | return null;
+ | }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { root ->
+ val fooPage = root.dfs { it.name == "foo" } as MemberPageNode
+ fooPage.content.dfs { it is ContentText && it.text == "Hello" }.assertNotNull()
+ }
+ }
+ }
}
diff --git a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt
index 5679010d..9e00fafd 100644
--- a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt
+++ b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt
@@ -1,11 +1,10 @@
package model.annotations
import org.jetbrains.dokka.links.DRI
-import org.jetbrains.dokka.model.Annotations
-import org.jetbrains.dokka.model.DClass
-import org.jetbrains.dokka.model.DFunction
+import org.jetbrains.dokka.model.*
import org.junit.jupiter.api.Test
import utils.AbstractModelTest
+import kotlin.test.Ignore
import kotlin.test.assertEquals
class JavaAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {
@@ -85,4 +84,34 @@ class JavaAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/jav
}
}
}
+
+ @Test
+ fun `function with generic parameter that has annotated bounds`() {
+ inlineModelTest(
+ """
+ |@Retention(RetentionPolicy.RUNTIME)
+ |@Target({ElementType.TYPE_USE})
+ |@interface Hello {
+ | public String bar() default "";
+ |}
+ |class Test {
+ | public <T extends @Hello(bar = "baz") String> List<T> foo() {
+ | return null;
+ | }
+ |}
+ """.trimIndent()
+ ) {
+ with((this / "java" / "Test").cast<DClass>()) {
+ with((this / "foo").cast<DFunction>()) {
+ val annotations = ((generics.first().bounds.first() as Nullable).inner as GenericTypeConstructor)
+ .extra[Annotations]?.directAnnotations?.flatMap { it.value }
+ val driOfHello = DRI("java", "Hello")
+ val annotationsValues = annotations?.flatMap { it.params.values }?.map { it.toString() }?.toList()
+
+ assertEquals(listOf(driOfHello), annotations?.map { it.dri })
+ assertEquals(listOf("baz"), annotationsValues)
+ }
+ }
+ }
+ }
} \ 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 4cc34c09..e88d7353 100644
--- a/plugins/base/src/test/kotlin/model/annotations/KotlinAnnotationsForParametersTest.kt
+++ b/plugins/base/src/test/kotlin/model/annotations/KotlinAnnotationsForParametersTest.kt
@@ -3,6 +3,8 @@ package model.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.junit.jupiter.api.Test
import utils.AbstractModelTest
import kotlin.test.assertEquals
@@ -27,4 +29,25 @@ class KotlinAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/a
}
}
}
+
+ @Test
+ fun `generic receiver with annotated bounds`() {
+ inlineModelTest(
+ """
+ |@Target(AnnotationTarget.TYPE_PARAMETER)
+ |annotation class Hello(val bar: String)
+ |fun <T: @Hello("abc") String> foo(arg: String): List<T> = TODO()
+ """.trimIndent()
+ ) {
+ with((this / "annotations" / "foo").cast<DFunction>()) {
+ val annotations = (generics.first().bounds.first() as GenericTypeConstructor)
+ .extra[Annotations]?.directAnnotations?.flatMap { it.value }
+ val driOfHello = DRI("annotations", "Hello")
+ val annotationsValues = annotations?.flatMap { it.params.values }?.map { it.toString() }?.toList()
+
+ assertEquals(listOf(driOfHello), annotations?.map { it.dri })
+ assertEquals(listOf("abc"), annotationsValues)
+ }
+ }
+ }
} \ No newline at end of file