diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt index 0f73c8e1f1..579a7910e9 100644 --- a/core/src/main/kotlin/model/Documentable.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -192,9 +192,10 @@ data class DEnumEntry( override val functions: List, override val properties: List, override val classlikes: List, + override val sources: SourceSetDependent, override val sourceSets: Set, override val extra: PropertyContainer = PropertyContainer.empty() -) : Documentable(), WithScope, WithExtraProperties { +) : Documentable(), WithScope, WithExtraProperties, WithSources { override val children: List get() = (functions + properties + classlikes) @@ -321,9 +322,10 @@ data class DParameter( override val documentation: SourceSetDependent, override val expectPresentInSet: DokkaSourceSet?, override val type: Bound, + override val sources: SourceSetDependent, override val sourceSets: Set, override val extra: PropertyContainer = PropertyContainer.empty() -) : Documentable(), WithExtraProperties, WithType { +) : Documentable(), WithExtraProperties, WithType, WithSources { override val children: List get() = emptyList() @@ -335,9 +337,10 @@ data class DTypeParameter( override val documentation: SourceSetDependent, override val expectPresentInSet: DokkaSourceSet?, val bounds: List, + override val sources: SourceSetDependent, override val sourceSets: Set, override val extra: PropertyContainer = PropertyContainer.empty() -) : Documentable(), WithExtraProperties { +) : Documentable(), WithExtraProperties, WithSources { constructor( dri: DRI, @@ -346,13 +349,15 @@ data class DTypeParameter( documentation: SourceSetDependent, expectPresentInSet: DokkaSourceSet?, bounds: List, + sources: SourceSetDependent, sourceSets: Set, extra: PropertyContainer = PropertyContainer.empty() ) : this( - Invariance(TypeParameter(dri, name, presentableName)), + Invariance(TypeParameter(dri, name, presentableName, sources)), documentation, expectPresentInSet, bounds, + sources, sourceSets, extra ) @@ -374,10 +379,11 @@ data class DTypeAlias( override val visibility: SourceSetDependent, override val documentation: SourceSetDependent, override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent, override val sourceSets: Set, override val generics: List, override val extra: PropertyContainer = PropertyContainer.empty() -) : Documentable(), WithType, WithVisibility, WithExtraProperties, WithGenerics { +) : Documentable(), WithType, WithVisibility, WithExtraProperties, WithGenerics, WithSources { override val children: List get() = emptyList() @@ -386,26 +392,30 @@ data class DTypeAlias( sealed class Projection sealed class Bound : Projection() +val Bound.sources get() = (this as WithSources).sources data class TypeParameter( val dri: DRI, val name: String, val presentableName: String? = null, + override val sources: SourceSetDependent, override val extra: PropertyContainer = PropertyContainer.empty() -) : Bound(), AnnotationTarget, WithExtraProperties { +) : Bound(), AnnotationTarget, WithExtraProperties, WithSources { override fun withNewExtras(newExtras: PropertyContainer): TypeParameter = copy(extra = extra) } -sealed class TypeConstructor : Bound(), AnnotationTarget { +sealed class TypeConstructor : Bound(), AnnotationTarget, WithSources { abstract val dri: DRI abstract val projections: List abstract val presentableName: String? + abstract override val sources: SourceSetDependent } data class GenericTypeConstructor( override val dri: DRI, override val projections: List, override val presentableName: String? = null, + override val sources: SourceSetDependent, override val extra: PropertyContainer = PropertyContainer.empty() ) : TypeConstructor(), WithExtraProperties { override fun withNewExtras(newExtras: PropertyContainer): GenericTypeConstructor = @@ -418,6 +428,7 @@ data class FunctionalTypeConstructor( val isExtensionFunction: Boolean = false, val isSuspendable: Boolean = false, override val presentableName: String? = null, + override val sources: SourceSetDependent, override val extra: PropertyContainer = PropertyContainer.empty(), ) : TypeConstructor(), WithExtraProperties { override fun withNewExtras(newExtras: PropertyContainer): FunctionalTypeConstructor = @@ -428,39 +439,65 @@ data class FunctionalTypeConstructor( data class TypeAliased( val typeAlias: Bound, val inner: Bound, + override val sources: SourceSetDependent, override val extra: PropertyContainer = PropertyContainer.empty() -) : Bound(), AnnotationTarget, WithExtraProperties { +) : Bound(), AnnotationTarget, WithExtraProperties, WithSources { override fun withNewExtras(newExtras: PropertyContainer): TypeAliased = copy(extra = newExtras) } data class PrimitiveJavaType( val name: String, + override val sources: SourceSetDependent, override val extra: PropertyContainer = PropertyContainer.empty() -) : Bound(), AnnotationTarget, WithExtraProperties { +) : Bound(), AnnotationTarget, WithExtraProperties, WithSources { override fun withNewExtras(newExtras: PropertyContainer): PrimitiveJavaType = copy(extra = newExtras) } -data class JavaObject(override val extra: PropertyContainer = PropertyContainer.empty()) : - Bound(), AnnotationTarget, WithExtraProperties { +data class JavaObject( + override val sources: SourceSetDependent, + override val extra: PropertyContainer = PropertyContainer.empty() +) : + Bound(), AnnotationTarget, WithExtraProperties, WithSources { override fun withNewExtras(newExtras: PropertyContainer): JavaObject = copy(extra = newExtras) } data class UnresolvedBound( val name: String, + override val sources: SourceSetDependent, override val extra: PropertyContainer = PropertyContainer.empty() -) : Bound(), AnnotationTarget, WithExtraProperties { +) : Bound(), AnnotationTarget, WithExtraProperties, WithSources { override fun withNewExtras(newExtras: PropertyContainer): UnresolvedBound = copy(extra = newExtras) } // The following Projections are not AnnotationTargets; they cannot be annotated. -data class Nullable(val inner: Bound) : Bound() - -sealed class Variance : Projection() { +data class Nullable(val inner: Bound) : Bound(), WithSources { + override val sources: SourceSetDependent + get() = when (inner) { + is Nullable -> throw RuntimeException("Nullable Nullable is not a valid type") + is Dynamic -> throw RuntimeException("Nullable Dynamic is not a valid type") + is JavaObject -> inner.sources + is PrimitiveJavaType -> inner.sources + is TypeAliased -> inner.sources + is TypeConstructor -> inner.sources + is TypeParameter -> inner.sources + is UnresolvedBound -> inner.sources + is Void -> inner.sources + } +} + +sealed class Variance : Projection(), WithSources { abstract val inner: T + + override val sources: SourceSetDependent get() = when (inner) { + is WithSources -> (inner as WithSources).sources + is Dynamic -> TODO() + is Void -> TODO() + else -> throw RuntimeException("Impossible inner Bound for Variance: $inner") + } } data class Covariance(override val inner: T) : Variance() { @@ -475,15 +512,18 @@ data class Invariance(override val inner: T) : Variance() { override fun toString() = "" } +// Void cannot be an object because not all Void types are the same. For example, a Void declared in Java is of +// platform nullability, while a (non-wrapped) Void declared in Kotlin is non-null. +class Void(override val sources: SourceSetDependent) : Bound(), WithSources +// TODO: should these be WithSources? (that would require they be classes) +object Dynamic : Bound() object Star : Projection() -object Void : Bound() -object Dynamic : Bound() -fun Variance.withDri(dri: DRI) = when (this) { - 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)) +fun Variance.withDri(dri: DRI, sources: SourceSetDependent) = when (this) { + is Contravariance -> Contravariance(TypeParameter(dri, inner.name, inner.presentableName, sources)) + is Covariance -> Covariance(TypeParameter(dri, inner.name, inner.presentableName, sources)) + is Invariance -> Invariance(TypeParameter(dri, inner.name, inner.presentableName, sources)) } fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = @@ -512,6 +552,11 @@ fun SourceSetDependent?.orEmpty(): SourceSetDependent = this ?: emptyM interface DocumentableSource { val path: String + val language: Language } data class TypeConstructorWithKind(val typeConstructor: TypeConstructor, val kind: ClassKind) + +public fun WithSources.sourceLanguage(sourceSet: DokkaSourceSet) = sources[sourceSet]!!.language + +public val WithSources.sourceLanguage: Language get() = this.sources.entries.single().value.language diff --git a/core/src/main/kotlin/model/documentableProperties.kt b/core/src/main/kotlin/model/documentableProperties.kt index 87f40bd6ce..d77381d03d 100644 --- a/core/src/main/kotlin/model/documentableProperties.kt +++ b/core/src/main/kotlin/model/documentableProperties.kt @@ -46,3 +46,8 @@ data class CheckedExceptions(val exceptions: SourceSetDependent>) : Ex } override val key: ExtraProperty.Key = CheckedExceptions } + +enum class Language { + JAVA, KOTLIN, XML, JS, UNKNOWN +} + diff --git a/core/src/main/kotlin/model/documentableUtils.kt b/core/src/main/kotlin/model/documentableUtils.kt index e32605ca17..b3fdd97154 100644 --- a/core/src/main/kotlin/model/documentableUtils.kt +++ b/core/src/main/kotlin/model/documentableUtils.kt @@ -9,13 +9,15 @@ fun DTypeParameter.filter(filteredSet: Set) = if (filteredSet.containsAll(sourceSets)) this else { val intersection = filteredSet.intersect(sourceSets) + val filteredSources = sources.filtered(intersection) if (intersection.isEmpty()) null else DTypeParameter( - variantTypeParameter, - documentation.filtered(intersection), - expectPresentInSet?.takeIf { intersection.contains(expectPresentInSet) }, - bounds, - intersection, - extra + variantTypeParameter = variantTypeParameter, + documentation = documentation.filtered(intersection), + expectPresentInSet = expectPresentInSet?.takeIf { intersection.contains(expectPresentInSet) }, + bounds = bounds, + sources = filteredSources, + sourceSets = intersection, + extra = extra ) } diff --git a/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt index 21548a4cfa..a4358fe8c5 100644 --- a/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt +++ b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt @@ -34,3 +34,4 @@ fun PreMergeDocumentableTransformer.perPackageOptions(documentable: Documentable fun PreMergeDocumentableTransformer.source(documentable: T) where T : Documentable, T : WithSources = checkNotNull(documentable.sources[sourceSet(documentable)]) + { "Sources were null for ${sourceSet(documentable)} \nfor $documentable" } diff --git a/core/src/test/kotlin/model/DocumentableTest.kt b/core/src/test/kotlin/model/DocumentableTest.kt index 0652831c21..ebe7cac262 100644 --- a/core/src/test/kotlin/model/DocumentableTest.kt +++ b/core/src/test/kotlin/model/DocumentableTest.kt @@ -39,7 +39,7 @@ class DocumentableTest { modifier = emptyMap(), sources = emptyMap(), sourceSets = emptySet(), - type = Void, + type = Void(emptyMap()), receiver = null, isConstructor = false, isExpectActual = false, @@ -50,8 +50,9 @@ class DocumentableTest { documentation = emptyMap(), expectPresentInSet = null, extra = PropertyContainer.empty(), + sources = emptyMap(), sourceSets = emptySet(), - type = Void + type = Void(emptyMap()) ), DParameter( dri = DRI(), @@ -59,8 +60,9 @@ class DocumentableTest { documentation = emptyMap(), expectPresentInSet = null, extra = PropertyContainer.empty(), + sources = emptyMap(), sourceSets = emptySet(), - type = Void + type = Void(emptyMap()) ) ) ), @@ -75,7 +77,7 @@ class DocumentableTest { modifier = emptyMap(), sources = emptyMap(), sourceSets = emptySet(), - type = Void, + type = Void(emptyMap()), receiver = null, isConstructor = false, isExpectActual = false, @@ -87,7 +89,8 @@ class DocumentableTest { expectPresentInSet = null, extra = PropertyContainer.empty(), sourceSets = emptySet(), - type = Void + sources = emptyMap(), + type = Void(emptyMap()) ), DParameter( dri = DRI(), @@ -96,7 +99,8 @@ class DocumentableTest { expectPresentInSet = null, extra = PropertyContainer.empty(), sourceSets = emptySet(), - type = Void + sources = emptyMap(), + type = Void(emptyMap()) ) ) ) diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt index 0c55fed43e..bc3d7b95be 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt @@ -1,14 +1,152 @@ package org.jetbrains.dokka.analysis +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.psi.PsiFile import com.intellij.psi.PsiNamedElement import org.jetbrains.dokka.model.DocumentableSource -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.load.kotlin.toSourceElement +import org.jetbrains.dokka.model.Language +import org.jetbrains.kotlin.backend.jvm.FacadeClassSourceShimForFragmentCompilation +import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.SourceFile.NO_SOURCE_FILE +import org.jetbrains.kotlin.descriptors.runtime.components.ReflectAnnotationSource +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.kotlin.* +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import org.jetbrains.kotlin.resolve.source.PsiSourceFile +import org.jetbrains.kotlin.descriptors.SourceElement.NO_SOURCE +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.load.java.structure.JavaClass -class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : DocumentableSource { - override val path = descriptor.toSourceElement.containingFile.toString() +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor +import org.jetbrains.kotlin.serialization.js.KotlinJavascriptPackageFragment + +/** + * Logic tree for figuring out the source file of a Descriptor: + * 1. If the descriptor (or psi) has a non-null `containingFile` attribute, use that. + * 2. If the descriptor is or is in a synthetic method or property that overrides a real method or property + * (and thus the real method defines the API surface), use the overridden member's "real" source file + * 3. If the descriptor is contained within a class, recur onto the outermost class. + * (NOTE: this assumes that extension inner classes are not possible, which is a feature request) + * 4. If the descriptor or something in its containingDeclaration hierarchy is deserialized, return a .class file or a + * .jar file, which may be a LiteralSourceFile + * 5. Else create a LiteralSourceFile based on the package name of the class, with no full path. + */ +data class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : DocumentableSource { + val file = descriptor.file() + val psi = descriptor.findPsi() + override val path = file.toString() + override val language = + if (descriptor.containingHierarchyHasJavaClass() == (file.getSourceLanguage() == Language.JAVA)) + file.getSourceLanguage() + else throw RuntimeException("Two methods of finding source language do not agree: " + + "KSP is Java: ${descriptor.containingHierarchyHasJavaClass()}, " + + "while complex version is ${file.getSourceLanguage()}.") + override fun toString() = "DescriptorDocumentableSource(path = $path, element = $psi)" +} +/** The source file in which this declaration exists. Declarations can be contained inside others in the same file. */ +fun DeclarationDescriptor.file(): SourceFile = when { + // Most PSIs/Descriptors will use this case. This case only fails with enum entries, params/types, and a few more. + this.source.name != null -> this.toSourceElement.containingFile // source is not NO_SOURCE + // FAKE_OVERRIDE is used in autogenerated functions, like some equals methods. The upstream signature is retained. + (this is DeserializedSimpleFunctionDescriptor && this.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) -> + this.overriddenDescriptors.first().file() // TODO: is first() appropriate here? + (this is PropertyDescriptor && this.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) -> + this.overriddenDescriptors.first().file() + + //this is PackageFragmentDescriptor -> LiteralSourceFile(fqName.asString(), Language.JAVA) + this.toSourceElement.getSourceLanguage() != Language.UNKNOWN -> + LiteralSourceFile(getPackageName()!!, toSourceElement.getSourceLanguage()) + this.containingDeclaration != null -> this.containingDeclaration!!.file() + else -> NO_SOURCE.containingFile +} + +private fun DeclarationDescriptor?.containingHierarchyHasJavaClass(): Boolean = when(this) { + null -> false + is JavaClassDescriptor -> true + else -> this.containingDeclaration.containingHierarchyHasJavaClass() } +private fun DeclarationDescriptor.getPackageName() = toSourceElement.getPackageName(this) ?: + containingDeclaration!!.toSourceElement.getPackageName(containingDeclaration!!) + +private class LiteralSourceFile(val _name: String, val language: Language, val path: String? = null) : SourceFile { + override fun getName() = _name + override fun toString() = this._name +} + +private val DeclarationDescriptor.source: SourceFile get() = this.toSourceElement.containingFile + class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource { override val path = psi.containingFile.virtualFile.path + override val language: Language = psi.containingFile.getSourceLanguage() +} + +fun PsiFile.getSourceLanguage() = when (this.fileType) { + is KotlinFileType -> Language.KOTLIN + is JavaFileType, is JavaClassFileType -> Language.JAVA + else -> Language.UNKNOWN +} + +/** + * Returns the package of this (presumed) + * This function assumes that containingFile has already been tried and is NO_SOURCE + */ +private fun SourceElement.getPackageName(parent: DeclarationDescriptor): String? = + when { + parent is PackageFragmentDescriptor -> parent.fqName.asString() + //parent.containingDeclaration.toSourceElement.javaElement is BinaryJavaClass -> + else -> when(this) { + is KotlinJvmBinarySourceElement -> this.binaryClass.classId.asString() + is KotlinJvmBinaryPackageSourceElement -> + getContainingBinaryClass(parent as DeserializedMemberDescriptor)!!.classId.asString() + is KotlinSourceElement -> this.psi.name!! + is JavaSourceElement -> when (this.javaElement) { + is JavaClass -> (this.javaElement as JavaClass).fqName?.asString() + else -> this.javaElement.javaClass.canonicalName + } + is PsiSourceElement -> this.psi!!.text + is KotlinJavascriptPackageFragment.JsContainerSource -> this.javaClass.canonicalName + NO_SOURCE -> null + else -> throw RuntimeException("Could not get SourceElement PackageName for $this") + } +} + +private fun SourceElement.getSourceLanguage() = when (this) { + is KotlinSourceElement -> Language.KOTLIN + is KotlinJvmBinarySourceElement, is KotlinJvmBinaryPackageSourceElement -> Language.KOTLIN + // TODO: verify that RuntimeSourceElement : JavaSourceElement is actually always from Java source + is JavaSourceElement -> Language.JAVA + is KotlinJavascriptPackageFragment.JsContainerSource -> Language.JS + is PsiSourceElement -> containingFile.getSourceLanguage() + NO_SOURCE -> Language.UNKNOWN + is JvmPackagePartSource -> throw RuntimeException("Not yet handled case: JvmPackagePartSource") + is FacadeClassSourceShimForFragmentCompilation, is ReflectAnnotationSource -> + throw RuntimeException("Should not be able to get element of this type from real code: ${this::class.java}") + // This `when` is exhaustive over all SourceElement implementers in the compiler as of 1.7.0-RC + else -> + if((this::class.java).name == "XmlSourceElement") Language.XML // This class is private in the compiler + else throw RuntimeException("Unknown SourceElement type: ${this::class.java}") +} + +fun SourceFile.getSourceLanguage() = when (this) { + NO_SOURCE_FILE -> + throw RuntimeException("No source file present") // For testing + is LiteralSourceFile -> this.language + //is JavaFileType, is JavaClassFileType -> Language.JAVA + is PsiSourceFile -> { + when (this.psiFile) { + is KtFile -> Language.KOTLIN + else -> throw RuntimeException("Uknown file type: ${this.psiFile}") + } + } + else -> throw RuntimeException("Uknown file type: $this") } diff --git a/plugins/all-modules-page/out/index.md b/plugins/all-modules-page/out/index.md new file mode 100644 index 0000000000..5c9593f62f --- /dev/null +++ b/plugins/all-modules-page/out/index.md @@ -0,0 +1,10 @@ +/ + +# Sample project + +Sample documentation with [external link](https://www.google.pl) + +## All modules: + +| Name | +|---| diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt index 642c01c3b8..35efd620d6 100644 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt @@ -255,7 +255,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog * where `s` is both a function parameter and a property */ private fun DProperty.isAlsoParameter(sourceSet: DokkaSourceSet) = - (this.sources[sourceSet] as? DescriptorDocumentableSource)?.descriptor?.findPsi() is KtParameter + (this.sources[sourceSet] as? DescriptorDocumentableSource)?.psi is KtParameter private fun propertySignature(p: DProperty) = p.sourceSets.map { sourceSet -> @@ -382,7 +382,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog private fun signature(t: DTypeParameter) = t.sourceSets.map { contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) { - signatureForProjection(t.variantTypeParameter.withDri(t.dri.withTargetToDeclaration())) + signatureForProjection(t.variantTypeParameter.withDri(t.dri.withTargetToDeclaration(), t.sources)) list(t.nontrivialBounds, prefix = " : ", surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { bound -> signatureForProjection(bound) @@ -488,7 +488,8 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog private fun PrimitiveJavaType.translateToKotlin() = GenericTypeConstructor( dri = dri, projections = emptyList(), - presentableName = null + presentableName = null, + sources = sources ) private val DTypeParameter.nontrivialBounds: List diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt index 946887991b..89d4bddee8 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt @@ -217,7 +217,10 @@ class DocumentableVisibilityFilterTransformer(val context: DokkaContext) : PreMe } } - private fun filterEnumEntries(entries: List, filteredPlatforms: Set): Pair> = + private fun filterEnumEntries( + entries: List, + filteredPlatforms: Set, + ): Pair> = entries.fold(Pair(false, emptyList())) { acc, entry -> val intersection = filteredPlatforms.intersect(entry.sourceSets) if (intersection.isEmpty()) Pair(true, acc.second) @@ -227,15 +230,16 @@ class DocumentableVisibilityFilterTransformer(val context: DokkaContext) : PreMe val classlikes = filterClasslikes(entry.classlikes) { _, data -> data in intersection } DEnumEntry( - entry.dri, - entry.name, - entry.documentation.filtered(intersection), - entry.expectPresentInSet.filtered(filteredPlatforms), - functions.second, - properties.second, - classlikes.second, - intersection, - entry.extra + dri = entry.dri, + name = entry.name, + documentation = entry.documentation.filtered(intersection), + expectPresentInSet = entry.expectPresentInSet.filtered(filteredPlatforms), + functions = functions.second, + properties = properties.second, + classlikes = classlikes.second, + sources = entry.sources.filtered(filteredPlatforms), + sourceSets = intersection, + extra = entry.extra ).let { Pair(functions.first || properties.first || classlikes.first, acc.second + it) } } } @@ -363,8 +367,7 @@ class DocumentableVisibilityFilterTransformer(val context: DokkaContext) : PreMe private fun filterTypeAliases( typeAliases: List, additionalCondition: (DTypeAlias, DokkaSourceSet) -> Boolean = ::alwaysTrue - ) = - typeAliases.transform(additionalCondition) { original, filteredPlatforms -> + ) = typeAliases.transform(additionalCondition) { original, filteredPlatforms -> with(original) { copy( documentation = documentation.filtered(filteredPlatforms), @@ -372,6 +375,7 @@ class DocumentableVisibilityFilterTransformer(val context: DokkaContext) : PreMe underlyingType = underlyingType.filtered(filteredPlatforms), visibility = visibility.filtered(filteredPlatforms), generics = generics.mapNotNull { it.filter(filteredPlatforms) }, + sources = sources.filtered(filteredPlatforms), sourceSets = filteredPlatforms, ) } diff --git a/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt index 251422f4d3..c03e4adbb6 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt @@ -19,35 +19,63 @@ class KotlinArrayDocumentableReplacerTransformer(context: DokkaContext): when (this?.dri) { DRI("kotlin", "Int") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "IntArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources + ), true) DRI("kotlin", "Boolean") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "BooleanArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources + ), true) DRI("kotlin", "Float") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "FloatArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "FloatArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources + ), true) DRI("kotlin", "Double") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "DoubleArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "DoubleArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources + ), true) DRI("kotlin", "Long") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "LongArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "LongArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources), true) DRI("kotlin", "Short") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "ShortArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "ShortArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources), true) DRI("kotlin", "Char") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "CharArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "CharArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources), true) DRI("kotlin", "Byte") -> AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "ByteArray"), emptyList()), + GenericTypeConstructor( + dri = DRI("kotlin", "ByteArray"), + projections = emptyList(), + sources = genericTypeConstructor.sources), true) else -> null } diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index 85ec1d07ff..6147968288 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -7,10 +7,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.runBlocking import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.KotlinAnalysis -import org.jetbrains.dokka.analysis.from +import org.jetbrains.dokka.analysis.* import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.parsers.MarkdownParser import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser @@ -121,7 +118,7 @@ class DefaultDescriptorToDocumentableTranslator( data class DRIWithPlatformInfo( val dri: DRI, - val actual: SourceSetDependent + val actualSources: SourceSetDependent ) fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, emptyMap()) @@ -181,7 +178,7 @@ private class DokkaDescriptorVisitor( val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() + val info = descriptor.resolveClassDescriptionData(parent.actualSources) return coroutineScope { val descriptorsWithKind = scope.getDescriptorsWithKind() @@ -189,7 +186,9 @@ private class DokkaDescriptorVisitor( val functions = async { descriptorsWithKind.functions.visitFunctions() } val properties = async { descriptorsWithKind.properties.visitProperties() } val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } + val generics = async { descriptor.declaredTypeParameters.parallelMap { + it.toVariantTypeParameter(parent.actualSources) + } } DInterface( dri = driWithPlatform.dri, @@ -210,7 +209,7 @@ private class DokkaDescriptorVisitor( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()), - info.ancestry.exceptionInSupertypesOrNull() + info.ancestry.exceptionInSupertypesOrNull(), ) ) } @@ -221,7 +220,8 @@ private class DokkaDescriptorVisitor( val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() + val sources = descriptor.createSources() + val info = descriptor.resolveClassDescriptionData(sources) return coroutineScope { @@ -237,7 +237,7 @@ private class DokkaDescriptorVisitor( functions = functions.await(), properties = properties.await(), classlikes = classlikes.await(), - sources = descriptor.createSources(), + sources = sources, expectPresentInSet = sourceSet.takeIf { isExpect }, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), supertypes = info.supertypes.toSourceSetDependent(), @@ -248,7 +248,7 @@ private class DokkaDescriptorVisitor( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()), - info.ancestry.exceptionInSupertypesOrNull() + info.ancestry.exceptionInSupertypesOrNull(), ) ) } @@ -261,7 +261,8 @@ private class DokkaDescriptorVisitor( val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() + val sources = descriptor.createSources() + val info = descriptor.resolveClassDescriptionData(sources) return coroutineScope { val descriptorsWithKind = scope.getDescriptorsWithKind() @@ -281,7 +282,7 @@ private class DokkaDescriptorVisitor( functions = functions.await(), properties = properties.await(), classlikes = classlikes.await(), - sources = descriptor.createSources(), + sources = sources, expectPresentInSet = sourceSet.takeIf { isExpect }, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), supertypes = info.supertypes.toSourceSetDependent(), @@ -292,7 +293,7 @@ private class DokkaDescriptorVisitor( extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()) + ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()), ) ) } @@ -318,11 +319,12 @@ private class DokkaDescriptorVisitor( properties = properties.await(), classlikes = classlikes.await(), sourceSets = setOf(sourceSet), + sources = descriptor.createSources(), expectPresentInSet = sourceSet.takeIf { isExpect }, extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ConstructorValues(descriptor.getAppliedConstructorParameters().toSourceSetDependent()) + ConstructorValues(descriptor.getAppliedConstructorParameters().toSourceSetDependent()), ) ) } @@ -336,11 +338,14 @@ private class DokkaDescriptorVisitor( return coroutineScope { val descriptorsWithKind = scope.getDescriptorsWithKind() + val sources = descriptor.createSources() val functions = async { descriptorsWithKind.functions.visitFunctions() } val properties = async { descriptorsWithKind.properties.visitProperties() } val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } + val generics = async { descriptor.declaredTypeParameters.parallelMap { + it.toVariantTypeParameter(sources) + } } val constructors = async { descriptor.constructors.parallelMap { visitConstructorDescriptor(it, driWithPlatform) } } @@ -356,13 +361,13 @@ private class DokkaDescriptorVisitor( isExpectActual = (isExpect || isActual), extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ), companion = descriptor.companionObjectDescriptor?.let { objectDescriptor(it, driWithPlatform) }, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), generics = generics.await(), constructors = constructors.await(), - sources = descriptor.createSources() + sources = sources ) } @@ -372,10 +377,10 @@ private class DokkaDescriptorVisitor( private suspend fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClass { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope + val actualSources = descriptor.createSources() val isExpect = descriptor.isExpect val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() - val actual = descriptor.createSources() + val info = descriptor.resolveClassDescriptionData(actualSources) return coroutineScope { val descriptorsWithKind = scope.getDescriptorsWithKind() @@ -383,12 +388,14 @@ private class DokkaDescriptorVisitor( val functions = async { descriptorsWithKind.functions.visitFunctions() } val properties = async { descriptorsWithKind.properties.visitProperties() } val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } + val generics = async { + descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter(actualSources) } + } val constructors = async { descriptor.constructors.parallelMap { visitConstructorDescriptor( it, - if (it.isPrimary) DRIWithPlatformInfo(driWithPlatform.dri, actual) + if (it.isPrimary) DRIWithPlatformInfo(driWithPlatform.dri, actualSources) else DRIWithPlatformInfo(driWithPlatform.dri, emptyMap()) ) } @@ -401,7 +408,7 @@ private class DokkaDescriptorVisitor( functions = functions.await(), properties = properties.await(), classlikes = classlikes.await(), - sources = actual, + sources = actualSources, expectPresentInSet = sourceSet.takeIf { isExpect }, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), supertypes = info.supertypes.toSourceSetDependent(), @@ -415,7 +422,7 @@ private class DokkaDescriptorVisitor( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()), - info.ancestry.exceptionInSupertypesOrNull() + info.ancestry.exceptionInSupertypesOrNull(), ) ) } @@ -427,18 +434,18 @@ private class DokkaDescriptorVisitor( val isExpect = descriptor.isExpect val isActual = descriptor.isActual - val actual = originalDescriptor.createSources() + val actualSources = originalDescriptor.createSources() return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter(actualSources) } } DProperty( dri = dri, name = descriptor.name.asString(), receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actualSources)) }, - sources = actual, + sources = actualSources, getter = descriptor.accessors.filterIsInstance().singleOrNull()?.let { visitPropertyAccessorDescriptor(it, descriptor, dri) }, @@ -448,7 +455,7 @@ private class DokkaDescriptorVisitor( visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), documentation = descriptor.resolveDescriptorData(), modifier = descriptor.modifier().toSourceSetDependent(), - type = descriptor.returnType!!.toBound(), + type = descriptor.returnType!!.toBound(actualSources), expectPresentInSet = sourceSet.takeIf { isExpect }, sourceSets = setOf(sourceSet), generics = generics.await(), @@ -479,28 +486,28 @@ private class DokkaDescriptorVisitor( val isExpect = descriptor.isExpect val isActual = descriptor.isActual - val actual = originalDescriptor.createSources() + val actualSources = originalDescriptor.createSources() return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter(actualSources) } } DFunction( dri = dri, name = descriptor.name.asString(), isConstructor = false, receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actualSources)) }, parameters = descriptor.valueParameters.mapIndexed { index, desc -> - parameter(index, desc, DRIWithPlatformInfo(dri, actual)) + parameter(index, desc, DRIWithPlatformInfo(dri, actualSources)) }, expectPresentInSet = sourceSet.takeIf { isExpect }, - sources = actual, + sources = actualSources, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), generics = generics.await(), documentation = descriptor.takeIf { it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED } ?.resolveDescriptorData() ?: emptyMap(), modifier = descriptor.modifier().toSourceSetDependent(), - type = descriptor.returnType!!.toBound(), + type = descriptor.returnType!!.toBound(actualSources), sourceSets = setOf(sourceSet), isExpectActual = (isExpect || isActual), extra = PropertyContainer.withAll( @@ -517,24 +524,24 @@ private class DokkaDescriptorVisitor( suspend fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): DFunction { val name = descriptor.constructedClass.name.toString() val dri = parent.dri.copy(callable = Callable.from(descriptor, name)) - val actual = descriptor.createSources() + val actualSources = descriptor.createSources() val isExpect = descriptor.isExpect val isActual = descriptor.isActual return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter(actualSources) } } DFunction( dri = dri, name = name, isConstructor = true, receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actualSources)) }, parameters = descriptor.valueParameters.mapIndexed { index, desc -> - parameter(index, desc, DRIWithPlatformInfo(dri, actual)) + parameter(index, desc, DRIWithPlatformInfo(dri, actualSources)) }, - sources = actual, + sources = actualSources, expectPresentInSet = sourceSet.takeIf { isExpect }, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), documentation = descriptor.resolveDescriptorData().let { sourceSetDependent -> @@ -550,14 +557,14 @@ private class DokkaDescriptorVisitor( sourceSetDependent } }, - type = descriptor.returnType.toBound(), + type = descriptor.returnType.toBound(actualSources), modifier = descriptor.modifier().toSourceSetDependent(), generics = generics.await(), sourceSets = setOf(sourceSet), isExpectActual = (isExpect || isActual), extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ).let { if (descriptor.isPrimary) { it + PrimaryConstructorExtra @@ -573,10 +580,11 @@ private class DokkaDescriptorVisitor( ) = DParameter( dri = parent.dri.copy(target = PointingToDeclaration), name = null, - type = descriptor.type.toBound(), + type = descriptor.type.toBound(parent.actualSources), expectPresentInSet = null, documentation = descriptor.resolveDescriptorData(), sourceSets = setOf(sourceSet), + sources = parent.actualSources, extra = PropertyContainer.withAll(descriptor.getAnnotations().toSourceSetDependent().toAnnotations()) ) @@ -589,18 +597,20 @@ private class DokkaDescriptorVisitor( val isGetter = descriptor is PropertyGetterDescriptor val isExpect = descriptor.isExpect val isActual = descriptor.isActual + val actualSources = descriptor.createSources() suspend fun PropertyDescriptor.asParameter(parent: DRI) = DParameter( parent.copy(target = PointingToCallableParameters(parameterIndex = 1)), this.name.asString(), - type = this.type.toBound(), + type = this.type.toBound(actualSources), expectPresentInSet = sourceSet.takeIf { isExpect }, documentation = descriptor.resolveDescriptorData(), sourceSets = setOf(sourceSet), + sources = actualSources, extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations() + getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations(), ) ) @@ -634,7 +644,7 @@ private class DokkaDescriptorVisitor( } return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter(actualSources) } } DFunction( dri, @@ -643,7 +653,7 @@ private class DokkaDescriptorVisitor( parameters = parameters, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), documentation = descriptor.resolveDescriptorData(), - type = descriptor.returnType!!.toBound(), + type = descriptor.returnType!!.toBound(actualSources), generics = generics.await(), modifier = descriptor.modifier().toSourceSetDependent(), expectPresentInSet = sourceSet.takeIf { isExpect }, @@ -658,7 +668,7 @@ private class DokkaDescriptorVisitor( isExpectActual = (isExpect || isActual), extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ) ) } @@ -667,20 +677,25 @@ private class DokkaDescriptorVisitor( private suspend fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor) = with(descriptor) { coroutineScope { - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } - val info = buildAncestryInformation(defaultType).copy( - superclass = buildAncestryInformation(underlyingType), + val sources = descriptor.createSources() + val generics = async { + descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter(sources) } + } + val info = buildAncestryInformation(defaultType, sources).copy( + superclass = buildAncestryInformation(underlyingType, sources), interfaces = emptyList() ) DTypeAlias( dri = DRI.from(this@with), name = name.asString(), - type = defaultType.toBound(), + type = defaultType.toBound(sources), expectPresentInSet = null, - underlyingType = underlyingType.toBound().toSourceSetDependent(), + underlyingType = underlyingType.toBound(sources).toSourceSetDependent(), visibility = visibility.toDokkaVisibility().toSourceSetDependent(), documentation = resolveDescriptorData(), sourceSets = setOf(sourceSet), + // A TypeAlias in Kotlin of a Java type uses Kotlin properties like default nullability + sources = sources, generics = generics.await(), extra = PropertyContainer.withAll( descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), @@ -690,14 +705,19 @@ private class DokkaDescriptorVisitor( } } - private suspend fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRIWithPlatformInfo) = - DParameter( + private suspend fun parameter( + index: Int, + descriptor: ValueParameterDescriptor, + parent: DRIWithPlatformInfo + ) = DParameter( dri = parent.dri.copy(target = PointingToCallableParameters(index)), name = descriptor.name.asString(), - type = descriptor.varargElementType?.toBound() ?: descriptor.type.toBound(), + type = descriptor.varargElementType?.toBound(parent.actualSources) + ?: descriptor.type.toBound(parent.actualSources), expectPresentInSet = null, documentation = descriptor.resolveDescriptorData(), sourceSets = setOf(sourceSet), + sources = parent.actualSources, extra = PropertyContainer.withAll(listOfNotNull( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), @@ -760,15 +780,17 @@ private class DokkaDescriptorVisitor( getDocumentation()?.toSourceSetDependent() ?: emptyMap() - private suspend fun toTypeConstructor(kt: KotlinType) = + private suspend fun toTypeConstructor(kt: KotlinType, sources: SourceSetDependent) = GenericTypeConstructor( DRI.from(kt.constructor.declarationDescriptor as DeclarationDescriptor), - kt.arguments.map { it.toProjection() }, + kt.arguments.map { it.toProjection(sources) }, + sources = sources, extra = PropertyContainer.withAll(kt.getAnnotations().toSourceSetDependent().toAnnotations()) ) private suspend fun buildAncestryInformation( - kotlinType: KotlinType + kotlinType: KotlinType, + sources: SourceSetDependent ): AncestryNode { val (interfaces, superclass) = kotlinType.immediateSupertypes().filterNot { it.isAnyOrNullableAny() } .partition { @@ -780,35 +802,41 @@ private class DokkaDescriptorVisitor( return coroutineScope { AncestryNode( - typeConstructor = toTypeConstructor(kotlinType), - superclass = superclass.parallelMap(::buildAncestryInformation).singleOrNull(), - interfaces = interfaces.parallelMap(::buildAncestryInformation) + typeConstructor = toTypeConstructor(kotlinType, sources), + superclass = superclass.parallelMap { buildAncestryInformation(it, sources) }.singleOrNull(), + interfaces = interfaces.parallelMap { buildAncestryInformation(it, sources) } ) } } - private suspend fun ClassDescriptor.resolveClassDescriptionData(): ClassInfo { + private suspend fun ClassDescriptor.resolveClassDescriptionData(sources: SourceSetDependent): ClassInfo { return coroutineScope { ClassInfo( - buildAncestryInformation(this@resolveClassDescriptionData.defaultType), + buildAncestryInformation(this@resolveClassDescriptionData.defaultType, sources), resolveDescriptorData() ) } } - private suspend fun TypeParameterDescriptor.toVariantTypeParameter() = + private suspend fun TypeParameterDescriptor.toVariantTypeParameter(sources: SourceSetDependent) = DTypeParameter( variantTypeParameter( - TypeParameter(DRI.from(this), name.identifier, annotations.getPresentableName()) + TypeParameter( + DRI.from(this), + name.identifier, + annotations.getPresentableName(), + sources + ) ), resolveDescriptorData(), null, - upperBounds.map { it.toBound() }, + upperBounds.map { it.toBound(sources) }, + sources, setOf(sourceSet), extra = PropertyContainer.withAll( additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - getAnnotations().toSourceSetDependent().toAnnotations() + getAnnotations().toSourceSetDependent().toAnnotations(), ) ) @@ -816,8 +844,9 @@ private class DokkaDescriptorVisitor( mapNotNull { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name") .safeAs()?.value?.let { unquotedValue(it) } - private suspend fun KotlinType.toBound(): Bound { - suspend fun annotations(): PropertyContainer = + private suspend fun KotlinType.toBound(sources: SourceSetDependent): Bound { + + suspend fun extras(): PropertyContainer where T:AnnotationTarget = getAnnotations().takeIf { it.isNotEmpty() }?.let { annotations -> PropertyContainer.withAll(annotations.toSourceSetDependent().toAnnotations()) } ?: PropertyContainer.empty() @@ -825,30 +854,34 @@ private class DokkaDescriptorVisitor( return when (this) { is DynamicType -> Dynamic is AbbreviatedType -> TypeAliased( - abbreviation.toBound(), - expandedType.toBound(), - annotations() + typeAlias = abbreviation.toBound(sources), + inner = expandedType.toBound(sources), + sources = sources, + extra = extras() ) else -> when (val ctor = constructor.declarationDescriptor) { is TypeParameterDescriptor -> TypeParameter( dri = DRI.from(ctor), name = ctor.name.asString(), presentableName = annotations.getPresentableName(), - extra = annotations() + sources = sources, + extra = extras() ) is FunctionClassDescriptor -> FunctionalTypeConstructor( - DRI.from(ctor), - arguments.map { it.toProjection() }, + dri = DRI.from(ctor), + projections = arguments.map { it.toProjection(sources) }, isExtensionFunction = isExtensionFunctionType || isBuiltinExtensionFunctionalType, isSuspendable = isSuspendFunctionTypeOrSubtype, presentableName = annotations.getPresentableName(), - extra = annotations() + sources = sources, + extra = extras() ) else -> GenericTypeConstructor( - DRI.from(ctor!!), // TODO: remove '!!' - arguments.map { it.toProjection() }, - annotations.getPresentableName(), - extra = annotations() + dri = DRI.from(ctor!!), // TODO: remove '!!' + projections = arguments.map { it.toProjection(sources) }, + presentableName = annotations.getPresentableName(), + sources = sources, + extra = extras() ) }.let { if (isMarkedNullable) Nullable(it) else it @@ -856,11 +889,11 @@ private class DokkaDescriptorVisitor( } } - private suspend fun TypeProjection.toProjection(): Projection = - if (isStarProjection) Star else formPossiblyVariant() + private suspend fun TypeProjection.toProjection(sources: SourceSetDependent): Projection = + if (isStarProjection) Star else formPossiblyVariant(sources) - private suspend fun TypeProjection.formPossiblyVariant(): Projection = - type.toBound().wrapWithVariance(projectionKind) + private suspend fun TypeProjection.formPossiblyVariant(sources: SourceSetDependent) = + type.toBound(sources).wrapWithVariance(projectionKind) private fun TypeParameterDescriptor.variantTypeParameter(type: TypeParameter) = type.wrapWithVariance(variance) diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index c5136c27ca..83ec352dc7 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -704,8 +704,9 @@ open class DefaultPageCreator( ) { (documentable as? WithSources)?.documentableLanguage(sourceSet)?.let { when (it) { - DocumentableLanguage.KOTLIN -> firstParagraphComment(tag.root) - DocumentableLanguage.JAVA -> firstSentenceComment(tag.root) + Language.KOTLIN -> firstParagraphComment(tag.root) + Language.JAVA -> firstSentenceComment(tag.root) + Language.XML, Language.JS, Language.UNKNOWN -> firstParagraphComment(tag.root) } } ?: firstParagraphComment(tag.root) } diff --git a/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt b/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt index b3ce7c5c8a..c2962aac95 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt @@ -2,14 +2,11 @@ package org.jetbrains.dokka.base.translators.documentables import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.analysis.PsiDocumentableSource +import org.jetbrains.dokka.model.Language import org.jetbrains.dokka.model.WithSources -internal enum class DocumentableLanguage { - JAVA, KOTLIN -} - -internal fun WithSources.documentableLanguage(sourceSet: DokkaConfiguration.DokkaSourceSet): DocumentableLanguage = +internal fun WithSources.documentableLanguage(sourceSet: DokkaConfiguration.DokkaSourceSet): Language = when (sources[sourceSet]) { - is PsiDocumentableSource -> DocumentableLanguage.JAVA - else -> DocumentableLanguage.KOTLIN + is PsiDocumentableSource -> Language.JAVA + else -> Language.KOTLIN } \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index f91991541b..8a00d15104 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -12,10 +12,7 @@ import com.intellij.psi.impl.source.PsiImmediateClassType import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.KotlinAnalysis -import org.jetbrains.dokka.analysis.PsiDocumentableSource -import org.jetbrains.dokka.analysis.from +import org.jetbrains.dokka.analysis.* import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.translators.typeConstructorsBeingExceptions import org.jetbrains.dokka.base.translators.psi.parsers.JavaDocumentationParser @@ -23,9 +20,7 @@ import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser import org.jetbrains.dokka.base.translators.unquotedValue import org.jetbrains.dokka.links.* 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.DocumentationNode import org.jetbrains.dokka.model.doc.Param import org.jetbrains.dokka.model.properties.PropertyContainer @@ -169,6 +164,7 @@ class DefaultPsiToDocumentableTranslator( private suspend fun parseClasslike(psi: PsiClass, parent: DRI): DClasslike = coroutineScope { with(psi) { val dri = parent.withClass(name.toString()) + val sources = PsiDocumentableSource(this).toSourceSetDependent() val superMethodsKeys = hashSetOf() val superMethods = mutableListOf>() methods.asIterable().parallelForEach { superMethodsKeys.add(it.hash) } @@ -208,14 +204,18 @@ class DefaultPsiToDocumentableTranslator( return AncestryNode( typeConstructor = GenericTypeConstructor( - DRI.from(psiClass), - psiClass.typeParameters.map { typeParameter -> + dri = DRI.from(psiClass), + projections = psiClass.typeParameters.map { typeParameter -> TypeParameter( dri = DRI.from(typeParameter), name = typeParameter.name.orEmpty(), - extra = typeParameter.annotations() + sources = sources, + extra = PropertyContainer.withAll( + typeParameter.annotations(), + ) ) - } + }, + sources = sources ), superclass = classes.singleOrNull()?.first?.let(::traversePsiClassForAncestorsAndInheritedMembers), interfaces = interfaces.map { traversePsiClassForAncestorsAndInheritedMembers(it.first) } @@ -234,7 +234,6 @@ class DefaultPsiToDocumentableTranslator( ) else null } + superMethods.filter { it.first !in overridden }.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() val ancestors = (listOfNotNull(ancestry.superclass?.let { @@ -251,103 +250,104 @@ class DefaultPsiToDocumentableTranslator( when { isAnnotationType -> DAnnotation( - name.orEmpty(), - dri, - documentation, - null, - source, - allFunctions.await(), - fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, - classlikes.await(), - visibility, - null, - constructors.map { parseFunction(it, true) }, - mapTypeParameters(dri), - setOf(sourceSetData), - false, - PropertyContainer.withAll( + name = name.orEmpty(), + dri = dri, + documentation = documentation, + expectPresentInSet = null, + sources = sources, + functions = allFunctions.await(), + properties = fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes = classlikes.await(), + visibility = visibility, + companion = null, + constructors = constructors.map { parseFunction(it, true) }, + generics = mapTypeParameters(dri, sources), + sourceSets = setOf(sourceSetData), + isExpectActual = false, + extra = PropertyContainer.withAll( implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() .toAnnotations() ) ) isEnum -> DEnum( - dri, - name.orEmpty(), - fields.filterIsInstance().map { entry -> + dri = dri, + name = name.orEmpty(), + entries = fields.filterIsInstance().map { entry -> DEnumEntry( - dri.withClass(entry.name).withEnumEntryExtra(), - entry.name, - javadocParser.parseDocumentation(entry).toSourceSetDependent(), - null, - emptyList(), - emptyList(), - emptyList(), - setOf(sourceSetData), - PropertyContainer.withAll( + dri = dri.withClass(entry.name).withEnumEntryExtra(), + name = entry.name, + documentation = javadocParser.parseDocumentation(entry).toSourceSetDependent(), + expectPresentInSet = null, + functions = emptyList(), + properties = emptyList(), + classlikes = emptyList(), + sourceSets = setOf(sourceSetData), + sources = sources, + extra = PropertyContainer.withAll( implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() .toAnnotations() ) ) }, - documentation, - null, - source, - allFunctions.await(), - fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) }, - classlikes.await(), - visibility, - null, - constructors.map { parseFunction(it, true) }, - ancestors, - setOf(sourceSetData), - false, - PropertyContainer.withAll( + documentation = documentation, + expectPresentInSet = null, + sources = sources, + functions = allFunctions.await(), + properties = fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) }, + classlikes = classlikes.await(), + visibility = visibility, + companion = null, + constructors = constructors.map { parseFunction(it, true) }, + supertypes = ancestors, + sourceSets = setOf(sourceSetData), + isExpectActual = false, + extra = PropertyContainer.withAll( implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() .toAnnotations() ) ) isInterface -> DInterface( - dri, - name.orEmpty(), - documentation, - null, - source, - allFunctions.await(), - fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, - classlikes.await(), - visibility, - null, - mapTypeParameters(dri), - ancestors, - setOf(sourceSetData), - false, - PropertyContainer.withAll( + dri = dri, + name = name.orEmpty(), + documentation = documentation, + expectPresentInSet = null, + sources = sources, + functions = allFunctions.await(), + properties = fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes = classlikes.await(), + visibility = visibility, + companion = null, + generics = mapTypeParameters(dri, sources), + supertypes = ancestors, + sourceSets = setOf(sourceSetData), + isExpectActual = false, + extra = PropertyContainer.withAll( implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() .toAnnotations() ) ) else -> DClass( - dri, - name.orEmpty(), - constructors.map { parseFunction(it, true) }, - allFunctions.await(), - fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, - classlikes.await(), - source, - visibility, - null, - mapTypeParameters(dri), - ancestors, - documentation, - null, - modifiers, - setOf(sourceSetData), - false, - PropertyContainer.withAll( + dri = dri, + name = name.orEmpty(), + constructors = constructors.map { parseFunction(it, true) }, + functions = allFunctions.await(), + properties = fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes = classlikes.await(), + sources = sources, + visibility = visibility, + companion = null, + generics = mapTypeParameters(dri, sources), + supertypes = ancestors, + documentation = documentation, + expectPresentInSet = null, + modifier = modifiers, + sourceSets = setOf(sourceSetData), + isExpectActual = false, + extra = PropertyContainer.withAll( implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() .toAnnotations(), @@ -371,39 +371,41 @@ class DefaultPsiToDocumentableTranslator( DRI.from(psi).copy(packageName = dri.packageName, classNames = dri.classNames) } ?: DRI.from(psi) val docs = javadocParser.parseDocumentation(psi) + val sources = PsiDocumentableSource(psi).toSourceSetDependent() return DFunction( - dri, - psi.name, - isConstructor, - psi.parameterList.parameters.map { psiParameter -> + dri = dri, + name = psi.name, + isConstructor = isConstructor, + parameters = psi.parameterList.parameters.map { psiParameter -> DParameter( - dri.copy(target = dri.target.nextTarget()), - psiParameter.name, - DocumentationNode( + dri = dri.copy(target = dri.target.nextTarget()), + name = psiParameter.name, + documentation = DocumentationNode( listOfNotNull(docs.firstChildOfTypeOrNull { it.name == psiParameter.name }) ).toSourceSetDependent(), - null, - getBound(psiParameter.type), - setOf(sourceSetData), - PropertyContainer.withAll( + expectPresentInSet = null, + type = getBound(psiParameter.type, sources), + sources = sources, + sourceSets = setOf(sourceSetData), + extra = PropertyContainer.withAll( psiParameter.annotations.toList().toListOfAnnotations().toSourceSetDependent() .toAnnotations() ) ) }, - docs.toSourceSetDependent(), - null, - PsiDocumentableSource(psi).toSourceSetDependent(), - psi.getVisibility().toSourceSetDependent(), - psi.returnType?.let { getBound(type = it) } ?: Void, - psi.mapTypeParameters(dri), - null, - psi.getModifier().toSourceSetDependent(), - setOf(sourceSetData), - false, - psi.additionalExtras().let { + documentation = docs.toSourceSetDependent(), + expectPresentInSet = null, + sources = PsiDocumentableSource(psi).toSourceSetDependent(), + visibility = psi.getVisibility().toSourceSetDependent(), + type = psi.returnType?.let { getBound(type = it, sources) } ?: Void(sources), + generics = psi.mapTypeParameters(dri, sources), + receiver = null, + modifier = psi.getModifier().toSourceSetDependent(), + sourceSets = setOf(sourceSetData), + isExpectActual = false, + extra = psi.additionalExtras().let { PropertyContainer.withAll( InheritedMember(inheritedFrom.toSourceSetDependent()), it.toSourceSetDependent().toAdditionalModifiers(), @@ -437,50 +439,62 @@ class DefaultPsiToDocumentableTranslator( Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap()) } - private fun PsiTypeParameter.annotations(): PropertyContainer = this.annotations.toList().toListOfAnnotations().annotations() - private fun PsiType.annotations(): PropertyContainer = this.annotations.toList().toListOfAnnotations().annotations() - - private fun List.annotations(): PropertyContainer = - this.takeIf { it.isNotEmpty() }?.let { annotations -> - PropertyContainer.withAll(annotations.toSourceSetDependent().toAnnotations()) - } ?: PropertyContainer.empty() + private fun PsiTypeParameter.annotations() = + this.annotations.toList().toListOfAnnotations().toSourceSetDependent().toAnnotations() + private fun PsiType.annotations() = + this.annotations.toList().toListOfAnnotations().toSourceSetDependent().toAnnotations() - private fun getBound(type: PsiType): Bound { - fun bound() = when (type) { + private fun getBound(type: PsiType, sources: SourceSetDependent): Bound { + fun bound(sources: SourceSetDependent): Bound = when (type) { is PsiClassReferenceType -> type.resolve()?.let { resolved -> when { - resolved.qualifiedName == "java.lang.Object" -> JavaObject(type.annotations()) + resolved.qualifiedName == "java.lang.Object" -> JavaObject( + sources = sources, + extra = PropertyContainer.withAll(type.annotations()) + ) resolved is PsiTypeParameter -> { TypeParameter( dri = DRI.from(resolved), name = resolved.name.orEmpty(), - extra = type.annotations() + sources = sources, + extra = PropertyContainer.withAll(type.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) }, - extra = type.annotations() + dri = DRI.from(resolved), + projections = type.parameters.map { getProjection(it, sources) }, + sources = sources, + extra = PropertyContainer.withAll(type.annotations()) ) else -> GenericTypeConstructor( - DRI.from(resolved), - type.parameters.map { getProjection(it) }, - extra = type.annotations() + dri = DRI.from(resolved), + projections = type.parameters.map { getProjection(it, sources) }, + sources = sources, + extra = PropertyContainer.withAll(type.annotations()) ) } - } ?: UnresolvedBound(type.presentableText, type.annotations()) + } ?: UnresolvedBound( + name = type.presentableText, + sources = sources, + extra = PropertyContainer.withAll(type.annotations()) + ) is PsiArrayType -> GenericTypeConstructor( - DRI("kotlin", "Array"), - listOf(getProjection(type.componentType)), - extra = type.annotations() + dri = DRI("kotlin", "Array"), + projections = listOf(getProjection(type.componentType, sources)), + sources = sources, + extra = PropertyContainer.withAll(type.annotations()) ) - is PsiPrimitiveType -> if (type.name == "void") Void - else PrimitiveJavaType(type.name, type.annotations()) - is PsiImmediateClassType -> JavaObject(type.annotations()) + is PsiPrimitiveType -> if (type.name == "void") Void(sources) + else PrimitiveJavaType( + name = type.name, + sources = sources, + extra = PropertyContainer.withAll(type.annotations()) + ) + is PsiImmediateClassType -> JavaObject(sources, PropertyContainer.withAll(type.annotations())) else -> throw IllegalStateException("${type.presentableText} is not supported by PSI parser") } @@ -488,24 +502,24 @@ class DefaultPsiToDocumentableTranslator( //but if this is the case, we treat them as 'one of' return if (type.annotations.toList().toListOfAnnotations().isEmpty()) { cachedBounds.getOrPut(type.canonicalText) { - bound() + bound(sources) } } else { - bound() + bound(sources) } } - private fun getVariance(type: PsiWildcardType): Projection = when { - type.extendsBound != PsiType.NULL -> Covariance(getBound(type.extendsBound)) - type.superBound != PsiType.NULL -> Contravariance(getBound(type.superBound)) + private fun getVariance(type: PsiWildcardType, sources: SourceSetDependent) = when { + type.extendsBound != PsiType.NULL -> Covariance(getBound(type.extendsBound, sources)) + type.superBound != PsiType.NULL -> Contravariance(getBound(type.superBound, sources)) else -> throw IllegalStateException("${type.presentableText} has incorrect bounds") } - private fun getProjection(type: PsiType): Projection = when (type) { + private fun getProjection(type: PsiType, sources: SourceSetDependent): Projection = when (type) { is PsiEllipsisType -> Star - is PsiWildcardType -> getVariance(type) - else -> getBound(type) + is PsiWildcardType -> getVariance(type, sources) + else -> getBound(type, sources) } private fun PsiModifierListOwner.getModifier() = when { @@ -514,21 +528,25 @@ class DefaultPsiToDocumentableTranslator( else -> JavaModifier.Empty } - private fun PsiTypeParameterListOwner.mapTypeParameters(dri: DRI): List { + private fun PsiTypeParameterListOwner.mapTypeParameters( + dri: DRI, + sources: SourceSetDependent + ): List { fun mapBounds(bounds: Array): List = if (bounds.isEmpty()) emptyList() else bounds.mapNotNull { - (it as? PsiClassType)?.let { classType -> Nullable(getBound(classType)) } + (it as? PsiClassType)?.let { classType -> Nullable(getBound(classType, sources)) } } return typeParameters.map { type -> DTypeParameter( - dri.copy(target = dri.target.nextTarget()), - type.name.orEmpty(), - null, - javadocParser.parseDocumentation(type).toSourceSetDependent(), - null, - mapBounds(type.bounds), - setOf(sourceSetData), - PropertyContainer.withAll( + dri = dri.copy(target = dri.target.nextTarget()), + name = type.name.orEmpty(), + presentableName = null, + documentation = javadocParser.parseDocumentation(type).toSourceSetDependent(), + expectPresentInSet = null, + bounds = mapBounds(type.bounds), + sources = sources, + sourceSets = setOf(sourceSetData), + extra = PropertyContainer.withAll( type.annotations.toList().toListOfAnnotations().toSourceSetDependent() .toAnnotations() ) @@ -562,22 +580,23 @@ class DefaultPsiToDocumentableTranslator( private fun parseField(psi: PsiField, accessors: List): DProperty { val dri = DRI.from(psi) + val sources = PsiDocumentableSource(psi).toSourceSetDependent() return DProperty( - dri, - psi.name, - javadocParser.parseDocumentation(psi).toSourceSetDependent(), - null, - PsiDocumentableSource(psi).toSourceSetDependent(), - psi.getVisibility().toSourceSetDependent(), - getBound(psi.type), - null, - accessors.firstOrNull { it.hasParameters() }?.let { parseFunction(it) }, - accessors.firstOrNull { it.returnType == psi.type }?.let { parseFunction(it) }, - psi.getModifier().toSourceSetDependent(), - setOf(sourceSetData), - emptyList(), - false, - psi.additionalExtras().let { + dri = dri, + name = psi.name, + documentation = javadocParser.parseDocumentation(psi).toSourceSetDependent(), + expectPresentInSet = null, + sources = sources, + visibility = psi.getVisibility().toSourceSetDependent(), + type = getBound(psi.type, sources), + receiver = null, + setter = accessors.firstOrNull { it.hasParameters() }?.let { parseFunction(it) }, + getter = accessors.firstOrNull { it.returnType == psi.type }?.let { parseFunction(it) }, + modifier = psi.getModifier().toSourceSetDependent(), + sourceSets = setOf(sourceSetData), + generics = emptyList(), + isExpectActual = false, + extra = psi.additionalExtras().let { PropertyContainer.withAll( it.toSourceSetDependent().toAdditionalModifiers(), (psi.annotations.toList() diff --git a/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt index b9b1dc1eb5..464b28f79a 100644 --- a/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt +++ b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt @@ -6,6 +6,8 @@ import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import kotlin.test.assertTrue +import kotlin.test.asserter class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { private val configuration = dokkaConfiguration { @@ -41,7 +43,7 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { Assertions.assertEquals(typeArrayNames.size, params?.size) params?.forEachIndexed{ i, param -> - Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", typeArrayNames[i]), emptyList()), + assertEqualsWithoutSources(GenericTypeConstructor(DRI("kotlin", typeArrayNames[i]), emptyList()), param.type) } } @@ -63,13 +65,13 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { ) { preMergeDocumentablesTransformationStage = { val params = it.firstOrNull()?.packages?.firstOrNull()?.functions?.firstOrNull()?.parameters - Assertions.assertEquals( + assertEqualsWithoutSources( Invariance(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList())), (params?.firstOrNull()?.type as? GenericTypeConstructor)?.projections?.firstOrNull()) - Assertions.assertEquals( + assertEqualsWithoutSources( Invariance(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList())), (params?.get(1)?.type as? FunctionalTypeConstructor)?.projections?.get(0)) - Assertions.assertEquals( + assertEqualsWithoutSources( Invariance(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList())), (params?.get(1)?.type as? FunctionalTypeConstructor)?.projections?.get(1)) } @@ -98,11 +100,11 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { val myTestClass = it.firstOrNull()?.packages?.firstOrNull()?.classlikes?.firstOrNull() val property = myTestClass?.properties?.firstOrNull() - Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + assertEqualsWithoutSources(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), property?.type) - Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + assertEqualsWithoutSources(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), property?.getter?.type) - Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + assertEqualsWithoutSources(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), property?.setter?.parameters?.firstOrNull()?.type) } } @@ -123,7 +125,7 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { preMergeDocumentablesTransformationStage = { val arrTypealias = it.firstOrNull()?.packages?.firstOrNull()?.typealiases?.firstOrNull() - Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), + assertEqualsWithoutSources(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), arrTypealias?.underlyingType?.firstOrNull()?.value) } } @@ -146,9 +148,9 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { val testFun = it.firstOrNull()?.packages?.firstOrNull()?.functions?.firstOrNull() val myTestClass = it.firstOrNull()?.packages?.firstOrNull()?.classlikes?.firstOrNull() as? DClass - Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin","IntArray"), emptyList()), + assertEqualsWithoutSources(GenericTypeConstructor(DRI("kotlin","IntArray"), emptyList()), testFun?.generics?.firstOrNull()?.bounds?.firstOrNull()) - Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin","IntArray"), emptyList()), + assertEqualsWithoutSources(GenericTypeConstructor(DRI("kotlin","IntArray"), emptyList()), myTestClass?.generics?.firstOrNull()?.bounds?.firstOrNull()) } } @@ -185,15 +187,41 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { ) { preMergeDocumentablesTransformationStage = { val paramsJS = it[1].packages.firstOrNull()?.functions?.firstOrNull()?.parameters - Assertions.assertNotEquals( + assertNotEqualsWithoutSources( GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), paramsJS?.firstOrNull()?.type) val paramsJVM = it.firstOrNull()?.packages?.firstOrNull()?.functions?.firstOrNull()?.parameters - Assertions.assertEquals( + assertEqualsWithoutSources( GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), paramsJVM?.firstOrNull()?.type) } } } + /** Fake extension constructor to simplify code */ + private fun GenericTypeConstructor(dri: DRI, projections: List): GenericTypeConstructor = + GenericTypeConstructor(dri, projections, sources = emptyMap()) + /** + * Testing equality for DocumentableSources is difficult and rarely useful. However, the change that added this + * involved changes in KotlinArrayDocumentableReplacer's treatment of sources, so some tests are present. + */ + private fun assertEqualsWithoutSources(bound: Bound, other: Bound?) = + Assertions.assertTrue(other!!.equalsWithoutSources(bound)) + .also { assertContains(other.sources.values.single().path, "Test", "kt") } + private fun assertEqualsWithoutSources(vari: Variance<*>, other: Projection?) = + Assertions.assertEquals(vari::class, other!!::class).also { + Assertions.assertTrue((other as Variance<*>).inner.equalsWithoutSources(vari.inner)) } + .also { assertContains((other as WithSources).sources.values.single().path, "Test", "kt") } + private fun assertNotEqualsWithoutSources(bound: Bound, other: Bound?) = + Assertions.assertFalse(other!!.equalsWithoutSources(bound)) + .also { assertContains(other.sources.values.single().path, "Test", "kt") } + private fun Bound.equalsWithoutSources(other: Bound): Boolean { + if (this !is GenericTypeConstructor || other !is GenericTypeConstructor) return false + return this.dri == other.dri && this.projections == other.projections + } + /** Source duplicated from kotlin.test as that kotlin.test.assertContains would not resolve. */ + private fun assertContains(charSequence: String, vararg other: String) = asserter.assertTrue( + { "Expected the string to contain the substring(s).\nString <$charSequence>, substring(s) <$other>." }, + other.all { charSequence.contains(it, ignoreCase = false) } + ) } \ No newline at end of file diff --git a/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt b/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt index f95d9860ed..926e27d4f3 100644 --- a/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt +++ b/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt @@ -3,7 +3,7 @@ package linking import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRIExtraContainer import org.jetbrains.dokka.links.EnumEntryDRIExtra -import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.DocumentationLink import org.jetbrains.dokka.pages.ContentDRILink import org.jetbrains.dokka.pages.ContentPage @@ -37,7 +37,8 @@ class EnumValuesLinkingTest : BaseAbstractTest() { val classlikes = it.packages.single().children assertEquals(4, classlikes.size) - val javaLinker = classlikes.single { it.name == "JavaLinker" } + val javaLinker = classlikes.single { it.name == "JavaLinker" } as DClass + assertEquals(javaLinker.sourceLanguage, Language.JAVA) javaLinker.documentation.values.single().children.run { when (val kotlinLink = this[0].children[1].children[1]) { is DocumentationLink -> kotlinLink.dri.run { @@ -58,7 +59,8 @@ class EnumValuesLinkingTest : BaseAbstractTest() { } } - val kotlinLinker = classlikes.single { it.name == "KotlinLinker" } + val kotlinLinker = classlikes.single { it.name == "KotlinLinker" } as DClass + assertEquals(kotlinLinker.sourceLanguage, Language.KOTLIN) kotlinLinker.documentation.values.single().children.run { when (val kotlinLink = this[0].children[0].children[5]) { is DocumentationLink -> kotlinLink.dri.run { @@ -79,6 +81,11 @@ class EnumValuesLinkingTest : BaseAbstractTest() { } } + val kotlinEnum = classlikes.single { it.name == "KotlinEnum" } as DEnum + assertEquals(kotlinEnum.sourceLanguage, Language.KOTLIN) + val javaEnum = classlikes.single { it.name == "JavaEnum" } as DEnum + assertEquals(javaEnum.sourceLanguage, Language.JAVA) + assertEquals( javaLinker.documentation.values.single().children[0].children[1].children[1].safeAs()?.dri, kotlinLinker.documentation.values.single().children[0].children[0].children[5].safeAs()?.dri diff --git a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt index 79e9f54867..2d408f7ba0 100644 --- a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt +++ b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt @@ -699,6 +699,7 @@ class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() { Annotations.Annotation(DRI("sample", "Hello"), emptyMap()), type.extra[Annotations]?.directAnnotations?.values?.single()?.single() ) + assertEquals(type.sourceLanguage, Language.KOTLIN) } } } @@ -727,6 +728,7 @@ class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() { type.extra[Annotations]?.directAnnotations?.values?.single()?.single() ) assertEquals("kotlin/Int///PointingToDeclaration/", type.dri.toString()) + assertEquals(type.sourceLanguage, Language.KOTLIN) } } } diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt index 1213727d0b..f97c32ba35 100644 --- a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt +++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt @@ -3,10 +3,8 @@ package translators import org.jetbrains.dokka.base.DokkaBase 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.TypeConstructor +import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.Text -import org.jetbrains.dokka.model.firstMemberOfType import org.jetbrains.dokka.plugability.DokkaPlugin import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -241,12 +239,17 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { |} """.trimMargin(), configurationWithNoJVM, - pluginOverrides = listOf(OnlyPsiPlugin()) // suppress a descriptor translator because of psi and descriptor translators work in parallel + //pluginOverrides = listOf(OnlyPsiPlugin()) // suppress a descriptor translator because of psi and descriptor translators work in parallel ) { documentablesMergingStage = { module -> - val kotlinSubclassFunction = - module.packages.single().classlikes.find { it.name == "JavaLeafClass" }?.functions?.find { it.name == "kotlinSubclassFunction" } - .assertNotNull("kotlinSubclassFunction ") + val javaLeafClass = module.packages.single().classlikes.find { it.name == "JavaLeafClass" }!! as DClass + val kotlinSubClass = module.packages.single().classlikes.find { it.name == "KotlinSubClass" }!! as DClass + val kotlinLeafClassFunction = javaLeafClass.functions.find { it.name == "kotlinSubclassFunction" } + .assertNotNull("kotlinSubclassFunction") + val kotlinSubclassFunction = kotlinSubClass.functions.find { it.name == "kotlinSubclassFunction" } + .assertNotNull("kotlinSubclassFunction") + val javaLeafClassFunction = javaLeafClass.functions.find { it.name == "javaLeafClassFunction" } + .assertNotNull("javaLeafClassFunction") assertEquals( "String", @@ -256,6 +259,13 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { "String", (kotlinSubclassFunction.parameters.firstOrNull()?.type as? TypeConstructor)?.dri?.classNames ) + assertEquals(javaLeafClass.sourceLanguage, Language.JAVA) + assertEquals(kotlinSubClass.sourceLanguage, Language.KOTLIN) + assertEquals(kotlinSubclassFunction.sourceLanguage, Language.KOTLIN) + // This function was defined in Kotlin and inherited by a Java class without overriding. + // Its param types, if no nullability information is present, default to non-null instead of platform. + assertEquals(kotlinLeafClassFunction.sourceLanguage, Language.KOTLIN) + assertEquals(javaLeafClassFunction.sourceLanguage, Language.JAVA) } } } diff --git a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt index 011ae72974..4a82288827 100644 --- a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt +++ b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt @@ -7,9 +7,11 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider import org.jetbrains.dokka.model.DClass import org.jetbrains.dokka.model.DInterface +import org.jetbrains.dokka.model.Language import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.utilities.cast +import org.jsoup.nodes.Element import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -21,7 +23,10 @@ class ExternalDocumentablesTest : BaseAbstractTest() { sourceSet { sourceRoots = listOf("src") analysisPlatform = "jvm" - classpath += jvmStdlibPath!! + classpath += listOf(jvmStdlibPath!!, + PathManager.getResourceRoot(Element::class.java, "/org/jsoup/nodes/Element.class")!! + .replaceAfter(".jar", "")) + } } } @@ -29,8 +34,11 @@ class ExternalDocumentablesTest : BaseAbstractTest() { testInline( """ /src/com/sample/MyList.kt + import org.jsoup.nodes.Element package com.sample class MyList: ArrayList() + + class Foo: org.jsoup.nodes.Element("foo") """.trimIndent(), configuration ) { @@ -38,20 +46,42 @@ class ExternalDocumentablesTest : BaseAbstractTest() { pluginsSetupStage = { provider = it.plugin().querySingle { externalDocumentablesProvider } } + val jfoo = org.jsoup.nodes.Element("jfoo") + println(jfoo::class.java) documentablesTransformationStage = { mod -> - val entry = mod.packages.single().classlikes.single().cast().supertypes.entries.single() - val res = provider.findClasslike( - entry.value.single().typeConstructor.dri, - entry.key) - assertEquals("ArrayList", res?.name) - assertEquals("java.util/ArrayList///PointingToDeclaration/", res?.dri?.toString()) + val listEntry = mod.packages.single().classlikes.single { it.name == "MyList" } + .cast().supertypes.entries.single() + val listRes = provider.findClasslike( + listEntry.value.single().typeConstructor.dri, + listEntry.key)!! + assertEquals("ArrayList", listRes.name) + assertEquals("java.util/ArrayList///PointingToDeclaration/", listRes.dri.toString()) - val supertypes = res?.cast()?.supertypes?.values?.single() - ?.map { it.typeConstructor.dri.classNames } + val listSupertypes = listRes.cast().supertypes.values.single() + .map { it.typeConstructor.dri.classNames } assertEquals( listOf("AbstractList", "RandomAccess", "Cloneable", "Serializable", "MutableList"), - supertypes + listSupertypes + ) + assertEquals(Language.JAVA, listRes.sources.values.single().language) + assertEquals("java.util", listRes.sources.values.single().path) + + val jsoupEntry = mod.packages.single().classlikes.single { it.name == "Foo" } + .cast().supertypes.entries.single() + val jsoupRes = provider.findClasslike( + jsoupEntry.value.single().typeConstructor.dri, + jsoupEntry.key)!! + assertEquals("Element", jsoupRes.name) + assertEquals("org.jsoup.nodes/Element///PointingToDeclaration/", jsoupRes.dri.toString()) + + val jsoupSupertypes = jsoupRes.cast().supertypes.values.single() + .map { it.typeConstructor.dri.classNames } + assertEquals( + listOf("Node"), + jsoupSupertypes ) + assertEquals(Language.JAVA, jsoupRes.sources.values.single().language) + assertEquals("org.jsoup.nodes", jsoupRes.sources.values.single().path) } } } @@ -98,6 +128,8 @@ class ExternalDocumentablesTest : BaseAbstractTest() { listOf("CoroutineContext.Element"), supertypes ) + assertEquals(Language.KOTLIN, res!!.sources.values.single().language) + assertEquals("KotlinBuiltins.kt", res.sources.values.single().path) } } } @@ -133,7 +165,9 @@ class ExternalDocumentablesTest : BaseAbstractTest() { entry.key) assertEquals("Entry", res?.name) assertEquals("kotlin.collections/Map.Entry///PointingToDeclaration/", res?.dri?.toString()) + assertEquals(Language.KOTLIN, res!!.sources.values.single().language) + assertEquals("KotlinBuiltins.kt", res.sources.values.single().path) } } } -} \ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/translators/SourcesTest.kt b/plugins/base/src/test/kotlin/translators/SourcesTest.kt new file mode 100644 index 0000000000..88ddac8e32 --- /dev/null +++ b/plugins/base/src/test/kotlin/translators/SourcesTest.kt @@ -0,0 +1,210 @@ +package translators + +import com.intellij.openapi.application.PathManager +import kotlinx.coroutines.Job +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider +import org.jetbrains.dokka.model.DClass +import org.jetbrains.dokka.model.DInterface +import org.jetbrains.dokka.model.Language +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.utilities.cast +import org.jsoup.nodes.Element +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class SourcesTest : BaseAbstractTest() { + @Test + fun `external documentable from java stdlib`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + analysisPlatform = "jvm" + classpath += listOf(jvmStdlibPath!!, + PathManager.getResourceRoot(Element::class.java, "/org/jsoup/nodes/Element.class")!! + .replaceAfter(".jar", ""), + PathManager.getResourceRoot(Language::class.java, "/com/fasterxml/jackson/module/kotlin/KotlinModule.class")!! + .replaceAfter(".jar", "") + ) + + } + } + } + + testInline( + """ + /src/com/sample/MyList.kt + import org.jsoup.nodes.Element + import com.fasterxml.jackson.module.kotlin.KotlinModule + package com.sample + class MyList: ArrayList() + + class Jextern: org.jsoup.nodes.Element("Jextern") + + class Kstd: IntArray() + + class Kextern: com.fasterxml.jackson.module.kotlin.KotlinModule + """.trimIndent(), + configuration + ) { + lateinit var provider: ExternalDocumentablesProvider + pluginsSetupStage = { + provider = it.plugin().querySingle { externalDocumentablesProvider } + } + + documentablesTransformationStage = { mod -> + val listEntry = mod.packages.single().classlikes.single { it.name == "MyList" } + .cast().supertypes.entries.single() + val listRes = provider.findClasslike( + listEntry.value.single().typeConstructor.dri, + listEntry.key)!! + assertEquals("ArrayList", listRes.name) + assertEquals("java.util/ArrayList///PointingToDeclaration/", listRes.dri.toString()) + + val listSupertypes = listRes.cast().supertypes.values.single() + .map { it.typeConstructor.dri.classNames } + assertEquals( + listOf("AbstractList", "RandomAccess", "Cloneable", "Serializable", "MutableList"), + listSupertypes + ) + assertEquals(Language.JAVA, listRes.sources.values.single().language) + assertEquals("java.util", listRes.sources.values.single().path) + + val jexternEntry = mod.packages.single().classlikes.single { it.name == "Jextern" } + .cast().supertypes.entries.single() + val jexternRes = provider.findClasslike( + jexternEntry.value.single().typeConstructor.dri, + jexternEntry.key)!! + assertEquals("Element", jexternRes.name) + assertEquals("org.jsoup.nodes/Element///PointingToDeclaration/", jexternRes.dri.toString()) + + val jexternSupertypes = jexternRes.cast().supertypes.values.single() + .map { it.typeConstructor.dri.classNames } + assertEquals( + listOf("Node"), + jexternSupertypes + ) + assertEquals(Language.JAVA, jexternRes.sources.values.single().language) + assertEquals("org.jsoup.nodes", jexternRes.sources.values.single().path) + + val kstdEntry = mod.packages.single().classlikes.single { it.name == "Kstd" } + .cast().supertypes.entries.single() + val kstdRes = provider.findClasslike( + kstdEntry.value.single().typeConstructor.dri, + kstdEntry.key)!! + assertEquals("IntArray", kstdRes.name) + assertEquals("kotlin/IntArray///PointingToDeclaration/", kstdRes.dri.toString()) + + val kstdSupertypes = kstdRes.cast().supertypes.values.single() + .map { it.typeConstructor.dri.classNames } + assertEquals( + listOf("Cloneable", "Serializable"), + kstdSupertypes + ) + assertEquals(Language.KOTLIN, kstdRes.sources.values.single().language) + assertEquals("KotlinBuiltins.kt", kstdRes.sources.values.single().path) + + val kexternEntry = mod.packages.single().classlikes.single { it.name == "Kextern" } + .cast().supertypes.entries.single() + val kexternRes = provider.findClasslike( + kexternEntry.value.single().typeConstructor.dri, + kexternEntry.key)!! + assertEquals("KotlinModule", kexternRes.name) + assertEquals("com.fasterxml.jackson.module.kotlin/KotlinModule///PointingToDeclaration/", kexternRes.dri.toString()) + + val kexternSupertypes = kexternRes.cast().supertypes.values.single() + .map { it.typeConstructor.dri.classNames } + assertEquals( + listOf("SimpleModule"), + kexternSupertypes + ) + assertEquals(Language.KOTLIN, kexternRes.sources.values.single().language) + assertEquals("com.fasterxml.jackson.module.kotlin", kexternRes.sources.values.single().path) + } + } + } + + @Test + fun `external documentable from dependency`() { + val coroutinesPath = + PathManager.getResourceRoot(Job::class.java, "/kotlinx/coroutines/Job.class") + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + analysisPlatform = "jvm" + classpath += listOf(jvmStdlibPath!!, coroutinesPath!!) + } + } + } + + testInline( + """ + /src/com/sample/MyJob.kt + package com.sample + import kotlinx.coroutines.Job + abstract class MyJob: Job + """.trimIndent(), + configuration + ) { + lateinit var provider: ExternalDocumentablesProvider + pluginsSetupStage = { + provider = it.plugin().querySingle { externalDocumentablesProvider } + } + documentablesTransformationStage = { mod -> + val entry = mod.packages.single().classlikes.single().cast().supertypes.entries.single() + val res = provider.findClasslike( + entry.value.single().typeConstructor.dri, + entry.key) + assertEquals("Job", res?.name) + assertEquals("kotlinx.coroutines/Job///PointingToDeclaration/", res?.dri?.toString()) + + val supertypes = res?.cast()?.supertypes?.values?.single() + ?.map { it.typeConstructor.dri.classNames } + assertEquals( + listOf("CoroutineContext.Element"), + supertypes + ) + } + } + } + + @Test + fun `external documentable for nested class`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + analysisPlatform = "jvm" + classpath += jvmStdlibPath!! + } + } + } + + testInline( + """ + /src/com/sample/MyList.kt + package com.sample + abstract class MyEntry: Map.Entry + """.trimIndent(), + configuration + ) { + lateinit var provider: ExternalDocumentablesProvider + pluginsSetupStage = { + provider = it.plugin().querySingle { externalDocumentablesProvider } + } + documentablesTransformationStage = { mod -> + val entry = mod.packages.single().classlikes.single().cast().supertypes.entries.single() + val res = provider.findClasslike( + entry.value.single().typeConstructor.dri, + entry.key) + assertEquals("Entry", res?.name) + assertEquals("kotlin.collections/Map.Entry///PointingToDeclaration/", res?.dri?.toString()) + } + } + } +} \ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt index 1e3bd80042..d156e6b39b 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt @@ -141,8 +141,7 @@ internal fun DProperty.javaAccessors(isTopLevel: Boolean = false, relocateToClas sourceSets.associateWith { setOf(ExtraModifiers.JavaOnlyModifiers.Static) } - ) - else getter.extra + ) else getter.extra ) }, setter?.let { setter -> @@ -165,13 +164,12 @@ internal fun DProperty.javaAccessors(isTopLevel: Boolean = false, relocateToClas }, modifier = javaModifierFromSetter(), visibility = visibility.mapValues { JavaVisibility.Public }, - type = Void, + type = Void(sources), extra = if (isTopLevel) setter.extra + setter.extra.mergeAdditionalModifiers( sourceSets.associateWith { setOf(ExtraModifiers.JavaOnlyModifiers.Static) } - ) - else setter.extra + ) else setter.extra ) } ) @@ -278,7 +276,7 @@ internal fun DClass.functionsInJava(): List = .flatMap { it.asJava(dri.classNames ?: name) } private fun DTypeParameter.asJava(): DTypeParameter = copy( - variantTypeParameter = variantTypeParameter.withDri(dri.possiblyAsJava()), + variantTypeParameter = variantTypeParameter.withDri(dri.possiblyAsJava(), sources), bounds = bounds.map { it.asJava() } ) @@ -353,7 +351,7 @@ internal fun DObject.asJava(): DObject = copy( visibility = sourceSets.associateWith { JavaVisibility.Public }, - type = GenericTypeConstructor(dri, emptyList()), + type = GenericTypeConstructor(dri = dri, projections = emptyList(), sources = sources), setter = null, getter = null, sourceSets = sourceSets,