Skip to content

Commit

Permalink
Correctly handle case when we have internal expect but `public actu…
Browse files Browse the repository at this point in the history
…al` declarations (#3662)
  • Loading branch information
whyoleg committed Jul 9, 2024
1 parent 02db40b commit e626d1a
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 109,12 @@ public class DefaultDocumentableMerger(context: DokkaContext) : DocumentableMerg

fun analyzeExpectActual(sameDriElements: List<T>): List<T> {
val (expects, actuals) = sameDriElements.partition { it.expectPresentInSet != null }
val groupedByOwnExpectWithActualSourceSetIds = expects.map { expect ->
// It's possible that there are no `expect` declarations, but there are `actual` declarations,
// e.g. in case `expect` is `internal` or filtered previously for some other reason.
// In this case we just merge `actual` declarations without `expect`
val groupedActualsWithSourceSets = if (expects.isEmpty()) {
listOf(actuals to actuals.flatMap { it.sourceSets }.toSet())
} else expects.map { expect ->
val actualsForGivenExpect = actuals.filter { actual ->
dependencyInfo[actual.sourceSets.single()]
?.contains(expect.expectPresentInSet!!)
Expand All @@ -118,7 123,7 @@ public class DefaultDocumentableMerger(context: DokkaContext) : DocumentableMerg
(listOf(expect) actualsForGivenExpect) to actualsForGivenExpect.flatMap { it.sourceSets }.toSet()
}
val reducedToOneDocumentableWithActualSourceSetIds =
groupedByOwnExpectWithActualSourceSetIds.map { it.first.reduce(reducer) to it.second }
groupedActualsWithSourceSets.map { it.first.reduce(reducer) to it.second }
return reducedToOneDocumentableWithActualSourceSetIds.let(::mergeClashingElements)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 305,79 @@ class ExpectActualsTest : BaseAbstractTest() {
}
}
}

@Test
fun `public actual function should be present when expect is internal`() = testInline(
"""
/src/common/test.kt
internal expect fun function(): String?
/src/jvm/test.kt
public actual fun function(): String? = null
/src/native/test.kt
public actual fun function(): String? = null
""".trimMargin(),
multiplatformConfiguration
) {
documentablesTransformationStage = { module ->
val function = module.packages.single().functions.single { it.name == "function" }
assertTrue(function.isExpectActual)
// no `common` is present
assertEquals(
setOf("jvm", "native"),
function.sourceSets.map { it.sourceSetID.sourceSetName }.toSet()
)
}
}

@Test
fun `public actual function should be present when expect is internal and other actual is internal`() = testInline(
"""
/src/common/test.kt
internal expect fun function(): String?
/src/jvm/test.kt
public actual fun function(): String? = null
/src/native/test.kt
internal actual fun function(): String? = null
""".trimMargin(),
multiplatformConfiguration
) {
documentablesTransformationStage = { module ->
val function = module.packages.single().functions.single { it.name == "function" }
assertTrue(function.isExpectActual)
// `common` - internal, `native` - internal, so only `jvm` is present
assertEquals(
setOf("jvm"),
function.sourceSets.map { it.sourceSetID.sourceSetName }.toSet()
)
}
}

@Test
fun `public actual classe should be present when expect is internal`() = testInline(
"""
/src/common/test.kt
internal expect class Class
/src/jvm/test.kt
public actual class Class
/src/native/test.kt
public actual class Class
""".trimMargin(),
multiplatformConfiguration
) {
documentablesTransformationStage = { module ->
val classlike = module.packages.single().classlikes.single { it.name == "Class" }
assertTrue(classlike.isExpectActual)
// no `common` is present
assertEquals(
setOf("jvm", "native"),
classlike.sourceSets.map { it.sourceSetID.sourceSetName }.toSet()
)
}
}
}

0 comments on commit e626d1a

Please sign in to comment.