Skip to content

Commit d6898f5

Browse files
authored
Correctly compute visibility for optics (#3877)
1 parent 5309d2f commit d6898f5

5 files changed

Lines changed: 58 additions & 8 deletions

File tree

arrow-libs/optics/arrow-optics-ksp-plugin/src/main/kotlin/arrow/optics/plugin/internals/domain.kt

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ import arrow.optics.plugin.companionObject
66
import com.google.devtools.ksp.getDeclaredProperties
77
import com.google.devtools.ksp.getVisibility
88
import com.google.devtools.ksp.symbol.KSClassDeclaration
9+
import com.google.devtools.ksp.symbol.KSDeclaration
910
import com.google.devtools.ksp.symbol.KSType
1011
import com.google.devtools.ksp.symbol.KSTypeParameter
1112
import com.google.devtools.ksp.symbol.Variance
13+
import com.google.devtools.ksp.symbol.Visibility
1214
import java.util.*
1315

1416
data class ADT(val declaration: KSClassDeclaration, val targets: List<Target>) {
1517
val sourceClassName = declaration.qualifiedNameOrSimpleName
1618
val sourceName = declaration.simpleName.asString().replaceFirstChar { it.lowercase(Locale.getDefault()) }.sanitize()
1719
val simpleName = declaration.nameWithParentClass
1820
val packageName = declaration.packageName.asSanitizedString()
19-
val visibilityModifierName =
20-
(declaration.companionObject?.getVisibility() ?: declaration.getVisibility()).name.lowercase()
21+
val visibilityModifierName = declaration.effectiveCompanionVisibility.name.lowercase()
2122
val typeParameters: List<String> = declaration.typeParameters.map { tyParam ->
2223
if (tyParam.variance == Variance.STAR) return@map "*"
2324
// val prefix = when (it.variance) {
@@ -52,6 +53,36 @@ val KSClassDeclaration.nameWithParentClass: String
5253
else -> simpleName.asString()
5354
}
5455

56+
val KSClassDeclaration.effectiveCompanionVisibility: Visibility
57+
get() {
58+
val visibilities =
59+
listOfNotNull(companionObject?.getVisibility(), getVisibility()) +
60+
allParentDeclarations().filterIsInstance<KSClassDeclaration>().map { it.getVisibility() }
61+
return visibilities.foldRight(Visibility.PUBLIC, Visibility::plus)
62+
}
63+
64+
fun KSDeclaration.allParentDeclarations(): List<KSDeclaration> = when (val parent = parentDeclaration) {
65+
null -> emptyList()
66+
else -> listOfNotNull(parent) + parent.allParentDeclarations()
67+
}
68+
69+
operator fun Visibility.plus(other: Visibility): Visibility = when {
70+
this == other -> this
71+
72+
this == Visibility.PUBLIC -> other
73+
74+
other == Visibility.PUBLIC -> this
75+
76+
this == Visibility.LOCAL || other == Visibility.LOCAL -> Visibility.LOCAL
77+
78+
this == Visibility.PRIVATE || other == Visibility.PRIVATE -> Visibility.PRIVATE
79+
80+
(this == Visibility.INTERNAL || this == Visibility.PROTECTED) &&
81+
(other == Visibility.INTERNAL || other == Visibility.PROTECTED) -> Visibility.PRIVATE
82+
83+
else -> Visibility.PRIVATE
84+
}
85+
5586
enum class OpticsTarget {
5687
ISO,
5788
LENS,

arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/Compilation.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fun buildCompilation(
8989
this.sources = sources.toList()
9090
this.verbose = false
9191
this.allWarningsAsErrors = allWarningsAsErrors
92-
this.languageVersion = "2.0"
92+
this.languageVersion = "2.1"
9393
if (contextParameters) {
9494
this.kotlincArguments = listOf("-Xcontext-parameters")
9595
}

arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/DSLTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class DSLTests {
1313
|$dslModel
1414
|$dslValues
1515
|val modify = Employees.employees.every.company.notNull.address
16-
| .street.name.modify(employees, String::toUpperCase)
16+
| .street.name.modify(employees, String::uppercase)
1717
|val r = modify.employees.map { it.company?.address?.street?.name }.toString()
1818
""".evals("r" to "[LAMBDA STREET, LAMBDA STREET]")
1919
}

arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/LensTests.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,23 @@ class LensTests {
325325
|val r = lens != null
326326
""".evals("r" to true)
327327
}
328+
329+
@Test
330+
fun `Visibilities are correctly computed (#3869)`() {
331+
"""
332+
|$`package`
333+
|$imports
334+
|@optics
335+
|internal sealed interface Interface {
336+
| @optics
337+
| data class DataClass(val value: Int) : Interface {
338+
| companion object
339+
| }
340+
| companion object
341+
|}
342+
|
343+
|internal val lens: Lens<Interface.DataClass, Int> = Interface.DataClass.value
344+
|internal val r = lens != null
345+
""".evals("r" to true)
346+
}
328347
}

generator-scripts/tuple.generator.kts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ for (i in 2..maxTuple) {
2828

2929
val letters = availableLetters.take(i + 1)
3030

31-
val diamond1 = "<${letters.joinToString { it.toUpperCase() }}>"
32-
val diamond2 = "<${letters.dropLast(1).joinToString { it.toUpperCase() }}>"
31+
val diamond1 = "<${letters.joinToString { it.uppercase() }}>"
32+
val diamond2 = "<${letters.dropLast(1).joinToString { it.uppercase() }}>"
3333

3434
val newLetter = availableLetters[i]
3535

3636
val constructor = (letters.dropLast(1).map { "this.$it" } + newLetter).joinToString()
3737

3838
fileContent.append(
39-
"operator fun $diamond1 Tuple$i$diamond2.plus($newLetter: ${newLetter.toUpperCase()}): Tuple${i+1}$diamond1 = Tuple${i + 1}($constructor)"
39+
"operator fun $diamond1 Tuple$i$diamond2.plus($newLetter: ${newLetter.uppercase()}): Tuple${i+1}$diamond1 = Tuple${i + 1}($constructor)"
4040
)
4141
fileContent.append("\n")
4242

4343
}
4444

4545
val fileLocation = "../modules/core/arrow-syntax/src/main/kotlin/arrow/syntax/collections/tuple.kt"
4646

47-
File("tuple.kt").writeText(fileContent.toString())
47+
File("tuple.kt").writeText(fileContent.toString())

0 commit comments

Comments
 (0)