diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.kt b/compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.kt new file mode 100644 index 00000000000..2a75d6d303b --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.kt @@ -0,0 +1,6 @@ +//ALLOW_AST_ACCESS +package test + +annotation class Anno(val value: String) + +class Constructor [Anno(value = "string")]() diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.txt b/compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.txt new file mode 100644 index 00000000000..037859eaf04 --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.txt @@ -0,0 +1,11 @@ +package test + +internal final annotation class Anno : kotlin.Annotation { + /*primary*/ public constructor Anno(/*0*/ value: kotlin.String) + internal final val value: kotlin.String + internal final fun (): kotlin.String +} + +internal final class Constructor { + /*primary*/ test.Anno(value = "string": kotlin.String) public constructor Constructor() +} diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.kt b/compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.kt new file mode 100644 index 00000000000..0b96e11ed56 --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.kt @@ -0,0 +1,10 @@ +//ALLOW_AST_ACCESS +package test + +annotation class A(val s: String) + +class Outer { + class Nested([A("nested")] val x: String) + + inner class Inner([A("inner")] val y: String) +} diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.txt b/compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.txt new file mode 100644 index 00000000000..9f81c2d6ca8 --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.txt @@ -0,0 +1,23 @@ +package test + +internal final annotation class A : kotlin.Annotation { + /*primary*/ public constructor A(/*0*/ s: kotlin.String) + internal final val s: kotlin.String + internal final fun (): kotlin.String +} + +internal final class Outer { + /*primary*/ public constructor Outer() + + internal final inner class Inner { + /*primary*/ public constructor Inner(/*0*/ test.A(s = "inner": kotlin.String) y: kotlin.String) + test.A(s = "inner": kotlin.String) internal final val y: kotlin.String + internal final fun (): kotlin.String + } + + internal final class Nested { + /*primary*/ public constructor Nested(/*0*/ test.A(s = "nested": kotlin.String) x: kotlin.String) + test.A(s = "nested": kotlin.String) internal final val x: kotlin.String + internal final fun (): kotlin.String + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index cc37c22efce..206a9f75f75 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -1939,6 +1939,12 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledKotlin(fileName); } + @TestMetadata("Constructor.kt") + public void testConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.kt"); + doTestCompiledKotlin(fileName); + } + @TestMetadata("DelegatedProperty.kt") public void testDelegatedProperty() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/DelegatedProperty.kt"); @@ -2176,6 +2182,12 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledKotlin(fileName); } + @TestMetadata("InnerClassConstructor.kt") + public void testInnerClassConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.kt"); + doTestCompiledKotlin(fileName); + } + @TestMetadata("ManyAnnotations.kt") public void testManyAnnotations() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/parameters/ManyAnnotations.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index a4985c8947d..6660595d637 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -113,6 +113,12 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD doTest(fileName); } + @TestMetadata("Constructor.kt") + public void testConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.kt"); + doTest(fileName); + } + @TestMetadata("DelegatedProperty.kt") public void testDelegatedProperty() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/DelegatedProperty.kt"); @@ -350,6 +356,12 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD doTest(fileName); } + @TestMetadata("InnerClassConstructor.kt") + public void testInnerClassConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.kt"); + doTest(fileName); + } + @TestMetadata("ManyAnnotations.kt") public void testManyAnnotations() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/parameters/ManyAnnotations.kt"); diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/ReflectKotlinClass.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/ReflectKotlinClass.kt index d718ea8a953..355a483d473 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/ReflectKotlinClass.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/ReflectKotlinClass.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.load.kotlin.reflect import org.jetbrains.kotlin.load.java.structure.reflect.classId +import org.jetbrains.kotlin.load.java.structure.reflect.isEnumClassOrSpecializedEnumEntryClass import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor @@ -105,20 +106,33 @@ private object ReflectClassStructure { private fun loadConstructorAnnotations(klass: Class<*>, memberVisitor: KotlinJvmBinaryClass.MemberVisitor) { for (constructor in klass.getDeclaredConstructors()) { - // TODO: load annotations on constructors val visitor = memberVisitor.visitMethod(Name.special(""), SignatureSerializer.constructorDesc(constructor)) ?: continue - // Constructors of enums have 2 additional synthetic parameters - // TODO: the similar logic should probably be present for annotations on parameters of inner class constructors - val shift = if (klass.isEnum()) 2 else 0 - for ((parameterIndex, annotations) in constructor.getParameterAnnotations().withIndex()) { - for (annotation in annotations) { - val annotationType = annotation.annotationType() - visitor.visitParameterAnnotation(parameterIndex + shift, annotationType.classId)?.let { - processAnnotationArguments(it, annotation, annotationType) + for (annotation in constructor.getDeclaredAnnotations()) { + processAnnotation(visitor, annotation) + } + + val parameterAnnotations = constructor.getParameterAnnotations() + if (parameterAnnotations.isNotEmpty()) { + // Constructors of some classes have additional synthetic parameters: + // - inner classes have one parameter, instance of the outer class + // - enum classes have two parameters, String name and int ordinal + // - local/anonymous classes may have many parameters for captured values + // At the moment this seems like a working heuristic for computing number of synthetic parameters for Kotlin classes, + // although this is wrong and likely to change, see KT-6886 + val shift = constructor.getParameterTypes().size() - parameterAnnotations.size() + + for ((parameterIndex, annotations) in parameterAnnotations.withIndex()) { + for (annotation in annotations) { + val annotationType = annotation.annotationType() + visitor.visitParameterAnnotation(parameterIndex + shift, annotationType.classId)?.let { + processAnnotationArguments(it, annotation, annotationType) + } } } } + + visitor.visitEnd() } } @@ -158,8 +172,8 @@ private object ReflectClassStructure { clazz in TYPES_ELIGIBLE_FOR_SIMPLE_VISIT -> { visitor.visit(name, value) } - javaClass>().isAssignableFrom(clazz) -> { - // isEnum returns false for specialized enum constants (enum entries which are subclasses) + clazz.isEnumClassOrSpecializedEnumEntryClass() -> { + // isEnum returns false for specialized enum constants (enum entries which are anonymous enum subclasses) val classId = (if (clazz.isEnum()) clazz else clazz.getEnclosingClass()).classId visitor.visitEnum(name, classId, Name.identifier((value as Enum<*>).name())) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java index 48f65fb53c0..598665307e4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java @@ -107,6 +107,12 @@ public class ResolveByStubTestGenerated extends AbstractResolveByStubTest { doTest(fileName); } + @TestMetadata("Constructor.kt") + public void testConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Constructor.kt"); + doTest(fileName); + } + @TestMetadata("DelegatedProperty.kt") public void testDelegatedProperty() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/DelegatedProperty.kt"); @@ -344,6 +350,12 @@ public class ResolveByStubTestGenerated extends AbstractResolveByStubTest { doTest(fileName); } + @TestMetadata("InnerClassConstructor.kt") + public void testInnerClassConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/parameters/InnerClassConstructor.kt"); + doTest(fileName); + } + @TestMetadata("ManyAnnotations.kt") public void testManyAnnotations() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/parameters/ManyAnnotations.kt");