Kapt: Don't convert field initializers for enum fields inside companion objects (KT-37732)

For classes with companion objects, Kotlin compiler generates a 'Companion' static accessor field.
Java prioritizes fields over inner types (apparently, Scala does this as well, KT-29864), so the generated initializer doesn't compile.
As a workaround, initializer generatation is disabled for enum fields inside companion objects. Certainly, it's not a proper fix, however it does fix the regression.
This commit is contained in:
Yan Zhulanow
2020-09-25 01:34:53 +09:00
parent 86ac44c23e
commit a44fd964cf
5 changed files with 253 additions and 0 deletions
@@ -60,10 +60,12 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.typeUtil.isEnum
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.*
@@ -668,6 +670,18 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
}
val propertyType = (origin?.descriptor as? PropertyDescriptor)?.returnType
/*
Work-around for enum classes in companions.
In expressions "Foo.Companion.EnumClass", Java prefers static field over a type name, making the reference invalid.
*/
if (propertyType != null && propertyType.isEnum()) {
val enumClass = propertyType.constructor.declarationDescriptor
if (enumClass is ClassDescriptor && enumClass.isInsideCompanionObject()) {
return null
}
}
if (propertyInitializer != null && propertyType != null) {
val constValue = getConstantValue(propertyInitializer, propertyType)
if (constValue != null) {
@@ -686,6 +700,15 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
return null
}
private fun DeclarationDescriptor.isInsideCompanionObject(): Boolean {
val parent = containingDeclaration ?: return false
if (parent.isCompanionObject()) {
return true
}
return parent.isInsideCompanionObject()
}
private object UnknownConstantValue
private fun getConstantValue(expression: KtExpression, expectedType: KotlinType): ConstantValue<*>? {
@@ -108,6 +108,11 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
runTest("plugins/kapt3/kapt3-compiler/testData/converter/enumImports.kt");
}
@TestMetadata("enumInCompanion.kt")
public void testEnumInCompanion() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.kt");
}
@TestMetadata("enums.kt")
public void testEnums() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/enums.kt");
@@ -109,6 +109,11 @@ public class IrClassFileToSourceStubConverterTestGenerated extends AbstractIrCla
runTest("plugins/kapt3/kapt3-compiler/testData/converter/enumImports.kt");
}
@TestMetadata("enumInCompanion.kt")
public void testEnumInCompanion() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.kt");
}
@TestMetadata("enums.kt")
public void testEnums() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/enums.kt");
@@ -0,0 +1,45 @@
class Test {
private val foo = Example.FOO
companion object {
enum class Example { FOO }
}
}
class Test2 {
private val foo = Example.FOO
companion object Amigo {
enum class Example { FOO }
}
}
class Test3 {
private val foo = Amigo.Example.FOO
object Amigo {
enum class Example { FOO }
}
}
class Test4 {
private val foo = Foo.constProperty
companion object {
object Foo {
const val constProperty = 1
}
}
}
class Test5 {
private val foo = Amigos.Companion.Goo.Example.FOO
class Amigos {
companion object {
class Goo {
enum class Example { FOO }
}
}
}
}
@@ -0,0 +1,175 @@
import java.lang.System;
@kotlin.Metadata()
public final class Test {
private final Test.Companion.Example foo;
@org.jetbrains.annotations.NotNull()
public static final Test.Companion Companion = null;
public Test() {
super();
}
@kotlin.Metadata()
public static final class Companion {
private Companion() {
super();
}
@kotlin.Metadata()
public static enum Example {
/*public static final*/ FOO /* = new Example() */;
Example() {
}
}
}
}
////////////////////
import java.lang.System;
@kotlin.Metadata()
public final class Test2 {
private final Test2.Amigo.Example foo;
@org.jetbrains.annotations.NotNull()
public static final Test2.Amigo Amigo = null;
public Test2() {
super();
}
@kotlin.Metadata()
public static final class Amigo {
private Amigo() {
super();
}
@kotlin.Metadata()
public static enum Example {
/*public static final*/ FOO /* = new Example() */;
Example() {
}
}
}
}
////////////////////
import java.lang.System;
@kotlin.Metadata()
public final class Test3 {
private final Test3.Amigo.Example foo = Test3.Amigo.Example.FOO;
public Test3() {
super();
}
@kotlin.Metadata()
public static final class Amigo {
@org.jetbrains.annotations.NotNull()
public static final Test3.Amigo INSTANCE = null;
private Amigo() {
super();
}
@kotlin.Metadata()
public static enum Example {
/*public static final*/ FOO /* = new Example() */;
Example() {
}
}
}
}
////////////////////
import java.lang.System;
@kotlin.Metadata()
public final class Test4 {
private final int foo = 1;
@org.jetbrains.annotations.NotNull()
public static final Test4.Companion Companion = null;
public Test4() {
super();
}
@kotlin.Metadata()
public static final class Companion {
private Companion() {
super();
}
@kotlin.Metadata()
public static final class Foo {
public static final int constProperty = 1;
@org.jetbrains.annotations.NotNull()
public static final Test4.Companion.Foo INSTANCE = null;
private Foo() {
super();
}
}
}
}
////////////////////
import java.lang.System;
@kotlin.Metadata()
public final class Test5 {
private final Test5.Amigos.Companion.Goo.Example foo;
public Test5() {
super();
}
@kotlin.Metadata()
public static final class Amigos {
@org.jetbrains.annotations.NotNull()
public static final Test5.Amigos.Companion Companion = null;
public Amigos() {
super();
}
@kotlin.Metadata()
public static final class Companion {
private Companion() {
super();
}
@kotlin.Metadata()
public static final class Goo {
public Goo() {
super();
}
@kotlin.Metadata()
public static enum Example {
/*public static final*/ FOO /* = new Example() */;
Example() {
}
}
}
}
}
}