Skip to content

Commit

Permalink
Fix optics generation in case of parameters having keywords as names. (
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasz-kusek authored May 28, 2024
1 parent 9894996 commit 796e3b6
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ data class Focus(
val subclasses: List<String> = emptyList(),
val classNameWithParameters: String? = className,
) {
val escapedParamName = paramName.plusIfNotBlank(
prefix = "`",
postfix = "`",
)
val refinedArguments: List<String>
get() = refinedType?.arguments?.filter {
it.type?.resolve()?.declaration is KSTypeParameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ private fun processLensSyntax(ele: ADT, foci: List<Focus>, className: String): S
return if (ele.typeParameters.isEmpty()) {
foci.joinToString(separator = "\n") { focus ->
"""
|${ele.visibilityModifierName} inline val <__S> $Lens<__S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Lens<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <__S> $Lens<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Lens<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|
""".trimMargin()
}
Expand All @@ -47,9 +47,9 @@ private fun processLensSyntax(ele: ADT, foci: List<Focus>, className: String): S
val joinedTypeParams = ele.typeParameters.joinToString(separator = ",")
foci.joinToString(separator = "\n") { focus ->
"""
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Lens<__S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Lens<__S, ${focus.classNameWithParameters}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Lens<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Lens<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|
""".trimMargin()
}
Expand All @@ -60,9 +60,9 @@ private fun processPrismSyntax(ele: ADT, dsl: SealedClassDsl, className: String)
if (ele.typeParameters.isEmpty()) {
dsl.foci.joinToString(separator = "\n\n") { focus ->
"""
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.paramName}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Prism<__S, ${ele.sourceClassName}>.${focus.paramName}: $Prism<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.paramName}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Prism<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Prism<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|
""".trimMargin()
}
Expand All @@ -74,9 +74,9 @@ private fun processPrismSyntax(ele: ADT, dsl: SealedClassDsl, className: String)
else -> focus.refinedArguments.joinToString(separator = ",")
}
"""
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Prism<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Prism<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Prism<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Prism<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|
""".trimMargin()
}
Expand All @@ -86,11 +86,11 @@ private fun processIsoSyntax(ele: ADT, dsl: ValueClassDsl, className: String): S
if (ele.typeParameters.isEmpty()) {
dsl.foci.joinToString(separator = "\n\n") { focus ->
"""
|${ele.visibilityModifierName} inline val <__S> $Iso<__S, ${ele.sourceClassName}>.${focus.paramName}: $Iso<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Lens<__S, ${ele.sourceClassName}>.${focus.paramName}: $Lens<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.paramName}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Prism<__S, ${ele.sourceClassName}>.${focus.paramName}: $Prism<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.paramName}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Iso<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Iso<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Lens<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Lens<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Prism<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Prism<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.escapedParamName}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.escapedParamName}
|
""".trimMargin()
}
Expand All @@ -102,11 +102,11 @@ private fun processIsoSyntax(ele: ADT, dsl: ValueClassDsl, className: String): S
else -> focus.refinedArguments.joinToString(separator = ",")
}
"""
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Iso<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Iso<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Lens<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Lens<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Prism<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Prism<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Iso<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Iso<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Lens<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Lens<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Prism<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Prism<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.escapedParamName}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.escapedParamName}()
|
""".trimMargin()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,14 @@ private fun OpticsProcessorOptions.processElement(adt: ADT, focus: Focus): Strin
val sourceClassNameWithParams = "${adt.sourceClassName}${adt.angledTypeParameters}"
val firstLine = when {
adt.typeParameters.isEmpty() ->
"${adt.visibilityModifierName} $inlineText val ${adt.sourceClassName}.Companion.${focus.paramName}: $Iso<${adt.sourceClassName}, ${focus.className}> $inlineText get()"
"${adt.visibilityModifierName} $inlineText val ${adt.sourceClassName}.Companion.${focus.escapedParamName}: $Iso<${adt.sourceClassName}, ${focus.className}> $inlineText get()"
else ->
"${adt.visibilityModifierName} $inlineText fun ${adt.angledTypeParameters} ${adt.sourceClassName}.Companion.${focus.paramName}(): $Iso<$sourceClassNameWithParams, ${focus.className}>"
"${adt.visibilityModifierName} $inlineText fun ${adt.angledTypeParameters} ${adt.sourceClassName}.Companion.${focus.escapedParamName}(): $Iso<$sourceClassNameWithParams, ${focus.className}>"
}
val fineParamName = focus.paramName.plusIfNotBlank(
prefix = "`",
postfix = "`",
)
return """
|$firstLine = $Iso(
| get = { ${adt.sourceName}: $sourceClassNameWithParams -> ${adt.sourceName}.$fineParamName },
| reverseGet = { $fineParamName: ${focus.className} -> ${adt.sourceClassName}($fineParamName) }
| get = { ${adt.sourceName}: $sourceClassNameWithParams -> ${adt.sourceName}.${focus.escapedParamName} },
| reverseGet = { ${focus.escapedParamName}: ${focus.className} -> ${adt.sourceClassName}(${focus.escapedParamName}) }
|)
|
""".trimMargin()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,13 @@ private fun OpticsProcessorOptions.processElement(adt: ADT, foci: List<Focus>):
return foci.joinToString(separator = "\n") { focus ->
val firstLine = when {
adt.typeParameters.isEmpty() ->
"${adt.visibilityModifierName} $inlineText val ${adt.sourceClassName}.Companion.${focus.lensParamName()}: $Lens<${adt.sourceClassName}, ${focus.className}> $inlineText get()"
"${adt.visibilityModifierName} $inlineText val ${adt.sourceClassName}.Companion.${focus.escapedParamName}: $Lens<${adt.sourceClassName}, ${focus.className}> $inlineText get()"
else ->
"${adt.visibilityModifierName} $inlineText fun ${adt.angledTypeParameters} ${adt.sourceClassName}.Companion.${focus.lensParamName()}(): $Lens<$sourceClassNameWithParams, ${focus.className}>"
"${adt.visibilityModifierName} $inlineText fun ${adt.angledTypeParameters} ${adt.sourceClassName}.Companion.${focus.escapedParamName}(): $Lens<$sourceClassNameWithParams, ${focus.className}>"
}
"""
|$firstLine = $Lens(
| get = { ${adt.sourceName}: $sourceClassNameWithParams -> ${adt.sourceName}.${
focus.paramName.plusIfNotBlank(
prefix = "`",
postfix = "`",
)
} },
| get = { ${adt.sourceName}: $sourceClassNameWithParams -> ${adt.sourceName}.${focus.escapedParamName} },
| set = { ${adt.sourceName}: $sourceClassNameWithParams, value: ${focus.className} ->
| ${setBody(focus)}
|}
Expand All @@ -54,5 +49,3 @@ private fun OpticsProcessorOptions.processElement(adt: ADT, foci: List<Focus>):
""".trimMargin()
}
}

fun Focus.lensParamName(): String = paramName
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ private fun OpticsProcessorOptions.processElement(ele: ADT, foci: List<Focus>):
}
val firstLine = when {
ele.typeParameters.isEmpty() ->
"${ele.visibilityModifierName} $inlineText val ${ele.sourceClassName}.Companion.${focus.paramName}: $Prism<${ele.sourceClassName}, ${focus.classNameWithParameters}> $inlineText get()"
"${ele.visibilityModifierName} $inlineText val ${ele.sourceClassName}.Companion.${focus.escapedParamName}: $Prism<${ele.sourceClassName}, ${focus.classNameWithParameters}> $inlineText get()"
else ->
"${ele.visibilityModifierName} $inlineText fun $angledTypeParameters ${ele.sourceClassName}.Companion.${focus.paramName}(): $Prism<$sourceClassNameWithParams, ${focus.classNameWithParameters}>"
"${ele.visibilityModifierName} $inlineText fun $angledTypeParameters ${ele.sourceClassName}.Companion.${focus.escapedParamName}(): $Prism<$sourceClassNameWithParams, ${focus.classNameWithParameters}>"
}
"$firstLine = $Prism.instanceOf()"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package arrow.optics.plugin

import arrow.optics.plugin.internals.noCompanion
import kotlin.test.Ignore
import kotlin.test.Test

class IsoTests {
Expand All @@ -20,6 +21,31 @@ class IsoTests {
""".evals("r" to true)
}

@Test
fun `Isos will be generated for value class with parameters having keywords as names`() {
"""
|$`package`
|$imports
|@optics @JvmInline
|value class IsoData(
| val `in`: String
|) { companion object }
""".compilationSucceeds()
}

@Test
@Ignore("Needs fixing joinedTypeParams in processIsoSyntax function")
fun `Isos will be generated for generic value class with parameters having keywords as names`() {
"""
|$`package`
|$imports
|@optics @JvmInline
|value class IsoData<T>(
| val `in`: T
|) { companion object }
""".compilationSucceeds()
}

@Test
fun `Iso generation requires companion object declaration`() {
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ class LensTests {
""".evals("r" to true)
}

@Test
fun `Lenses will be generated for data class with parameters having keywords as names`() {
"""
|$`package`
|$imports
|@optics
|data class LensData(
| val `in`: String
|) { companion object }
""".compilationSucceeds()
}

@Test
fun `Lenses will be generated for generic data class with parameters having keywords as names`() {
"""
|$`package`
|$imports
|@optics
|data class LensData<T>(
| val `in`: T
|) { companion object }
""".compilationSucceeds()
}

@Test
fun `Lenses will be generated for data class with secondary constructors`() {
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,36 @@ class PrismTests {
""".compilationSucceeds(allWarningsAsErrors = true)
}

@Test
fun `Prism will be generated for sealed class and subclasses having keywords as names`() {
"""
|$`package`
|$imports
|@optics
|sealed class PrismSealed(val field: String, val nullable: String?) {
| data class In(private val a: String?) : PrismSealed("", a)
| data class PrismSealed2(private val b: String?) : PrismSealed("", b)
| companion object
|}
|val i: Prism<PrismSealed, PrismSealed.In> = PrismSealed.`in`
""".compilationSucceeds(allWarningsAsErrors = true)
}

@Test
fun `Prism will be generated for generic sealed class and subclasses having keywords as names`() {
"""
|$`package`
|$imports
|@optics
|sealed class PrismSealed<A,B>(val field: A, val nullable: B?) {
| data class In(private val a: String?) : PrismSealed<String, String>("", a)
| data class PrismSealed2<C>(private val b: C?) : PrismSealed<String, C>("", b)
| companion object
|}
|val i: Prism<PrismSealed<String, String>, PrismSealed.In> = PrismSealed.`in`()
""".compilationSucceeds(allWarningsAsErrors = true)
}

@Test
fun `Prism will be generated without warning for sealed class with only one subclass`() {
"""
Expand Down

0 comments on commit 796e3b6

Please sign in to comment.