[FIR] Fix loading of classId for nested enums in java annotation arguments

^KT-55887 Fixed
^KT-55976
This commit is contained in:
Dmitriy Novozhilov
2023-01-12 11:11:55 +02:00
committed by Space Team
parent da0dd519d0
commit 259303ca50
15 changed files with 187 additions and 2 deletions
@@ -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");
@@ -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))
}
@@ -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<Int>()
for (p in internalName.indices) {
val c = internalName[p]
@@ -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<test/Api.Status!>| {
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<test/Api.Status>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|test/Api.Status| {
}
public final static val entries: R|kotlin/enums/EnumEntries<test/Api.Status>|
public get(): R|kotlin/enums/EnumEntries<test/Api.Status>|
}
}
@R|test/Api|(status = R|test/Api.Status.Ok|()) public open class NestedEnumInAnnotation : R|kotlin/Any| {
public constructor(): R|test/NestedEnumInAnnotation|
}
@@ -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;
}
}
@@ -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<test.Api.Status!> {
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<test.Api.Status!>!
// Static members
public final /*synthesized*/ val entries: kotlin.enums.EnumEntries<test.Api.Status>
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): test.Api.Status
public final /*synthesized*/ fun values(): kotlin.Array<test.Api.Status>
}
}
@test.Api(status = Status.Ok) public open class NestedEnumInAnnotation {
public constructor NestedEnumInAnnotation()
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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"
}
@@ -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 {
@@ -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 {
@@ -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 {