aboutsummaryrefslogtreecommitdiff
path: root/plugins/base
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2022-06-17 15:16:03 +0200
committerGitHub <noreply@github.com>2022-06-17 15:16:03 +0200
commit9f67dcf75d3b86fa6e4e352d2cebc4f9e17b8048 (patch)
tree5b30ddf007113d5b3da04390e093dc8023c04c79 /plugins/base
parentb783439932372e823c36776c53cda5bdc8d0ccae (diff)
downloaddokka-9f67dcf75d3b86fa6e4e352d2cebc4f9e17b8048.tar.gz
dokka-9f67dcf75d3b86fa6e4e352d2cebc4f9e17b8048.tar.bz2
dokka-9f67dcf75d3b86fa6e4e352d2cebc4f9e17b8048.zip
Handle more corner cases for inherited accessors (#2532)
Diffstat (limited to 'plugins/base')
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt45
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt94
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt33
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt38
-rw-r--r--plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt431
-rw-r--r--plugins/base/src/test/kotlin/signatures/SignatureTest.kt5
-rw-r--r--plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt172
-rw-r--r--plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt31
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt106
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt81
10 files changed, 894 insertions, 142 deletions
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
index bfd755cd..38992ba0 100644
--- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
@@ -21,6 +21,7 @@ import org.jetbrains.dokka.links.Callable
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.AnnotationTarget
import org.jetbrains.dokka.model.Nullable
+import org.jetbrains.dokka.model.Visibility
import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.plugability.DokkaContext
@@ -40,9 +41,11 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
+import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
+import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.load.kotlin.toSourceElement
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
@@ -379,7 +382,7 @@ private class DokkaDescriptorVisitor(
return coroutineScope {
val descriptorsWithKind = scope.getDescriptorsWithKind()
- val (regularFunctions, accessors) = splitFunctionsAndAccessors(
+ val (regularFunctions, accessors) = splitFunctionsAndInheritedAccessors(
properties = descriptorsWithKind.properties,
functions = descriptorsWithKind.functions
)
@@ -427,11 +430,11 @@ private class DokkaDescriptorVisitor(
/**
* @param implicitAccessors getters/setters that are not part of the property descriptor, for instance
- * average methods inherited from java sources
+ * average methods inherited from java sources that access the property
*/
private suspend fun visitPropertyDescriptor(
originalDescriptor: PropertyDescriptor,
- implicitAccessors: List<FunctionDescriptor>,
+ implicitAccessors: DescriptorAccessorHolder?,
parent: DRIWithPlatformInfo
): DProperty {
val (dri, _) = originalDescriptor.createDRI()
@@ -451,9 +454,7 @@ private class DokkaDescriptorVisitor(
}
suspend fun getImplicitAccessorGetter() =
- implicitAccessors
- .firstOrNull { it.isGetterFor(originalDescriptor) }
- ?.let { visitFunctionDescriptor(it, parent) }
+ implicitAccessors?.getter?.let { visitFunctionDescriptor(it, parent) }
// example - generated setter that comes with data classes
suspend fun getDescriptorSetter() =
@@ -464,9 +465,7 @@ private class DokkaDescriptorVisitor(
}
suspend fun getImplicitAccessorSetter() =
- implicitAccessors
- .firstOrNull { it.isSetterFor(originalDescriptor) }
- ?.let { visitFunctionDescriptor(it, parent) }
+ implicitAccessors?.setter?.let { visitFunctionDescriptor(it, parent) }
return coroutineScope {
val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } }
@@ -480,7 +479,7 @@ private class DokkaDescriptorVisitor(
sources = actual,
getter = getDescriptorGetter() ?: getImplicitAccessorGetter(),
setter = getDescriptorSetter() ?: getImplicitAccessorSetter(),
- visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(),
+ visibility = descriptor.getVisibility(implicitAccessors).toSourceSetDependent(),
documentation = descriptor.resolveDescriptorData(),
modifier = descriptor.modifier().toSourceSetDependent(),
type = descriptor.returnType!!.toBound(),
@@ -502,6 +501,22 @@ private class DokkaDescriptorVisitor(
}
}
+ private fun PropertyDescriptor.getVisibility(implicitAccessors: DescriptorAccessorHolder?): Visibility {
+ val isNonPublicJavaProperty = this is JavaPropertyDescriptor && !this.visibility.isPublicAPI
+ val visibility =
+ if (isNonPublicJavaProperty) {
+ // only try to take implicit getter's visibility if it's a java property
+ // because it's not guaranteed that implicit accessor will be used
+ // for the kotlin property, as it may have an explicit accessor of its own,
+ // i.e in data classes or with get() and set() are overridden
+ (implicitAccessors?.getter?.visibility ?: this.visibility)
+ } else {
+ this.visibility
+ }
+
+ return visibility.toDokkaVisibility()
+ }
+
private fun CallableMemberDescriptor.createDRI(wasOverridenBy: DRI? = null): Pair<DRI, DRI?> =
if (kind == CallableMemberDescriptor.Kind.DECLARATION || overriddenDescriptors.isEmpty())
Pair(DRI.from(this), wasOverridenBy)
@@ -818,12 +833,11 @@ private class DokkaDescriptorVisitor(
private suspend fun List<PropertyDescriptor>.visitProperties(
parent: DRIWithPlatformInfo,
- implicitAccessors: Map<PropertyDescriptor, List<FunctionDescriptor>> = emptyMap(),
+ implicitAccessors: Map<PropertyDescriptor, DescriptorAccessorHolder> = emptyMap(),
): List<DProperty> {
return coroutineScope {
parallelMap {
- val propertyAccessors = implicitAccessors[it] ?: emptyList()
- visitPropertyDescriptor(it, propertyAccessors, parent)
+ visitPropertyDescriptor(it, implicitAccessors[it], parent)
}
}
}
@@ -1151,11 +1165,14 @@ private class DokkaDescriptorVisitor(
}) + ancestry.interfaces.map { TypeConstructorWithKind(it.typeConstructor, KotlinClassKindTypes.INTERFACE) }
}
- private fun DescriptorVisibility.toDokkaVisibility(): org.jetbrains.dokka.model.Visibility = when (this.delegate) {
+ private fun DescriptorVisibility.toDokkaVisibility(): Visibility = when (this.delegate) {
Visibilities.Public -> KotlinVisibility.Public
Visibilities.Protected -> KotlinVisibility.Protected
Visibilities.Internal -> KotlinVisibility.Internal
Visibilities.Private -> KotlinVisibility.Private
+ JavaVisibilities.ProtectedAndPackage -> KotlinVisibility.Protected
+ JavaVisibilities.ProtectedStaticVisibility -> KotlinVisibility.Protected
+ JavaVisibilities.PackageVisibility -> JavaVisibility.Default
else -> KotlinVisibility.Public
}
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt b/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt
index f182b9be..292dbfca 100644
--- a/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt
@@ -10,29 +10,93 @@ import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName
internal data class DescriptorFunctionsHolder(
val regularFunctions: List<FunctionDescriptor>,
- val accessors: Map<PropertyDescriptor, List<FunctionDescriptor>>
+ val accessors: Map<PropertyDescriptor, DescriptorAccessorHolder>
)
-internal fun splitFunctionsAndAccessors(
+internal data class DescriptorAccessorHolder(
+ val getter: FunctionDescriptor? = null,
+ val setter: FunctionDescriptor? = null
+)
+
+/**
+ * Separate regular Kotlin/Java functions and inherited Java accessors
+ * to properly display properties inherited from Java.
+ *
+ * Take this example:
+ * ```
+ * // java
+ * public class JavaClass {
+ * private int a = 1;
+ * public int getA() { return a; }
+ * public void setA(int a) { this.a = a; }
+ * }
+ *
+ * // kotlin
+ * class Bar : JavaClass() {
+ * fun foo() {}
+ * }
+ * ```
+ *
+ * It should result in:
+ * - 1 regular function `foo`
+ * - Map a=[`getA`, `setA`]
+ */
+internal fun splitFunctionsAndInheritedAccessors(
properties: List<PropertyDescriptor>,
functions: List<FunctionDescriptor>
): DescriptorFunctionsHolder {
+ val (javaMethods, kotlinFunctions) = functions.partition { it is JavaMethodDescriptor }
+ if (javaMethods.isEmpty()) {
+ return DescriptorFunctionsHolder(regularFunctions = kotlinFunctions, emptyMap())
+ }
+
val propertiesByName = properties.associateBy { it.name.asString() }
- val regularFunctions = mutableListOf<FunctionDescriptor>()
- val accessors = mutableMapOf<PropertyDescriptor, MutableList<FunctionDescriptor>>()
- functions.forEach { function ->
+ val regularFunctions = ArrayList<FunctionDescriptor>(kotlinFunctions)
+
+ val accessors = mutableMapOf<PropertyDescriptor, DescriptorAccessorHolder>()
+ javaMethods.forEach { function ->
val possiblePropertyNamesForFunction = function.toPossiblePropertyNames()
val property = possiblePropertyNamesForFunction.firstNotNullOfOrNull { propertiesByName[it] }
if (property != null && function.isAccessorFor(property)) {
- accessors.getOrPut(property, ::mutableListOf).add(function)
+ accessors.compute(property) { prop, accessorHolder ->
+ if (function.isGetterFor(prop))
+ accessorHolder?.copy(getter = function) ?: DescriptorAccessorHolder(getter = function)
+ else
+ accessorHolder?.copy(setter = function) ?: DescriptorAccessorHolder(setter = function)
+ }
} else {
regularFunctions.add(function)
}
}
+
+ val accessorLookalikes = removeNonAccessorsReturning(accessors)
+ regularFunctions.addAll(accessorLookalikes)
+
return DescriptorFunctionsHolder(regularFunctions, accessors)
}
-internal fun FunctionDescriptor.toPossiblePropertyNames(): List<String> {
+/**
+ * If a field has no getter, it's not accessible as a property from Kotlin's perspective,
+ * but it still might have a setter lookalike. In this case, this "setter" should be just a regular function
+ *
+ * @return removed elements
+ */
+private fun removeNonAccessorsReturning(
+ propertyAccessors: MutableMap<PropertyDescriptor, DescriptorAccessorHolder>
+): List<FunctionDescriptor> {
+ val nonAccessors = mutableListOf<FunctionDescriptor>()
+ propertyAccessors.entries.removeIf { (_, accessors) ->
+ if (accessors.getter == null && accessors.setter != null) {
+ nonAccessors.add(accessors.setter)
+ true
+ } else {
+ false
+ }
+ }
+ return nonAccessors
+}
+
+private fun FunctionDescriptor.toPossiblePropertyNames(): List<String> {
val stringName = this.name.asString()
return when {
JvmAbi.isSetterName(stringName) -> propertyNamesBySetMethodName(this.name).map { it.asString() }
@@ -41,7 +105,7 @@ internal fun FunctionDescriptor.toPossiblePropertyNames(): List<String> {
}
}
-internal fun propertyNamesByGetMethod(functionDescriptor: FunctionDescriptor): List<String> {
+private fun propertyNamesByGetMethod(functionDescriptor: FunctionDescriptor): List<String> {
val stringName = functionDescriptor.name.asString()
// In java, the convention for boolean property accessors is as follows:
// - `private boolean active;`
@@ -63,21 +127,19 @@ internal fun propertyNamesByGetMethod(functionDescriptor: FunctionDescriptor): L
return listOfNotNull(javaPropName, kotlinPropName)
}
-internal fun FunctionDescriptor.isAccessorFor(property: PropertyDescriptor): Boolean {
- return this.isGetterFor(property) || this.isSetterFor(property)
+private fun FunctionDescriptor.isAccessorFor(property: PropertyDescriptor): Boolean {
+ return (this.isGetterFor(property) || this.isSetterFor(property))
+ && !property.visibility.isPublicAPI
+ && this.visibility.isPublicAPI
}
-internal fun FunctionDescriptor.isGetterFor(property: PropertyDescriptor): Boolean {
+private fun FunctionDescriptor.isGetterFor(property: PropertyDescriptor): Boolean {
return this.returnType == property.returnType
&& this.valueParameters.isEmpty()
- && !property.visibility.isPublicAPI
- && this.visibility.isPublicAPI
}
-internal fun FunctionDescriptor.isSetterFor(property: PropertyDescriptor): Boolean {
+private fun FunctionDescriptor.isSetterFor(property: PropertyDescriptor): Boolean {
return this.valueParameters.size == 1
&& this.valueParameters[0].type == property.returnType
- && !property.visibility.isPublicAPI
- && this.visibility.isPublicAPI
}
diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
index c20073a4..bef86144 100644
--- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
@@ -17,9 +17,9 @@ import org.jetbrains.dokka.analysis.KotlinAnalysis
import org.jetbrains.dokka.analysis.PsiDocumentableSource
import org.jetbrains.dokka.analysis.from
import org.jetbrains.dokka.base.DokkaBase
-import org.jetbrains.dokka.base.translators.typeConstructorsBeingExceptions
import org.jetbrains.dokka.base.translators.psi.parsers.JavaDocumentationParser
import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser
+import org.jetbrains.dokka.base.translators.typeConstructorsBeingExceptions
import org.jetbrains.dokka.base.translators.unquotedValue
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.model.*
@@ -40,12 +40,7 @@ import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
-import org.jetbrains.kotlin.load.java.JvmAbi
-import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
-import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName
-import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
-import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.File
@@ -108,16 +103,6 @@ class DefaultPsiToDocumentableTranslator(
private val cachedBounds = hashMapOf<String, Bound>()
- private fun PsiModifierListOwner.getVisibility() = modifierList?.let {
- val ml = it.children.toList()
- when {
- ml.any { it.text == PsiKeyword.PUBLIC } || it.hasModifierProperty("public") -> JavaVisibility.Public
- ml.any { it.text == PsiKeyword.PROTECTED } || it.hasModifierProperty("protected") -> JavaVisibility.Protected
- ml.any { it.text == PsiKeyword.PRIVATE } || it.hasModifierProperty("private") -> JavaVisibility.Private
- else -> JavaVisibility.Default
- }
- } ?: JavaVisibility.Default
-
private val PsiMethod.hash: Int
get() = "$returnType $name$parameterList".hashCode()
@@ -641,7 +626,7 @@ class DefaultPsiToDocumentableTranslator(
documentation = javadocParser.parseDocumentation(psi).toSourceSetDependent(),
expectPresentInSet = null,
sources = PsiDocumentableSource(psi).toSourceSetDependent(),
- visibility = psi.getVisibility().toSourceSetDependent(),
+ visibility = psi.getVisibility(getter).toSourceSetDependent(),
type = getBound(psi.type),
receiver = null,
setter = setter,
@@ -666,6 +651,10 @@ class DefaultPsiToDocumentableTranslator(
)
}
+ private fun PsiField.getVisibility(getter: DFunction?): Visibility {
+ return getter?.visibility?.get(sourceSetData) ?: this.getVisibility()
+ }
+
private fun Collection<PsiAnnotation>.toListOfAnnotations() =
filter { it !is KtLightAbstractAnnotation }.mapNotNull { it.toAnnotation() }
@@ -740,3 +729,13 @@ class DefaultPsiToDocumentableTranslator(
get() = getChildOfType<PsiJavaCodeReferenceElement>()?.resolve()
}
}
+
+internal fun PsiModifierListOwner.getVisibility() = modifierList?.let {
+ val ml = it.children.toList()
+ when {
+ ml.any { it.text == PsiKeyword.PUBLIC } || it.hasModifierProperty("public") -> JavaVisibility.Public
+ ml.any { it.text == PsiKeyword.PROTECTED } || it.hasModifierProperty("protected") -> JavaVisibility.Protected
+ ml.any { it.text == PsiKeyword.PRIVATE } || it.hasModifierProperty("private") -> JavaVisibility.Private
+ else -> JavaVisibility.Default
+ }
+} ?: JavaVisibility.Default
diff --git a/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt b/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt
index c2ab8c03..2ab70843 100644
--- a/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt
@@ -3,6 +3,9 @@ package org.jetbrains.dokka.base.translators.psi
import com.intellij.psi.PsiField
import com.intellij.psi.PsiMethod
import org.jetbrains.dokka.base.translators.firstNotNullOfOrNull
+import org.jetbrains.dokka.model.JavaVisibility
+import org.jetbrains.dokka.model.KotlinVisibility
+import org.jetbrains.dokka.model.Visibility
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName
@@ -28,9 +31,32 @@ internal fun splitFunctionsAndAccessors(fields: Array<PsiField>, methods: Array<
regularFunctions.add(method)
}
}
+
+ val accessorLookalikes = removeNonAccessorsReturning(accessors)
+ regularFunctions.addAll(accessorLookalikes)
+
return PsiFunctionsHolder(regularFunctions, accessors)
}
+/**
+ * If a field has no getter, it's not accessible as a property from Kotlin's perspective,
+ * but it still might have a setter. In this case, this "setter" should be just a regular function
+ */
+private fun removeNonAccessorsReturning(
+ fieldAccessors: MutableMap<PsiField, MutableList<PsiMethod>>
+): List<PsiMethod> {
+ val nonAccessors = mutableListOf<PsiMethod>()
+ fieldAccessors.entries.removeIf { (field, methods) ->
+ if (methods.size == 1 && methods[0].isSetterFor(field)) {
+ nonAccessors.add(methods[0])
+ true
+ } else {
+ false
+ }
+ }
+ return nonAccessors
+}
+
internal fun PsiMethod.getPossiblePropertyNamesForFunction(): List<String> {
val jvmName = getAnnotation(DescriptorUtils.JVM_NAME.asString())?.findAttributeValue("name")?.text
return jvmName?.let { listOf(jvmName) }
@@ -46,7 +72,9 @@ internal fun PsiMethod.getPossiblePropertyNamesForFunction(): List<String> {
}
internal fun PsiMethod.isAccessorFor(field: PsiField): Boolean {
- return this.isGetterFor(field) || this.isSetterFor(field)
+ return (this.isGetterFor(field) || this.isSetterFor(field))
+ && !field.getVisibility().isPublicAPI()
+ && this.getVisibility().isPublicAPI()
}
internal fun PsiMethod.isGetterFor(field: PsiField): Boolean {
@@ -56,3 +84,11 @@ internal fun PsiMethod.isGetterFor(field: PsiField): Boolean {
internal fun PsiMethod.isSetterFor(field: PsiField): Boolean {
return parameterList.getParameter(0)?.type == field.type
}
+
+internal fun Visibility.isPublicAPI() = when(this) {
+ KotlinVisibility.Public,
+ KotlinVisibility.Protected,
+ JavaVisibility.Public,
+ JavaVisibility.Protected -> true
+ else -> false
+}
diff --git a/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt
new file mode 100644
index 00000000..d3e03666
--- /dev/null
+++ b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt
@@ -0,0 +1,431 @@
+package signatures
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.junit.jupiter.api.Test
+import utils.A
+import utils.Span
+import utils.TestOutputWriterPlugin
+import utils.match
+import kotlin.test.assertEquals
+
+class InheritedAccessorsSignatureTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOf(
+ commonStdlibPath ?: throw IllegalStateException("Common stdlib is not found"),
+ jvmStdlibPath ?: throw IllegalStateException("JVM stdlib is not found")
+ )
+ externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
+ }
+ }
+ }
+
+ @Test
+ fun `should collapse accessor functions inherited from java into the property`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | private int a = 1;
+ | public int getA() { return a; }
+ | public void setA(int a) { this.a = a; }
+ |}
+ |
+ |/src/test/B.kt
+ |package test
+ |class B : A {}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/test/-b/index.html").let { kotlinClassContent ->
+ val signatures = kotlinClassContent.signature().toList()
+ assertEquals(
+ 3, signatures.size,
+ "Expected 3 signatures: class signature, constructor and property"
+ )
+
+ val property = signatures[2]
+ property.match(
+ "var ", A("a"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+
+ writerPlugin.writer.renderedContent("root/test/-a/index.html").let { javaClassContent ->
+ val signatures = javaClassContent.signature().toList()
+ assertEquals(
+ 2, signatures.size,
+ "Expected 2 signatures: class signature and property"
+ )
+
+ val property = signatures[1]
+ property.match(
+ "open var ", A("a"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should render as val if inherited java property has no setter`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | private int a = 1;
+ | public int getA() { return a; }
+ |}
+ |
+ |/src/test/B.kt
+ |package test
+ |class B : A {}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/test/-b/index.html").let { kotlinClassContent ->
+ val signatures = kotlinClassContent.signature().toList()
+ assertEquals(3, signatures.size, "Expected 3 signatures: class signature, constructor and property")
+
+ val property = signatures[2]
+ property.match(
+ "val ", A("a"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+
+ writerPlugin.writer.renderedContent("root/test/-a/index.html").let { javaClassContent ->
+ val signatures = javaClassContent.signature().toList()
+ assertEquals(2, signatures.size, "Expected 2 signatures: class signature and property")
+
+ val property = signatures[1]
+ property.match(
+ "open val ", A("a"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should keep inherited java setter as a regular function due to inaccessible property`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | private int a = 1;
+ | public void setA(int a) { this.a = a; }
+ |}
+ |
+ |/src/test/B.kt
+ |package test
+ |class B : A {}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/test/-b/index.html").let { kotlinClassContent ->
+ val signatures = kotlinClassContent.signature().toList()
+ assertEquals(3, signatures.size, "Expected 3 signatures: class signature, constructor and setter")
+
+ val setterFunction = signatures[2]
+ setterFunction.match(
+ "open fun ", A("setA"), "(", Parameters(
+ Parameter("a: ", A("Int"))
+ ), ")", Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+
+ writerPlugin.writer.renderedContent("root/test/-a/index.html").let { javaClassContent ->
+ val signatures = javaClassContent.signature().toList()
+ assertEquals(2, signatures.size, "Expected 2 signatures: class signature and setter")
+
+ val setterFunction = signatures[1]
+ setterFunction.match(
+ "open fun ", A("setA"), "(", Parameters(
+ Parameter("a: ", A("Int"))
+ ), ")", Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should keep inherited java accessor lookalikes if underlying function is public`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | public int a = 1;
+ | public int getA() { return a; }
+ | public void setA(int a) { this.a = a; }
+ |}
+ |
+ |/src/test/B.kt
+ |package test
+ |class B : A {}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val signatures = writerPlugin.writer.renderedContent("root/test/-b/index.html").signature().toList()
+ assertEquals(
+ 5, signatures.size,
+ "Expected 5 signatures: class signature, constructor, property and two accessor lookalikes"
+ )
+
+ val getterLookalikeFunction = signatures[2]
+ getterLookalikeFunction.match(
+ "open fun ", A("getA"), "():", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+
+ val setterLookalikeFunction = signatures[3]
+ setterLookalikeFunction.match(
+ "open fun ", A("setA"), "(", Parameters(
+ Parameter("a: ", A("Int"))
+ ), ")", Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+
+ val property = signatures[4]
+ property.match(
+ "val ", A("a"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should keep kotlin property with no accessors when java inherits kotlin a var`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/JavaClass.java
+ |package test;
+ |public class JavaClass extends KotlinClass {}
+ |
+ |/src/test/KotlinClass.kt
+ |package test
+ |open class KotlinClass {
+ | var variable: String = "s"
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/test/-java-class/index.html").let { kotlinClassContent ->
+ val signatures = kotlinClassContent.signature().toList()
+ assertEquals(2, signatures.size, "Expected to find two signatures: class and property")
+
+ val property = signatures[1]
+ property.match(
+ "open var ", A("variable"), ": ", Span("String"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `kotlin property with compute get and set`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/JavaClass.java
+ |package test;
+ |public class JavaClass extends KotlinClass {}
+ |
+ |/src/test/KotlinClass.kt
+ |package test
+ |open class KotlinClass {
+ | var variable: String
+ | get() = "asd"
+ | set(value) {}
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/test/-kotlin-class/index.html").let { kotlinClassContent ->
+ val signatures = kotlinClassContent.signature().toList()
+ assertEquals(3, signatures.size, "Expected to find 3 signatures: class, constructor and property")
+
+ val property = signatures[2]
+ property.match(
+ "var ", A("variable"), ": ", A("String"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+
+ // it's actually unclear how it should react in this situation. It should most likely not
+ // break the abstraction and display it as a simple variable just like can be seen from Kotlin,
+ // test added to control changes
+ writerPlugin.writer.renderedContent("root/test/-java-class/index.html").let { javaClassContent ->
+ val signatures = javaClassContent.signature().toList()
+ assertEquals(3, signatures.size, "Expected to find 3 signatures: class and two accessors")
+
+ val getter = signatures[1]
+ getter.match(
+ "fun ", A("getVariable"), "(): ", Span("String"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+
+ val setter = signatures[2]
+ setter.match(
+ "fun ", A("setVariable"), "(", Parameters(
+ Parameter("value: ", Span("String"))
+ ), ")", Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `inherited property should inherit getter's visibility`() {
+ val configWithProtectedVisibility = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOf(
+ commonStdlibPath ?: throw IllegalStateException("Common stdlib is not found"),
+ jvmStdlibPath ?: throw IllegalStateException("JVM stdlib is not found")
+ )
+ externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
+ documentedVisibilities = setOf(
+ DokkaConfiguration.Visibility.PUBLIC,
+ DokkaConfiguration.Visibility.PROTECTED
+ )
+ }
+ }
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/JavaClass.java
+ |package test;
+ |public class JavaClass {
+ | private int protectedGetterAndProtectedSetter = 0;
+ |
+ | protected int getProtectedGetterAndProtectedSetter() {
+ | return protectedGetterAndProtectedSetter;
+ | }
+ |
+ | protected void setProtectedGetterAndProtectedSetter(int protectedGetterAndProtectedSetter) {
+ | this.protectedGetterAndProtectedSetter = protectedGetterAndProtectedSetter;
+ | }
+ |}
+ |
+ |/src/test/KotlinClass.kt
+ |package test
+ |open class KotlinClass : JavaClass() { }
+ """.trimIndent(),
+ configWithProtectedVisibility,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/test/-kotlin-class/index.html").let { kotlinClassContent ->
+ val signatures = kotlinClassContent.signature().toList()
+ assertEquals(3, signatures.size, "Expected 3 signatures: class signature, constructor and property")
+
+ val property = signatures[2]
+ property.match(
+ "protected var ", A("protectedGetterAndProtectedSetter"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+
+ writerPlugin.writer.renderedContent("root/test/-java-class/index.html").let { javaClassContent ->
+ val signatures = javaClassContent.signature().toList()
+ assertEquals(2, signatures.size, "Expected 2 signatures: class signature and property")
+
+ val property = signatures[1]
+ property.match(
+ "protected open var ", A("protectedGetterAndProtectedSetter"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should resolve protected java property as protected`() {
+ val configWithProtectedVisibility = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOf(
+ commonStdlibPath ?: throw IllegalStateException("Common stdlib is not found"),
+ jvmStdlibPath ?: throw IllegalStateException("JVM stdlib is not found")
+ )
+ externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
+ documentedVisibilities = setOf(
+ DokkaConfiguration.Visibility.PUBLIC,
+ DokkaConfiguration.Visibility.PROTECTED
+ )
+ }
+ }
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/test/JavaClass.java
+ |package test;
+ |public class JavaClass {
+ | protected int protectedProperty = 0;
+ |}
+ |
+ |/src/test/KotlinClass.kt
+ |package test
+ |open class KotlinClass : JavaClass() { }
+ """.trimIndent(),
+ configWithProtectedVisibility,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/test/-kotlin-class/index.html").let { kotlinClassContent ->
+ val signatures = kotlinClassContent.signature().toList()
+ assertEquals(3, signatures.size, "Expected 2 signatures: class signature, constructor and property")
+
+ val property = signatures[2]
+ property.match(
+ "protected val ", A("protectedProperty"), ":", A("Int"), Span(),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
index 59665b8c..b021fae1 100644
--- a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
+++ b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
@@ -15,7 +15,10 @@ class SignatureTest : BaseAbstractTest() {
sourceSets {
sourceSet {
sourceRoots = listOf("src/")
- classpath = listOf(commonStdlibPath ?: throw IllegalStateException("Common stdlib is not found"), jvmStdlibPath ?: throw IllegalStateException("JVM stdlib is not found"))
+ classpath = listOf(
+ commonStdlibPath ?: throw IllegalStateException("Common stdlib is not found"),
+ jvmStdlibPath ?: throw IllegalStateException("JVM stdlib is not found")
+ )
externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
}
}
diff --git a/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt b/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt
index a6dd4350..06ced8c9 100644
--- a/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt
+++ b/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt
@@ -1,8 +1,10 @@
package superFields
+import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.InheritedMember
+import org.jetbrains.dokka.model.KotlinVisibility
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@@ -53,6 +55,41 @@ class DescriptorSuperPropertiesTest : BaseAbstractTest() {
}
}
+
+ @Test
+ fun `kotlin inheriting java should ignore setter lookalike for non accessible field`() {
+ testInline(
+ """
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | private int a = 1;
+ |
+ | public void setA(int a) { this.a = a; }
+ |}
+ |
+ |/src/test/B.kt
+ |package test
+ |class B : A {}
+ """.trimIndent(),
+ commonTestConfiguration
+ ) {
+ documentablesMergingStage = { module ->
+ val testedClass = module.packages.single().classlikes.single { it.name == "B" }
+
+ val property = testedClass.properties.firstOrNull { it.name == "a" }
+ assertNull(property, "Inherited property `a` should not be visible as it's not accessible")
+
+ val setterLookalike = testedClass.functions.firstOrNull { it.name == "setA" }
+ assertNotNull(setterLookalike) {
+ "Expected setA to be a regular function because field `a` is neither var nor val from Kotlin's " +
+ "interop perspective, it's not accessible."
+ }
+ }
+ }
+ }
+
+
@Test
fun `kotlin inheriting java should append getter and setter`() {
testInline(
@@ -130,13 +167,27 @@ class DescriptorSuperPropertiesTest : BaseAbstractTest() {
}
@Test
- fun `kotlin inheriting java should not append anything since field is public`() {
+ fun `kotlin inheriting java should not append anything since field is public api`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ name = "jvm"
+ documentedVisibilities = setOf(
+ DokkaConfiguration.Visibility.PUBLIC,
+ DokkaConfiguration.Visibility.PROTECTED
+ )
+ }
+ }
+ }
+
testInline(
"""
|/src/test/A.java
|package test;
|public class A {
- | public int a = 1;
+ | protected int a = 1;
| public int getA() { return a; }
| public void setA(int a) { this.a = a; }
|}
@@ -145,14 +196,18 @@ class DescriptorSuperPropertiesTest : BaseAbstractTest() {
|package test
|class B : A {}
""".trimIndent(),
- commonTestConfiguration
+ configuration
) {
documentablesMergingStage = { module ->
- val kotlinProperties = module.packages.single().classlikes.single { it.name == "B" }.properties
- val property = kotlinProperties.single { it.name == "a" }
+ val testedClass = module.packages.single().classlikes.single { it.name == "B" }
+ val property = testedClass.properties.single { it.name == "a" }
assertNull(property.getter)
assertNull(property.setter)
+ assertEquals(2, testedClass.functions.size)
+
+ assertEquals("getA", testedClass.functions[0].name)
+ assertEquals("setA", testedClass.functions[1].name)
val inheritedFrom = property.extra[InheritedMember]?.inheritedFrom?.values?.single()
assertEquals(DRI(packageName = "test", classNames = "A"), inheritedFrom)
@@ -161,31 +216,106 @@ class DescriptorSuperPropertiesTest : BaseAbstractTest() {
}
@Test
- fun `should preserve regular functions that look like accessors, but are not accessors`() {
+ fun `should inherit property visibility from getter`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ name = "jvm"
+ documentedVisibilities = setOf(
+ DokkaConfiguration.Visibility.PUBLIC,
+ DokkaConfiguration.Visibility.PROTECTED
+ )
+ }
+ }
+ }
+
testInline(
"""
- |/src/test/A.kt
- |package test
- |class A {
- | val v = 0
- | fun setV() { println(10) } // no arg
- | fun getV(): String { return "s" } // wrong return type
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | private int a = 1;
+ | protected int getA() { return a; }
+ | protected void setA(int a) { this.a = a; }
|}
+ |
+ |/src/test/B.kt
+ |package test
+ |class B : A {}
""".trimIndent(),
- commonTestConfiguration
+ configuration
) {
documentablesMergingStage = { module ->
- val testClass = module.packages.single().classlikes.single { it.name == "A" }
- val setterLookalike = testClass.functions.firstOrNull { it.name == "setV" }
- assertNotNull(setterLookalike) {
- "Expected regular function not found, wrongly categorized as setter?"
- }
+ val testedClass = module.packages.single().classlikes.single { it.name == "B" }
+ assertEquals(0, testedClass.functions.size)
+
+ val property = testedClass.properties.single { it.name == "a" }
+
+ assertNotNull(property.getter)
+ assertNotNull(property.setter)
+
+ val propertyVisibility = property.visibility.values.single()
+ assertEquals(KotlinVisibility.Protected, propertyVisibility)
- val getterLookalike = testClass.functions.firstOrNull { it.name == "getV" }
- assertNotNull(getterLookalike) {
- "Expected regular function not found, wrongly categorized as getter?"
+ val getterVisibility = property.getter?.visibility?.values?.single()
+ assertEquals(KotlinVisibility.Protected, getterVisibility)
+
+ val setterVisibility = property.setter?.visibility?.values?.single()
+ assertEquals(KotlinVisibility.Protected, setterVisibility)
+
+ val inheritedFrom = property.extra[InheritedMember]?.inheritedFrom?.values?.single()
+ assertEquals(DRI(packageName = "test", classNames = "A"), inheritedFrom)
+ }
+ }
+ }
+
+ @Test // checking for mapping between kotlin and java visibility
+ fun `should resolve inherited java protected field as protected`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ name = "jvm"
+ documentedVisibilities = setOf(
+ DokkaConfiguration.Visibility.PUBLIC,
+ DokkaConfiguration.Visibility.PROTECTED
+ )
}
}
}
+
+ testInline(
+ """
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | protected int protectedProperty = 0;
+ |}
+ |
+ |/src/test/B.kt
+ |package test
+ |class B : A {}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val testedClass = module.packages.single().classlikes.single { it.name == "B" }
+ assertEquals(0, testedClass.functions.size)
+
+ val property = testedClass.properties.single { it.name == "protectedProperty" }
+
+ assertNull(property.getter)
+ assertNull(property.setter)
+
+ val propertyVisibility = property.visibility.values.single()
+ assertEquals(KotlinVisibility.Protected, propertyVisibility)
+
+ val inheritedFrom = property.extra[InheritedMember]?.inheritedFrom?.values?.single()
+ assertEquals(DRI(packageName = "test", classNames = "A"), inheritedFrom)
+ }
+ }
}
}
diff --git a/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt b/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt
index 025c9b06..8dd74ef2 100644
--- a/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt
+++ b/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt
@@ -1,5 +1,6 @@
package superFields
+import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.Annotations
@@ -153,34 +154,4 @@ class PsiSuperFieldsTest : BaseAbstractTest() {
}
}
}
-
- @Test
- fun `should preserve regular functions that look like accessors, but are not accessors`() {
- testInline(
- """
- |/src/test/A.java
- |package test;
- |public class A {
- | public int a = 1;
- | public void setA() { } // no arg
- | public String getA() { return "s"; } // wrong return type
- |}
- """.trimIndent(),
- commonTestConfiguration
- ) {
- documentablesMergingStage = { module ->
- val testClass = module.packages.single().classlikes.single { it.name == "A" }
-
- val setterLookalike = testClass.functions.firstOrNull { it.name == "setA" }
- assertNotNull(setterLookalike) {
- "Expected regular function not found, wrongly categorized as setter?"
- }
-
- val getterLookalike = testClass.functions.firstOrNull { it.name == "getA" }
- assertNotNull(getterLookalike) {
- "Expected regular function not found, wrongly categorized as getter?"
- }
- }
- }
- }
}
diff --git a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
index 79e9f548..a9466f29 100644
--- a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
+++ b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
@@ -11,6 +11,7 @@ import org.junit.Assert
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
+import kotlin.test.assertNotNull
class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() {
val configuration = dokkaConfiguration {
@@ -22,6 +23,16 @@ class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() {
}
}
+ @Suppress("DEPRECATION") // for includeNonPublic
+ val javaConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/java")
+ includeNonPublic = true
+ }
+ }
+ }
+
@Test
fun `data class kdocs over generated methods`() {
testInline(
@@ -154,33 +165,6 @@ class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() {
}
}
- private sealed class TestSuite {
- abstract val propertyName: String
-
- data class PropertyDoesntExist(
- override val propertyName: String
- ) : TestSuite()
-
-
- data class PropertyExists(
- override val propertyName: String,
- val modifier: KotlinModifier,
- val visibility: KotlinVisibility,
- val additionalModifiers: Set<ExtraModifiers.KotlinOnlyModifiers>
- ) : TestSuite()
-
- data class FunctionDoesntExist(
- override val propertyName: String,
- ) : TestSuite()
-
- data class FunctionExists(
- override val propertyName: String,
- val modifier: KotlinModifier,
- val visibility: KotlinVisibility,
- val additionalModifiers: Set<ExtraModifiers.KotlinOnlyModifiers>
- ) : TestSuite()
- }
-
private fun runTestSuitesAgainstGivenClasses(classlikes: List<DClasslike>, testSuites: List<List<TestSuite>>) {
classlikes.zip(testSuites).forEach { (classlike, testSuites) ->
testSuites.forEach { testSuite ->
@@ -669,16 +653,6 @@ class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() {
}
}
- @Suppress("DEPRECATION") // for includeNonPublic
- val javaConfiguration = dokkaConfiguration {
- sourceSets {
- sourceSet {
- sourceRoots = listOf("src/main/java")
- includeNonPublic = true
- }
- }
- }
-
@Disabled // The compiler throws away annotations on unresolved types upstream
@Test
fun `Can annotate unresolved type`() {
@@ -730,4 +704,62 @@ class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() {
}
}
}
+
+ @Test
+ fun `should preserve regular functions that look like accessors, but are not accessors`() {
+ testInline(
+ """
+ |/src/main/kotlin/A.kt
+ |package test
+ |class A {
+ | private var v: Int = 0
+ |
+ | // not accessors because declared separately, just functions
+ | fun setV(new: Int) { v = new }
+ | fun getV(): Int = v
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val testClass = module.packages.single().classlikes.single { it.name == "A" }
+ val setterLookalike = testClass.functions.firstOrNull { it.name == "setV" }
+ assertNotNull(setterLookalike) {
+ "Expected regular function not found, wrongly categorized as setter?"
+ }
+
+ val getterLookalike = testClass.functions.firstOrNull { it.name == "getV" }
+ assertNotNull(getterLookalike) {
+ "Expected regular function not found, wrongly categorized as getter?"
+ }
+ }
+ }
+ }
+}
+
+private sealed class TestSuite {
+ abstract val propertyName: String
+
+ data class PropertyDoesntExist(
+ override val propertyName: String
+ ) : TestSuite()
+
+
+ data class PropertyExists(
+ override val propertyName: String,
+ val modifier: KotlinModifier,
+ val visibility: KotlinVisibility,
+ val additionalModifiers: Set<ExtraModifiers.KotlinOnlyModifiers>
+ ) : TestSuite()
+
+ data class FunctionDoesntExist(
+ override val propertyName: String,
+ ) : TestSuite()
+
+ data class FunctionExists(
+ override val propertyName: String,
+ val modifier: KotlinModifier,
+ val visibility: KotlinVisibility,
+ val additionalModifiers: Set<ExtraModifiers.KotlinOnlyModifiers>
+ ) : TestSuite()
}
diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
index 1213727d..25d6b22e 100644
--- a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
+++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
@@ -1,15 +1,14 @@
package translators
+import org.jetbrains.dokka.DokkaConfiguration
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
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import utils.assertNotNull
@@ -259,4 +258,76 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() {
}
}
}
+
+ @Test
+ fun `should preserve regular functions that look like accessors, but are not accessors`() {
+ testInline(
+ """
+ |/src/main/java/test/A.java
+ |package test;
+ |public class A {
+ | public int a = 1;
+ | public void setA() { } // no arg
+ | public String getA() { return "s"; } // wrong return type
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val testClass = module.packages.single().classlikes.single { it.name == "A" }
+
+ val setterLookalike = testClass.functions.firstOrNull { it.name == "setA" }
+ assertNotNull(setterLookalike) {
+ "Expected regular function not found, wrongly categorized as setter?"
+ }
+
+ val getterLookalike = testClass.functions.firstOrNull { it.name == "getA" }
+ assertNotNull(getterLookalike) {
+ "Expected regular function not found, wrongly categorized as getter?"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should not associate accessors with field because field is public api`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ documentedVisibilities = setOf(
+ DokkaConfiguration.Visibility.PUBLIC,
+ DokkaConfiguration.Visibility.PROTECTED
+ )
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/test/A.java
+ |package test;
+ |public class A {
+ | protected int a = 1;
+ | public int getA() { return a; }
+ | public void setA(int a) { this.a = a; }
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val testedClass = module.packages.single().classlikes.single { it.name == "A" }
+
+ val property = testedClass.properties.single { it.name == "a" }
+ assertEquals(JavaVisibility.Protected, property.visibility.values.single())
+ assertNull(property.getter)
+ assertNull(property.setter)
+
+ assertEquals(2, testedClass.functions.size)
+
+ assertEquals("getA", testedClass.functions[0].name)
+ assertEquals("setA", testedClass.functions[1].name)
+ }
+ }
+ }
}