aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/translators
diff options
context:
space:
mode:
authorAndrzej Ratajczak <andrzej.ratajczak98@gmail.com>2020-05-04 13:53:10 +0200
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-05-26 11:32:03 +0200
commitd47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc (patch)
tree364724f661349211f053ea80db1a7feb283f48ba /plugins/base/src/main/kotlin/translators
parentb1e3033fca65ac1e8e312e51d2eed4f278ddb076 (diff)
downloaddokka-d47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc.tar.gz
dokka-d47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc.tar.bz2
dokka-d47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc.zip
Enhance signature presentation. Support presetnation Java as Kotlin and Kotlin as Java. Refactor annotations creation from PSI/Descriptors. Add proper rendering of annotation signatures in both kotlin syntax and java syntax. Tests for annotations
Diffstat (limited to 'plugins/base/src/main/kotlin/translators')
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt112
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt87
2 files changed, 140 insertions, 59 deletions
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
index f5b86df6..c24a3384 100644
--- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
@@ -1,5 +1,6 @@
package org.jetbrains.dokka.base.translators.descriptors
+import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.links.Callable
@@ -12,19 +13,27 @@ import org.jetbrains.dokka.parsers.MarkdownParser
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator
+import org.jetbrains.kotlin.asJava.classes.tryResolveMarkerInterfaceFQName
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.codegen.isJvmStaticInObjectOrClassOrInterface
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibility
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.load.kotlin.toSourceElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.components.isVararg
-import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
+import org.jetbrains.kotlin.resolve.constants.ConstantValue
+import org.jetbrains.kotlin.resolve.constants.AnnotationValue as ConstantsAnnotationValue
+import org.jetbrains.kotlin.resolve.constants.ArrayValue as ConstantsArrayValue
+import org.jetbrains.kotlin.resolve.constants.EnumValue as ConstantsEnumValue
+import org.jetbrains.kotlin.resolve.constants.KClassValue as ConstantsKtClassValue
+import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.NormalClass
+import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.LocalClass
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
@@ -32,10 +41,13 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
+import org.jetbrains.kotlin.types.DynamicType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.nio.file.Paths
+import java.lang.IllegalArgumentException
+import kotlin.reflect.jvm.internal.impl.resolve.constants.KClassValue
object DefaultDescriptorToDocumentableTranslator : SourceToDocumentableTranslator {
@@ -285,7 +297,11 @@ private class DokkaDescriptorVisitor(
expectPresentInSet = sourceSet.takeIf { isExpect },
sourceSets = listOf(sourceSet),
generics = descriptor.typeParameters.map { it.toTypeParameter() },
- extra = PropertyContainer.withAll(descriptor.additionalExtras(), descriptor.getAnnotations())
+ extra = PropertyContainer.withAll(
+ (descriptor.additionalExtras() + (descriptor.backingField?.getAnnotationsAsExtraModifiers()
+ ?: emptyList())).toProperty(),
+ descriptor.getAllAnnotations()
+ )
)
}
@@ -375,7 +391,8 @@ private class DokkaDescriptorVisitor(
type = descriptor.type.toBound(),
expectPresentInSet = null,
documentation = descriptor.resolveDescriptorData(),
- sourceSets = listOf(sourceSet)
+ sourceSets = listOf(sourceSet),
+ extra = PropertyContainer.withAll(descriptor.getAnnotations())
)
private fun visitPropertyAccessorDescriptor(
@@ -395,7 +412,7 @@ private class DokkaDescriptorVisitor(
expectPresentInSet = sourceSet.takeIf { isExpect },
documentation = descriptor.resolveDescriptorData(),
sourceSets = listOf(sourceSet),
- extra = PropertyContainer.withAll(descriptor.additionalExtras(), descriptor.getAnnotations())
+ extra = PropertyContainer.withAll(descriptor.additionalExtras(), getAllAnnotations())
)
val name = run {
@@ -523,20 +540,23 @@ private class DokkaDescriptorVisitor(
extra = PropertyContainer.withAll(additionalExtras())
)
- private fun KotlinType.toBound(): Bound = when (val ctor = constructor.declarationDescriptor) {
- is TypeParameterDescriptor -> OtherParameter(
- declarationDRI = DRI.from(ctor.containingDeclaration),
- name = ctor.name.asString()
- ).let {
- if (isMarkedNullable) Nullable(it) else it
+ private fun KotlinType.toBound(): Bound = when (this) {
+ is DynamicType -> Dynamic
+ else -> when (val ctor = constructor.declarationDescriptor) {
+ is TypeParameterDescriptor -> OtherParameter(
+ declarationDRI = DRI.from(ctor.containingDeclaration),
+ name = ctor.name.asString()
+ ).let {
+ if (isMarkedNullable) Nullable(it) else it
+ }
+ else -> TypeConstructor(
+ DRI.from(constructor.declarationDescriptor!!), // TODO: remove '!!'
+ arguments.map { it.toProjection() },
+ if (isExtensionFunctionType) FunctionModifiers.EXTENSION
+ else if (isFunctionType) FunctionModifiers.FUNCTION
+ else FunctionModifiers.NONE
+ )
}
- else -> TypeConstructor(
- DRI.from(constructor.declarationDescriptor!!), // TODO: remove '!!'
- arguments.map { it.toProjection() },
- if (isExtensionFunctionType) FunctionModifiers.EXTENSION
- else if (isFunctionType) FunctionModifiers.FUNCTION
- else FunctionModifiers.NONE
- )
}
private fun TypeProjection.toProjection(): Projection =
@@ -573,7 +593,6 @@ private class DokkaDescriptorVisitor(
DescriptorDocumentableSource(this).toSourceSetDependent()
private fun FunctionDescriptor.additionalExtras() = listOfNotNull(
- ExtraModifiers.DYNAMIC.takeIf { isDynamic() },
ExtraModifiers.INFIX.takeIf { isInfix },
ExtraModifiers.INLINE.takeIf { isInline },
ExtraModifiers.SUSPEND.takeIf { isSuspend },
@@ -585,17 +604,14 @@ private class DokkaDescriptorVisitor(
).toProperty()
private fun ClassDescriptor.additionalExtras() = listOfNotNull(
- ExtraModifiers.DYNAMIC.takeIf { isDynamic() },
ExtraModifiers.INLINE.takeIf { isInline },
ExtraModifiers.EXTERNAL.takeIf { isExternal },
ExtraModifiers.INNER.takeIf { isInner },
- ExtraModifiers.DATA.takeIf { isData },
- ExtraModifiers.OVERRIDE.takeIf { getSuperInterfaces().isNotEmpty() || getSuperClassNotAny() != null }
+ ExtraModifiers.DATA.takeIf { isData }
).toProperty()
private fun ValueParameterDescriptor.additionalExtras() =
listOfNotNull(
- ExtraModifiers.DYNAMIC.takeIf { isDynamic() },
ExtraModifiers.NOINLINE.takeIf { isNoinline },
ExtraModifiers.CROSSINLINE.takeIf { isCrossinline },
ExtraModifiers.CONST.takeIf { isConst },
@@ -605,28 +621,62 @@ private class DokkaDescriptorVisitor(
private fun TypeParameterDescriptor.additionalExtras() =
listOfNotNull(
- ExtraModifiers.DYNAMIC.takeIf { isDynamic() },
ExtraModifiers.REIFIED.takeIf { isReified }
).toProperty()
private fun PropertyDescriptor.additionalExtras() = listOfNotNull(
- ExtraModifiers.DYNAMIC.takeIf { isDynamic() },
ExtraModifiers.CONST.takeIf { isConst },
ExtraModifiers.LATEINIT.takeIf { isLateInit },
ExtraModifiers.STATIC.takeIf { isJvmStaticInObjectOrClassOrInterface() },
ExtraModifiers.EXTERNAL.takeIf { isExternal },
ExtraModifiers.OVERRIDE.takeIf { DescriptorUtils.isOverride(this) }
- ).toProperty()
+ )
private fun List<ExtraModifiers>.toProperty() =
AdditionalModifiers(this.toSet())
- private fun DeclarationDescriptor.getAnnotations() = annotations.map { annotation ->
- Annotations.Annotation(
- annotation.let { it.annotationClass as DeclarationDescriptor }.let { DRI.from(it) },
- annotation.allValueArguments.map { (k, v) -> k.asString() to v.value.toString() }.toMap()
- )
- }.let(::Annotations)
+ private fun Annotated.getAnnotations() = getListOfAnnotations().let(::Annotations)
+
+ private fun Annotated.getListOfAnnotations() = annotations.map { it.toAnnotation() }
+
+ private fun ConstantValue<*>.toValue(): AnnotationParameterValue = when (this) {
+ is ConstantsAnnotationValue -> AnnotationValue(value.let { it.toAnnotation() })
+ is ConstantsArrayValue -> ArrayValue(value.map { it.toValue() })
+ is ConstantsEnumValue -> EnumValue(
+ enumEntryName.identifier,
+ enumClassId.let { DRI(it.packageFqName.asString(), it.relativeClassName.asString()) })
+ is ConstantsKtClassValue -> when(value) {
+ is NormalClass -> (value as NormalClass).value.classId.let {
+ ClassValue(
+ it.relativeClassName.asString(),
+ DRI(it.packageFqName.asString(), it.relativeClassName.asString())
+ )
+ }
+ is LocalClass -> (value as LocalClass).type.let {
+ ClassValue(
+ it.toString(),
+ DRI.from(it.constructor.declarationDescriptor as DeclarationDescriptor)
+ )
+ }
+ }
+ else -> StringValue(toString())
+ }
+
+ private fun AnnotationDescriptor.toAnnotation() = Annotations.Annotation(
+ DRI.from(annotationClass as DeclarationDescriptor),
+ allValueArguments.map { it.key.asString() to it.value.toValue() }.toMap()
+ )
+
+ private fun PropertyDescriptor.getAllAnnotations() =
+ (getListOfAnnotations() + (backingField?.getListOfAnnotations() ?: emptyList())).let(::Annotations)
+
+ private fun FieldDescriptor.getAnnotationsAsExtraModifiers() = getAnnotations().content.mapNotNull {
+ try {
+ ExtraModifiers.valueOf(it.dri.classNames?.toUpperCase() ?: "")
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+ }
private fun ValueParameterDescriptor.getDefaultValue(): String? =
(source as? KotlinSourceElement)?.psi?.children?.find { it is KtExpression }?.text
diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
index ab4a84f6..0f8fc011 100644
--- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
@@ -1,10 +1,13 @@
package org.jetbrains.dokka.base.translators.psi
+import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute
import com.intellij.lang.jvm.JvmModifier
import com.intellij.lang.jvm.types.JvmReferenceType
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.*
import com.intellij.psi.impl.source.PsiClassReferenceType
+import com.intellij.psi.impl.source.PsiImmediateClassType
+import com.intellij.psi.impl.source.tree.java.PsiArrayInitializerMemberValueImpl
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.nextTarget
import org.jetbrains.dokka.links.withClass
@@ -24,6 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.File
+import java.lang.ClassValue
object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
@@ -162,7 +166,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
constructors.map { parseFunction(it, true) },
mapTypeParameters(dri),
listOf(sourceSetData),
- PropertyContainer.empty<DAnnotation>() + annotations.toList().toExtra()
+ PropertyContainer.empty<DAnnotation>() + annotations.toList().toListOfAnnotations().let(::Annotations)
)
isEnum -> DEnum(
dri,
@@ -177,7 +181,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
emptyList(),
emptyList(),
listOf(sourceSetData),
- PropertyContainer.empty<DEnumEntry>() + entry.annotations.toList().toExtra()
+ PropertyContainer.empty<DEnumEntry>() + entry.annotations.toList().toListOfAnnotations().let(::Annotations)
)
},
documentation,
@@ -191,7 +195,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
constructors.map { parseFunction(it, true) },
ancestors,
listOf(sourceSetData),
- PropertyContainer.empty<DEnum>() + annotations.toList().toExtra()
+ PropertyContainer.empty<DEnum>() + annotations.toList().toListOfAnnotations().let(::Annotations)
)
isInterface -> DInterface(
dri,
@@ -207,7 +211,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
mapTypeParameters(dri),
ancestors,
listOf(sourceSetData),
- PropertyContainer.empty<DInterface>() + annotations.toList().toExtra()
+ PropertyContainer.empty<DInterface>() + annotations.toList().toListOfAnnotations().let(::Annotations)
)
else -> DClass(
dri,
@@ -225,7 +229,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
null,
modifiers,
listOf(sourceSetData),
- PropertyContainer.empty<DClass>() + annotations.toList().toExtra()
+ PropertyContainer.empty<DClass>() + annotations.toList().toListOfAnnotations().let(::Annotations)
)
}
}
@@ -259,15 +263,17 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
null,
psi.getModifier().toPlatformDependant(),
listOf(sourceSetData),
- PropertyContainer.withAll(
- InheritedFunction(isInherited),
- psi.annotations.toList().toExtra(),
- psi.additionalExtras()
- )
+ psi.additionalExtras().let {
+ PropertyContainer.withAll(
+ InheritedFunction(isInherited),
+ it,
+ (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).let(::Annotations)
+ )
+ }
)
}
- private fun PsiMethod.additionalExtras() = AdditionalModifiers(
+ private fun PsiModifierListOwner.additionalExtras() = AdditionalModifiers(
listOfNotNull(
ExtraModifiers.STATIC.takeIf { hasModifier(JvmModifier.STATIC) },
ExtraModifiers.NATIVE.takeIf { hasModifier(JvmModifier.NATIVE) },
@@ -279,6 +285,13 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
).toSet()
)
+ private fun AdditionalModifiers.toListOfAnnotations() = this.content.map {
+ if (it.name != "STATIC")
+ Annotations.Annotation(DRI("kotlin.jvm", it.name.toLowerCase().capitalize()), emptyMap())
+ else
+ Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap())
+ }
+
private fun getBound(type: PsiType): Bound =
cachedBounds.getOrPut(type.canonicalText) {
when (type) {
@@ -295,7 +308,8 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
DRI("kotlin", "Array"),
listOf(getProjection(type.componentType))
)
- is PsiPrimitiveType -> if(type.name == "void") Void else PrimitiveJavaType(type.name)
+ 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")
}
}
@@ -375,26 +389,43 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator {
psi.getModifier().toPlatformDependant(),
listOf(sourceSetData),
emptyList(),
- PropertyContainer.empty<DProperty>() + psi.annotations.toList().toExtra()
+ psi.additionalExtras().let {
+ PropertyContainer.withAll<DProperty>(
+ it,
+ (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).let(::Annotations)
+ )
+ }
)
}
- private fun Collection<PsiAnnotation>.toExtra() = mapNotNull { annotation ->
- val resolved = annotation.getChildOfType<PsiJavaCodeReferenceElement>()?.resolve() ?: run {
- logger.error("$annotation cannot be resolved to symbol!")
- return@mapNotNull null
- }
+ private fun Collection<PsiAnnotation>.toListOfAnnotations() = mapNotNull { it.toAnnotation() }
- Annotations.Annotation(
- DRI.from(resolved),
- annotation.attributes.mapNotNull { attr ->
- if (attr is PsiNameValuePair) {
- attr.value?.text?.let { attr.attributeName to it }
- } else {
- attr.attributeName to ""
- }
- }.toMap()
+ private fun JvmAnnotationAttribute.toValue(): AnnotationParameterValue = when (this) {
+ is PsiNameValuePair -> value?.toValue() ?: StringValue("")
+ else -> StringValue(this.attributeName)
+ }
+
+ private fun PsiAnnotationMemberValue.toValue(): AnnotationParameterValue = when(this) {
+ is PsiAnnotation -> AnnotationValue(toAnnotation())
+ is PsiArrayInitializerMemberValue -> ArrayValue(this.initializers.map { it.toValue() })
+ is PsiReferenceExpression -> EnumValue(
+ text ?: "",
+ driOfReference()
)
- }.let(::Annotations)
+ is PsiClassObjectAccessExpression -> ClassValue(
+ text ?: "",
+ DRI.from(((type as PsiImmediateClassType).parameters.single() as PsiClassReferenceType).resolve()!!)
+ )
+ else -> StringValue(text ?: "")
+ }
+
+ private fun PsiAnnotation.toAnnotation() = Annotations.Annotation(
+ driOfReference(),
+ attributes.mapNotNull { it.attributeName to it.toValue() }.toMap()
+ )
+
+ private fun PsiElement.driOfReference() = DRI.from(getChildOfType<PsiJavaCodeReferenceElement>()?.resolve() ?:
+ throw IllegalStateException("$this cannot be resolved to symbol!")
+ )
}
}