mirror of
https://github.com/apple/pkl.git
synced 2026-03-26 11:01:14 +01:00
codegen-kotlin: Fix generation of copy() methods (#705)
- don't generate copy methods for abstract classes - precisely determine if copy method needs override modifier (tricky) Fixes #569
This commit is contained in:
@@ -274,9 +274,20 @@ class KotlinCodeGenerator(
|
||||
return methodBuilder.addCode(codeBuilder.build()).build()
|
||||
}
|
||||
|
||||
fun inheritsCopyMethodWithSameArity(): Boolean {
|
||||
val nearestNonAbstractAncestor =
|
||||
generateSequence(pClass.superclass) { it.superclass }.firstOrNull { !it.isAbstract }
|
||||
?: return false
|
||||
return nearestNonAbstractAncestor.allProperties.values.count { !it.isHidden } ==
|
||||
allProperties.size
|
||||
}
|
||||
|
||||
// besides generating copy method for current class,
|
||||
// override copy methods inherited from parent classes
|
||||
fun generateCopyMethods(typeBuilder: TypeSpec.Builder) {
|
||||
// copy methods don't make sense for abstract classes
|
||||
if (pClass.isAbstract) return
|
||||
|
||||
var prevParameterCount = Int.MAX_VALUE
|
||||
for (currClass in generateSequence(pClass) { it.superclass }) {
|
||||
if (currClass.isAbstract) continue
|
||||
@@ -285,7 +296,7 @@ class KotlinCodeGenerator(
|
||||
|
||||
// avoid generating multiple methods with same no. of parameters
|
||||
if (currParameters.size < prevParameterCount) {
|
||||
val isOverride = currClass !== pClass || superclass != null && properties.isEmpty()
|
||||
val isOverride = currClass !== pClass || inheritsCopyMethodWithSameArity()
|
||||
typeBuilder.addFunction(generateCopyMethod(currParameters, isOverride))
|
||||
prevParameterCount = currParameters.size
|
||||
}
|
||||
|
||||
@@ -661,6 +661,211 @@ class KotlinCodeGeneratorTest {
|
||||
assertCompilesSuccessfully(kotlinCode)
|
||||
}
|
||||
|
||||
// https://github.com/apple/pkl/issues/569
|
||||
@Test
|
||||
fun `abstract classes`() {
|
||||
val kotlinCode =
|
||||
generateKotlinCode(
|
||||
"""
|
||||
module my.mod
|
||||
|
||||
abstract class Foo { one: Int }
|
||||
abstract class Bar extends Foo { two: String }
|
||||
class Baz extends Bar { three: Duration }
|
||||
class Qux extends Bar {}
|
||||
"""
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| abstract class Foo(
|
||||
| open val one: Long
|
||||
| ) {
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| abstract class Bar(
|
||||
| one: Long,
|
||||
| open val two: String
|
||||
| ) : Foo(one) {
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| class Baz(
|
||||
| one: Long,
|
||||
| two: String,
|
||||
| val three: Duration
|
||||
| ) : Bar(one, two) {
|
||||
| fun copy(
|
||||
| one: Long = this.one,
|
||||
| two: String = this.two,
|
||||
| three: Duration = this.three
|
||||
| ): Baz = Baz(one, two, three)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| class Qux(
|
||||
| one: Long,
|
||||
| two: String
|
||||
| ) : Bar(one, two) {
|
||||
| fun copy(one: Long = this.one, two: String = this.two): Qux = Qux(one, two)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertCompilesSuccessfully(kotlinCode)
|
||||
}
|
||||
|
||||
// https://github.com/apple/pkl/issues/569
|
||||
@Test
|
||||
fun `abstract class that extends open class`() {
|
||||
val kotlinCode =
|
||||
generateKotlinCode(
|
||||
"""
|
||||
module my.mod
|
||||
|
||||
open class Foo { one: Int }
|
||||
abstract class Bar extends Foo { two: String }
|
||||
class Baz extends Bar { three: Duration }
|
||||
class Qux extends Bar {}
|
||||
"""
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| open class Foo(
|
||||
| open val one: Long
|
||||
| ) {
|
||||
| open fun copy(one: Long = this.one): Foo = Foo(one)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| abstract class Bar(
|
||||
| one: Long,
|
||||
| open val two: String
|
||||
| ) : Foo(one) {
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| class Baz(
|
||||
| one: Long,
|
||||
| two: String,
|
||||
| val three: Duration
|
||||
| ) : Bar(one, two) {
|
||||
| fun copy(
|
||||
| one: Long = this.one,
|
||||
| two: String = this.two,
|
||||
| three: Duration = this.three
|
||||
| ): Baz = Baz(one, two, three)
|
||||
|
|
||||
| override fun copy(one: Long): Baz = Baz(one, two, three)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| class Qux(
|
||||
| one: Long,
|
||||
| two: String
|
||||
| ) : Bar(one, two) {
|
||||
| fun copy(one: Long = this.one, two: String = this.two): Qux = Qux(one, two)
|
||||
|
|
||||
| override fun copy(one: Long): Qux = Qux(one, two)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertCompilesSuccessfully(kotlinCode)
|
||||
}
|
||||
|
||||
// https://github.com/apple/pkl/issues/569
|
||||
@Test
|
||||
fun `abstract class that extends open class without adding properties`() {
|
||||
val kotlinCode =
|
||||
generateKotlinCode(
|
||||
"""
|
||||
module my.mod
|
||||
|
||||
open class Foo { one: Int }
|
||||
abstract class Bar extends Foo {}
|
||||
class Baz extends Bar { two: Duration }
|
||||
class Qux extends Bar {}
|
||||
"""
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| open class Foo(
|
||||
| open val one: Long
|
||||
| ) {
|
||||
| open fun copy(one: Long = this.one): Foo = Foo(one)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| abstract class Bar(
|
||||
| one: Long
|
||||
| ) : Foo(one) {
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| class Baz(
|
||||
| one: Long,
|
||||
| val two: Duration
|
||||
| ) : Bar(one) {
|
||||
| fun copy(one: Long = this.one, two: Duration = this.two): Baz = Baz(one, two)
|
||||
|
|
||||
| override fun copy(one: Long): Baz = Baz(one, two)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| class Qux(
|
||||
| one: Long
|
||||
| ) : Bar(one) {
|
||||
| override fun copy(one: Long): Qux = Qux(one)
|
||||
"""
|
||||
.trimMargin(),
|
||||
kotlinCode
|
||||
)
|
||||
|
||||
assertCompilesSuccessfully(kotlinCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun keywords() {
|
||||
val props = kotlinKeywords.joinToString("\n") { "`$it`: Int" }
|
||||
|
||||
Reference in New Issue
Block a user