Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better handling of functional types in rendered output #1483

Merged
merged 1 commit into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions core/src/main/kotlin/model/Documentable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,13 @@ data class DTypeParameter(
constructor(
dri: DRI,
name: String,
presentableName: String?,
documentation: SourceSetDependent<DocumentationNode>,
expectPresentInSet: DokkaSourceSet?,
bounds: List<Bound>,
sourceSets: Set<DokkaSourceSet>,
extra: PropertyContainer<DTypeParameter> = PropertyContainer.empty()
) : this(Invariance(TypeParameter(dri, name)), documentation, expectPresentInSet, bounds, sourceSets, extra)
) : this(Invariance(TypeParameter(dri, name, presentableName)), documentation, expectPresentInSet, bounds, sourceSets, extra)

override val dri: DRI by variantTypeParameter.inner::dri
override val name: String by variantTypeParameter.inner::name
Expand Down Expand Up @@ -376,13 +377,28 @@ data class DTypeAlias(

sealed class Projection
sealed class Bound : Projection()
data class TypeParameter(val dri: DRI, val name: String) : Bound()
data class TypeParameter(val dri: DRI, val name: String, val presentableName: String? = null) : Bound()
object Star : Projection()
data class TypeConstructor(
val dri: DRI,
val projections: List<Projection>,
val modifier: FunctionModifiers = FunctionModifiers.NONE
) : Bound()

sealed class TypeConstructor : Bound() {
abstract val dri: DRI
abstract val projections: List<Projection>
abstract val presentableName: String?
}

data class GenericTypeConstructor(
override val dri: DRI,
override val projections: List<Projection>,
override val presentableName: String? = null
) : TypeConstructor()

data class FunctionalTypeConstructor(
override val dri: DRI,
override val projections: List<Projection>,
val isExtensionFunction: Boolean = false,
val isSuspendable: Boolean = false,
override val presentableName: String? = null
) : TypeConstructor()

data class Nullable(val inner: Bound) : Bound()

Expand All @@ -406,14 +422,10 @@ object JavaObject : Bound()
object Dynamic : Bound()
data class UnresolvedBound(val name: String) : Bound()

enum class FunctionModifiers {
NONE, FUNCTION, EXTENSION
}

fun Variance<TypeParameter>.withDri(dri: DRI) = when(this) {
is Contravariance -> Contravariance(TypeParameter(dri, inner.name))
is Covariance -> Covariance(TypeParameter(dri, inner.name))
is Invariance -> Invariance(TypeParameter(dri, inner.name))
is Contravariance -> Contravariance(TypeParameter(dri, inner.name, inner.presentableName))
is Covariance -> Covariance(TypeParameter(dri, inner.name, inner.presentableName))
is Invariance -> Invariance(TypeParameter(dri, inner.name, inner.presentableName))
}

private fun String.shorten(maxLength: Int) = lineSequence().first().let {
Expand Down
28 changes: 14 additions & 14 deletions plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -317,14 +317,15 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
when (p) {
is TypeParameter -> link(p.name, p.dri)

is TypeConstructor -> if (p.function)
is FunctionalTypeConstructor ->
+funType(mainDRI.single(), mainSourcesetData, p)
else

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 + ": ")
link(linkText, p.dri)
list(p.projections, prefix = "<", suffix = ">") {
signatureForProjection(it, showFullyQualifiedName)
Expand All @@ -351,14 +352,18 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
is UnresolvedBound -> text(p.name)
}

private fun funType(dri: DRI, sourceSets: Set<DokkaSourceSet>, type: TypeConstructor) =
private fun funType(dri: DRI, sourceSets: Set<DokkaSourceSet>, type: FunctionalTypeConstructor) =
contentBuilder.contentFor(dri, sourceSets, ContentKind.Main) {
if (type.extension) {

if (type.presentableName != null) text(type.presentableName + ": ")
if (type.isSuspendable) text("suspend ")

if (type.isExtensionFunction) {
signatureForProjection(type.projections.first())
text(".")
}

val args = if (type.extension)
val args = if (type.isExtensionFunction)
type.projections.drop(1)
else
type.projections
Expand All @@ -373,16 +378,11 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
}
}

private fun PrimitiveJavaType.translateToKotlin() = TypeConstructor(
private fun PrimitiveJavaType.translateToKotlin() = GenericTypeConstructor(
dri = dri,
projections = emptyList()
projections = emptyList(),
presentableName = null
)

private val DTypeParameter.nontrivialBounds: List<Bound>
get() = bounds.filterNot { it is Nullable && it.inner.driOrNull == DriOfAny }

val TypeConstructor.function
get() = modifier == FunctionModifiers.FUNCTION || modifier == FunctionModifiers.EXTENSION

val TypeConstructor.extension
get() = modifier == FunctionModifiers.EXTENSION
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype
import org.jetbrains.kotlin.codegen.isJvmStaticInObjectOrClassOrInterface
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind
Expand Down Expand Up @@ -585,7 +588,7 @@ private class DokkaDescriptorVisitor(
private fun ClassDescriptor.resolveClassDescriptionData(): ClassInfo {

fun toTypeConstructor(kt: KotlinType) =
TypeConstructor(
GenericTypeConstructor(
DRI.from(kt.constructor.declarationDescriptor as DeclarationDescriptor),
kt.arguments.map { it.toProjection() })

Expand Down Expand Up @@ -620,7 +623,7 @@ private class DokkaDescriptorVisitor(
private fun TypeParameterDescriptor.toVariantTypeParameter() =
DTypeParameter(
variantTypeParameter(
TypeParameter(DRI.from(this), name.identifier)
TypeParameter(DRI.from(this), name.identifier, annotations.getPresentableName())
),
resolveDescriptorData(),
null,
Expand All @@ -629,6 +632,10 @@ private class DokkaDescriptorVisitor(
extra = PropertyContainer.withAll(additionalExtras().toSourceSetDependent().toAdditionalModifiers())
)

private fun org.jetbrains.kotlin.descriptors.annotations.Annotations.getPresentableName(): String? =
map { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name")
.safeAs<StringValue>()?.value?.drop(1)?.dropLast(1) // Dropping enclosing doublequotes because we don't want to have it in our custom signature serializer

private fun KotlinType.toBound(): Bound = when (this) {
is DynamicType -> Dynamic
is AbbreviatedType -> TypeAliased(
Expand All @@ -638,14 +645,20 @@ private class DokkaDescriptorVisitor(
else -> when (val ctor = constructor.declarationDescriptor) {
is TypeParameterDescriptor -> TypeParameter(
dri = DRI.from(ctor),
name = ctor.name.asString()
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()
)
else -> TypeConstructor(
else -> GenericTypeConstructor(
DRI.from(ctor!!), // TODO: remove '!!'
arguments.map { it.toProjection() },
if (isExtensionFunctionType) FunctionModifiers.EXTENSION
else if (isFunctionType) FunctionModifiers.FUNCTION
else FunctionModifiers.NONE
annotations.getPresentableName()
)
}.let {
if (isMarkedNullable) Nullable(it) else it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
Expand Down Expand Up @@ -139,7 +141,7 @@ class DefaultPsiToDocumentableTranslator(
psiClass.isInterface -> DRI.from(psiClass) to JavaClassKindTypes.INTERFACE
else -> DRI.from(psiClass) to JavaClassKindTypes.CLASS
}
TypeConstructor(
GenericTypeConstructor(
dri,
psi.parameters.map(::getProjection)
) to javaClassKind
Expand Down Expand Up @@ -370,11 +372,19 @@ class DefaultPsiToDocumentableTranslator(
dri = DRI.from(resolved),
name = resolved.name.orEmpty()
)
else ->
TypeConstructor(DRI.from(resolved), type.parameters.map { getProjection(it) })
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) }
)
else -> GenericTypeConstructor(
DRI.from(resolved),
type.parameters.map { getProjection(it) })
}
}
is PsiArrayType -> TypeConstructor(
is PsiArrayType -> GenericTypeConstructor(
DRI("kotlin", "Array"),
listOf(getProjection(type.componentType))
)
Expand Down Expand Up @@ -411,6 +421,7 @@ class DefaultPsiToDocumentableTranslator(
DTypeParameter(
dri.copy(target = dri.target.nextTarget()),
type.name.orEmpty(),
null,
javadocParser.parseDocumentation(type).toSourceSetDependent(),
null,
mapBounds(type.bounds),
Expand Down
Loading