diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/java/FirTypeEnhancementTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/java/FirTypeEnhancementTestGenerated.java index 6ec87ea5fdd..2593d98d8cc 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/java/FirTypeEnhancementTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/java/FirTypeEnhancementTestGenerated.java @@ -431,6 +431,11 @@ public class FirTypeEnhancementTestGenerated extends AbstractFirTypeEnhancementT runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumArgument.java"); } + @TestMetadata("NestedEnumInAnnotation.java") + public void testNestedEnumInAnnotation() throws Exception { + runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java"); + } + @TestMetadata("PrimitiveValueInParam.java") public void testPrimitiveValueInParam() throws Exception { runTest("compiler/testData/loadJava/compiledJava/annotations/PrimitiveValueInParam.java"); diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt index 10e3bd893be..a190a16be56 100644 --- a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt @@ -246,7 +246,47 @@ class BinaryJavaAnnotationVisitor( } override fun visitEnum(name: String?, desc: String, value: String) { - val enumClassId = context.mapInternalNameToClassId(Type.getType(desc).internalName) + /** + * There are cases when enum is an inner class of some class which is not related to current loading Java class. + * And in this cases `mapInternalNameToClassId` leaves `$` in name as is, which may lead to unresolved errors later + * in compiler + * + * @Api(status = Api.Status.Ok) // classId will be /Api$Status.Ok + * public class NestedEnumInAnnotation {} + * + * public @interface Api { + * Status status(); + * + * enum Status { + * Ok, Error; + * } + * } + * + * It's impossible to use `resolveByInternalName` (which always provides correct classId), because it may lead to + * StackOverflowError for cases when enum and annotation are declared in same outer class, which will lead to + * infinite loading of this class + * + * public class NestedEnumArgument { + * public enum E { + * FIRST + * } + * + * public @interface Anno { + * E value(); + * } + * + * @Anno(E.FIRST) + * void foo() {} + * } + * + * So to avoid such recursion and in the same time fix original case we use simple heuristic about names with $ + * for enums in annotation arguments which contain `$` in internal name + */ + val internalName = Type.getType(desc).internalName + var enumClassId = context.mapInternalNameToClassId(internalName) + if (enumClassId.asString().contains("$")) { + enumClassId = context.convertNestedClassInternalNameWithSimpleHeuristic(internalName) ?: enumClassId + } addArgument(PlainJavaEnumValueAnnotationArgument(name, enumClassId, value)) } diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt index 681d135f2bf..3c0c50cfec6 100644 --- a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt @@ -73,7 +73,7 @@ class ClassifierResolutionContext private constructor( } // See com.intellij.psi.impl.compiled.StubBuildingVisitor.GUESSING_MAPPER - private fun convertNestedClassInternalNameWithSimpleHeuristic(internalName: String): ClassId? { + internal fun convertNestedClassInternalNameWithSimpleHeuristic(internalName: String): ClassId? { val splitPoints = SmartList() for (p in internalName.indices) { val c = internalName[p] diff --git a/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.fir.txt b/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.fir.txt new file mode 100644 index 00000000000..c09d95eb26a --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.fir.txt @@ -0,0 +1,21 @@ +public final annotation class Api : R|kotlin/Annotation| { + public constructor(status: R|test/Api.Status|): R|test/Api| + + public final enum class Status : R|kotlin/Enum| { + public final static enum entry Ok: R|@EnhancedNullability test/Api.Status| + public final static enum entry Error: R|@EnhancedNullability test/Api.Status| + public final static fun values(): R|kotlin/Array| { + } + + public final static fun valueOf(value: R|kotlin/String|): R|test/Api.Status| { + } + + public final static val entries: R|kotlin/enums/EnumEntries| + public get(): R|kotlin/enums/EnumEntries| + + } +} +@R|test/Api|(status = R|test/Api.Status.Ok|()) public open class NestedEnumInAnnotation : R|kotlin/Any| { + public constructor(): R|test/NestedEnumInAnnotation| + +} diff --git a/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java b/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java new file mode 100644 index 00000000000..c129cf6e77b --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java @@ -0,0 +1,17 @@ +// FILE: NestedEnumInAnnotation.java + +package test; + +@Api(status = Api.Status.Ok) +public class NestedEnumInAnnotation {} + +// FILE: Api.java +package test; + +public @interface Api { + Status status(); + + enum Status { + Ok, Error; + } +} diff --git a/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.txt b/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.txt new file mode 100644 index 00000000000..3677e77ad3d --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.txt @@ -0,0 +1,29 @@ +package test + +public final annotation class Api : kotlin.Annotation { + public constructor Api(/*0*/ status: test.Api.Status) + public final val status: test.Api.Status + + public final enum class Status : kotlin.Enum { + enum entry Ok + + enum entry Error + + private constructor Status() + @kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Api.Status!): kotlin.Int + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + + // Static members + public final /*synthesized*/ val entries: kotlin.enums.EnumEntries + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): test.Api.Status + public final /*synthesized*/ fun values(): kotlin.Array + } +} + +@test.Api(status = Status.Ok) public open class NestedEnumInAnnotation { + public constructor NestedEnumInAnnotation() +} diff --git a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index 39f650537ba..77c942128d3 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -433,6 +433,11 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumArgument.java"); } + @TestMetadata("NestedEnumInAnnotation.java") + public void testNestedEnumInAnnotation() throws Exception { + runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java"); + } + @TestMetadata("PrimitiveValueInParam.java") public void testPrimitiveValueInParam() throws Exception { runTest("compiler/testData/loadJava/compiledJava/annotations/PrimitiveValueInParam.java"); diff --git a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaWithPsiClassReadingTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaWithPsiClassReadingTestGenerated.java index cd2e6876cb1..1fc6f0b6ad0 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaWithPsiClassReadingTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/LoadJavaWithPsiClassReadingTestGenerated.java @@ -431,6 +431,11 @@ public class LoadJavaWithPsiClassReadingTestGenerated extends AbstractLoadJavaWi runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumArgument.java"); } + @TestMetadata("NestedEnumInAnnotation.java") + public void testNestedEnumInAnnotation() throws Exception { + runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java"); + } + @TestMetadata("PrimitiveValueInParam.java") public void testPrimitiveValueInParam() throws Exception { runTest("compiler/testData/loadJava/compiledJava/annotations/PrimitiveValueInParam.java"); diff --git a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/ir/IrLoadJavaTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/ir/IrLoadJavaTestGenerated.java index e21a651a63a..f55bc8f8042 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/ir/IrLoadJavaTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/ir/IrLoadJavaTestGenerated.java @@ -434,6 +434,11 @@ public class IrLoadJavaTestGenerated extends AbstractIrLoadJavaTest { runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumArgument.java"); } + @TestMetadata("NestedEnumInAnnotation.java") + public void testNestedEnumInAnnotation() throws Exception { + runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java"); + } + @TestMetadata("PrimitiveValueInParam.java") public void testPrimitiveValueInParam() throws Exception { runTest("compiler/testData/loadJava/compiledJava/annotations/PrimitiveValueInParam.java"); diff --git a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java index ec1c250a5bc..f1008179c45 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java @@ -433,6 +433,11 @@ public class LoadJavaUsingJavacTestGenerated extends AbstractLoadJavaUsingJavacT runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumArgument.java"); } + @TestMetadata("NestedEnumInAnnotation.java") + public void testNestedEnumInAnnotation() throws Exception { + runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java"); + } + @TestMetadata("PrimitiveValueInParam.java") public void testPrimitiveValueInParam() throws Exception { runTest("compiler/testData/loadJava/compiledJava/annotations/PrimitiveValueInParam.java"); diff --git a/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index b87ee716fe5..cfbe74541e7 100644 --- a/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -3279,6 +3279,11 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumArgument.java"); } + @TestMetadata("NestedEnumInAnnotation.java") + public void testNestedEnumInAnnotation() throws Exception { + runTest("compiler/testData/loadJava/compiledJava/annotations/NestedEnumInAnnotation.java"); + } + @TestMetadata("PrimitiveValueInParam.java") public void testPrimitiveValueInParam() throws Exception { runTest("compiler/testData/loadJava/compiledJava/annotations/PrimitiveValueInParam.java"); diff --git a/plugins/noarg/testData/box/javaAnnotationWithInnerEnum.kt b/plugins/noarg/testData/box/javaAnnotationWithInnerEnum.kt new file mode 100644 index 00000000000..410e5f1e4fe --- /dev/null +++ b/plugins/noarg/testData/box/javaAnnotationWithInnerEnum.kt @@ -0,0 +1,30 @@ +// WITH_STDLIB +// ISSUE: KT-55887 + +// MODULE: lib +// FILE: NoArg.kt +annotation class NoArg + +// FILE: Api.java +@NoArg +public @interface Api { + Status status(); + + enum Status { + Ok, Error; + } +} + +// FILE: ExtendWith.java +@Api(status = Api.Status.Ok) +public @interface ExtendWith {} + +// MODULE: main(lib) +// FILE: main.kt +@ExtendWith +class Test(val x: Int) + +fun box(): String { + Test::class.java.newInstance() + return "OK" +} diff --git a/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/BlackBoxCodegenTestForNoArgGenerated.java b/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/BlackBoxCodegenTestForNoArgGenerated.java index 8aad1352f99..db9b3d85811 100644 --- a/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/BlackBoxCodegenTestForNoArgGenerated.java +++ b/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/BlackBoxCodegenTestForNoArgGenerated.java @@ -37,6 +37,12 @@ public class BlackBoxCodegenTestForNoArgGenerated extends AbstractBlackBoxCodege runTest("plugins/noarg/testData/box/initializersWithoutInvokeInitializers.kt"); } + @Test + @TestMetadata("javaAnnotationWithInnerEnum.kt") + public void testJavaAnnotationWithInnerEnum() throws Exception { + runTest("plugins/noarg/testData/box/javaAnnotationWithInnerEnum.kt"); + } + @Test @TestMetadata("kt18245.kt") public void testKt18245() throws Exception { diff --git a/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/FirBlackBoxCodegenTestForNoArgGenerated.java b/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/FirBlackBoxCodegenTestForNoArgGenerated.java index e257e2faf76..2c5f6e5ede0 100644 --- a/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/FirBlackBoxCodegenTestForNoArgGenerated.java +++ b/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/FirBlackBoxCodegenTestForNoArgGenerated.java @@ -37,6 +37,12 @@ public class FirBlackBoxCodegenTestForNoArgGenerated extends AbstractFirBlackBox runTest("plugins/noarg/testData/box/initializersWithoutInvokeInitializers.kt"); } + @Test + @TestMetadata("javaAnnotationWithInnerEnum.kt") + public void testJavaAnnotationWithInnerEnum() throws Exception { + runTest("plugins/noarg/testData/box/javaAnnotationWithInnerEnum.kt"); + } + @Test @TestMetadata("kt18245.kt") public void testKt18245() throws Exception { diff --git a/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/IrBlackBoxCodegenTestForNoArgGenerated.java b/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/IrBlackBoxCodegenTestForNoArgGenerated.java index 4123dc247b0..80848505d87 100644 --- a/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/IrBlackBoxCodegenTestForNoArgGenerated.java +++ b/plugins/noarg/tests-gen/org/jetbrains/kotlin/noarg/IrBlackBoxCodegenTestForNoArgGenerated.java @@ -37,6 +37,12 @@ public class IrBlackBoxCodegenTestForNoArgGenerated extends AbstractIrBlackBoxCo runTest("plugins/noarg/testData/box/initializersWithoutInvokeInitializers.kt"); } + @Test + @TestMetadata("javaAnnotationWithInnerEnum.kt") + public void testJavaAnnotationWithInnerEnum() throws Exception { + runTest("plugins/noarg/testData/box/javaAnnotationWithInnerEnum.kt"); + } + @Test @TestMetadata("kt18245.kt") public void testKt18245() throws Exception {