From a44fd964cf9ff1d9aa083893b31459dcaee3e384 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 25 Sep 2020 01:34:53 +0900 Subject: [PATCH] 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. --- .../stubs/ClassFileToSourceStubConverter.kt | 23 +++ ...ileToSourceStubConverterTestGenerated.java | 5 + ...ileToSourceStubConverterTestGenerated.java | 5 + .../testData/converter/enumInCompanion.kt | 45 +++++ .../testData/converter/enumInCompanion.txt | 175 ++++++++++++++++++ 5 files changed, 253 insertions(+) create mode 100644 plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.kt create mode 100644 plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.txt diff --git a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt index 0b0ab672bed..451ba7d5bf6 100644 --- a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt +++ b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt @@ -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<*>? { diff --git a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java index 66d723b9961..9636f5f5d88 100644 --- a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java @@ -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"); diff --git a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/IrClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/IrClassFileToSourceStubConverterTestGenerated.java index 5c78e74cbb0..82cfc57bef5 100644 --- a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/IrClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/IrClassFileToSourceStubConverterTestGenerated.java @@ -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"); diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.kt b/plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.kt new file mode 100644 index 00000000000..eb480b1e74f --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.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 } + } + } + } +} \ No newline at end of file diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.txt b/plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.txt new file mode 100644 index 00000000000..445dabca9b1 --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/enumInCompanion.txt @@ -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() { + } + } + } + } + } +}