Skip to content

Commit

Permalink
Get information about expect/actual for properties in K2 via Analysis…
Browse files Browse the repository at this point in the history
… API and not via PSI (#3617)

* return `false` for `isExpectActual` for property accessors
  • Loading branch information
whyoleg committed May 22, 2024
1 parent 68e1760 commit 72d32b0
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -740,8 +740,6 @@ private class DokkaDescriptorVisitor(
): DFunction {
val dri = parent.copy(callable = Callable.from(descriptor))
val isGetter = descriptor is PropertyGetterDescriptor
val isExpect = descriptor.isExpect
val isActual = descriptor.isActual

suspend fun PropertyDescriptor.asParameter(parent: DRI) =
DParameter(
Expand Down Expand Up @@ -798,7 +796,7 @@ private class DokkaDescriptorVisitor(
type = descriptor.returnType!!.toBound(),
generics = generics.await(),
modifier = descriptor.modifier().toSourceSetDependent(),
expectPresentInSet = sourceSet.takeIf { isExpect },
expectPresentInSet = null,
receiver = descriptor.extensionReceiverParameter?.let {
visitReceiverParameterDescriptor(
it,
Expand All @@ -807,7 +805,7 @@ private class DokkaDescriptorVisitor(
},
sources = descriptor.createSources(),
sourceSets = setOf(sourceSet),
isExpectActual = (isExpect || isActual),
isExpectActual = false,
extra = PropertyContainer.withAll(
descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(),
descriptor.getAnnotations().toSourceSetDependent().toAnnotations(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ import org.jetbrains.kotlin.analysis.api.types.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier

internal class DefaultSymbolToDocumentableTranslator(context: DokkaContext) : AsyncSourceToDocumentableTranslator {
private val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
Expand Down Expand Up @@ -93,12 +90,6 @@ internal class DokkaSymbolVisitor(

private fun <T> T.toSourceSetDependent() = if (this != null) mapOf(sourceSet to this) else emptyMap()

// KT-54846 will replace
private val KtDeclarationSymbol.isActual
get() = (psi as? KtModifierListOwner)?.hasActualModifier() == true
private val KtDeclarationSymbol.isExpect
get() = (psi as? KtModifierListOwner)?.hasExpectModifier() == true

private fun <T : KtSymbol> List<T>.filterSymbolsInSourceSet(moduleFiles: Set<KtFile>): List<T> = filter {
when (val file = it.psi?.containingFile) {
is KtFile -> moduleFiles.contains(file)
Expand Down Expand Up @@ -481,8 +472,10 @@ internal class DokkaSymbolVisitor(
withExceptionCatcher(propertySymbol) {
val dri = createDRIWithOverridden(propertySymbol).origin
val inheritedFrom = dri.getInheritedFromDRI(parent)
val isExpect = propertySymbol.isExpect
val isActual = propertySymbol.isActual
val (isExpect, isActual) = when (propertySymbol) {
is KtKotlinPropertySymbol -> propertySymbol.isExpect to propertySymbol.isActual
is KtSyntheticJavaPropertySymbol -> false to false
}
val generics =
propertySymbol.typeParameters.mapIndexed { index, symbol ->
visitVariantTypeParameter(
Expand Down Expand Up @@ -592,9 +585,6 @@ internal class DokkaSymbolVisitor(
// for SyntheticJavaProperty
val inheritedFrom = if(propertyAccessorSymbol.origin == KtSymbolOrigin.JAVA_SYNTHETIC_PROPERTY) dri.copy(callable = null) else null

val isExpect = propertyAccessorSymbol.isExpect
val isActual = propertyAccessorSymbol.isActual

val generics = propertyAccessorSymbol.typeParameters.mapIndexed { index, symbol ->
visitVariantTypeParameter(
index,
Expand All @@ -616,15 +606,15 @@ internal class DokkaSymbolVisitor(
parameters = propertyAccessorSymbol.valueParameters.mapIndexed { index, symbol ->
visitValueParameter(index, symbol, dri)
},
expectPresentInSet = sourceSet.takeIf { isExpect },
expectPresentInSet = null,
sources = propertyAccessorSymbol.getSource(),
visibility = propertyAccessorSymbol.visibility.toDokkaVisibility().toSourceSetDependent(),
generics = generics,
documentation = getDocumentation(propertyAccessorSymbol)?.toSourceSetDependent() ?: emptyMap(),
modifier = propertyAccessorSymbol.getDokkaModality().toSourceSetDependent(),
type = toBoundFrom(propertyAccessorSymbol.returnType),
sourceSets = setOf(sourceSet),
isExpectActual = (isExpect || isActual),
isExpectActual = false,
extra = PropertyContainer.withAll(
propertyAccessorSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,135 @@ import kotlin.test.assertTrue

class ExpectActualsTest : BaseAbstractTest() {

private val multiplatformConfiguration = dokkaConfiguration {
sourceSets {
val commonId = sourceSet {
sourceRoots = listOf("src/common/")
analysisPlatform = "common"
name = "common"
displayName = "common"
}.value.sourceSetID
sourceSet {
sourceRoots = listOf("src/jvm/")
analysisPlatform = "jvm"
name = "jvm"
displayName = "jvm"
dependentSourceSets = setOf(commonId)
}
sourceSet {
sourceRoots = listOf("src/native/")
analysisPlatform = "native"
name = "native"
displayName = "native"
dependentSourceSets = setOf(commonId)
}
}
}
private val commonSourceSetId =
multiplatformConfiguration.sourceSets.single { it.displayName == "common" }.sourceSetID

@Test
fun `property inside expect class should be marked as expect`() = testInline(
"""
/src/common/test.kt
expect class ExpectActualClass {
val property: String?
}
/src/jvm/test.kt
actual class ExpectActualClass {
actual val property: String? = null
}
/src/native/test.kt
actual class ExpectActualClass {
actual val property: String? = null
}
""".trimMargin(),
multiplatformConfiguration
) {
documentablesTransformationStage = { module ->
val cls = module.packages.single().classlikes.single { it.name == "ExpectActualClass" }
assertTrue(cls.isExpectActual)
assertEquals(commonSourceSetId, cls.expectPresentInSet?.sourceSetID)
val property = cls.properties.single { it.name == "property" }
assertTrue(property.isExpectActual)
assertEquals(commonSourceSetId, property.expectPresentInSet?.sourceSetID)
}
}

@Test
fun `function inside expect class should be marked as expect`() = testInline(
"""
/src/common/test.kt
expect class ExpectActualClass {
fun function(): String?
}
/src/jvm/test.kt
actual class ExpectActualClass {
actual fun function(): String? = null
}
/src/native/test.kt
actual class ExpectActualClass {
actual fun function(): String? = null
}
""".trimMargin(),
multiplatformConfiguration
) {
documentablesTransformationStage = { module ->
val cls = module.packages.single().classlikes.single { it.name == "ExpectActualClass" }
assertTrue(cls.isExpectActual)
assertEquals(commonSourceSetId, cls.expectPresentInSet?.sourceSetID)
val function = cls.functions.single { it.name == "function" }
assertTrue(function.isExpectActual)
assertEquals(commonSourceSetId, function.expectPresentInSet?.sourceSetID)
}
}

@Test
fun `top level expect property should be marked as expect`() = testInline(
"""
/src/common/test.kt
expect val property: String?
/src/jvm/test.kt
actual val property: String? = null
/src/native/test.kt
actual val property: String? = null
""".trimMargin(),
multiplatformConfiguration
) {
documentablesTransformationStage = { module ->
val property = module.packages.single().properties.single { it.name == "property" }
assertTrue(property.isExpectActual)
assertEquals(commonSourceSetId, property.expectPresentInSet?.sourceSetID)
}
}

@Test
fun `top level expect function should be marked as expect`() = testInline(
"""
/src/common/test.kt
expect fun function(): String?
/src/jvm/test.kt
actual fun function(): String? = null
/src/native/test.kt
actual fun function(): String? = null
""".trimMargin(),
multiplatformConfiguration
) {
documentablesTransformationStage = { module ->
val function = module.packages.single().functions.single { it.name == "function" }
assertTrue(function.isExpectActual)
assertEquals(commonSourceSetId, function.expectPresentInSet?.sourceSetID)
}
}

@Test
fun `three same named expect actual classes`() {

Expand Down

0 comments on commit 72d32b0

Please sign in to comment.