diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt b/compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt new file mode 100644 index 00000000000..72ab25be3a8 --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt @@ -0,0 +1,22 @@ +package test + +target(AnnotationTarget.VALUE_PARAMETER) +annotation class Ann + +class A { + + @receiver:Ann + fun String.myLength(@Ann q:String): Int { + return length() + } + + @receiver:Ann + val String.myLength2: Int + get() = length() + + @receiver:Ann + var String.myLength3: Int + get() = length() + set(v) {} + +} \ No newline at end of file diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.txt b/compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.txt new file mode 100644 index 00000000000..2d708993bbf --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.txt @@ -0,0 +1,15 @@ +package test + +internal final class A { + /*primary*/ public constructor A() + @receiver:test.Ann() internal final val kotlin.String.myLength2: kotlin.Int + internal final fun kotlin.String.(): kotlin.Int + @receiver:test.Ann() internal final var kotlin.String.myLength3: kotlin.Int + internal final fun kotlin.String.(): kotlin.Int + internal final fun kotlin.String.(/*0*/ v: kotlin.Int): kotlin.Unit + @receiver:test.Ann() internal final fun kotlin.String.myLength(/*0*/ test.Ann() q: kotlin.String): kotlin.Int +} + +kotlin.annotation.target(allowedTargets = {AnnotationTarget.VALUE_PARAMETER}) kotlin.annotation.annotation() internal final class Ann : kotlin.Annotation { + /*primary*/ public constructor Ann() +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index 5b280378d8f..23c5a277345 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -2361,6 +2361,21 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledKotlin(fileName); } } + + @TestMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WithUseSiteTarget extends AbstractLoadJavaTest { + public void testAllFilesPresentInWithUseSiteTarget() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ReceiverTarget.kt") + public void testReceiverTarget() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt"); + doTestCompiledKotlin(fileName); + } + } } @TestMetadata("compiler/testData/loadJava/compiledKotlin/class") diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index 20590cd13c7..370fbfc6451 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -470,6 +470,21 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD doTest(fileName); } } + + @TestMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WithUseSiteTarget extends AbstractJvmRuntimeDescriptorLoaderTest { + public void testAllFilesPresentInWithUseSiteTarget() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ReceiverTarget.kt") + public void testReceiverTarget() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/loadJava/compiledKotlin/class") diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt index 27211782282..a51da420ce0 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt @@ -132,6 +132,22 @@ public abstract class AbstractBinaryClassAnnotationAndConstantLoader { + if (!callable.hasReceiverType()) return emptyList() + val methodSignature = getCallableSignature(callable, nameResolver, kind) + if (methodSignature != null) { + val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, 0) + return findClassAndLoadMemberAnnotations(container, callable, nameResolver, kind, paramSignature) + } + + return emptyList() + } + override fun loadTypeAnnotations(type: ProtoBuf.Type, nameResolver: NameResolver): List { return type.getExtension(JvmProtoBuf.typeAnnotation).map { loadTypeAnnotation(it, nameResolver) } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsAnnotationAndConstantLoader.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsAnnotationAndConstantLoader.kt index dbf23ac2dc0..f91765f4c8b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsAnnotationAndConstantLoader.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsAnnotationAndConstantLoader.kt @@ -58,6 +58,13 @@ class BuiltInsAnnotationAndConstantLoader( return annotations.map { proto -> deserializer.deserializeAnnotation(proto, nameResolver) } } + override fun loadExtensionReceiverParameterAnnotations( + container: ProtoContainer, + callable: ProtoBuf.Callable, + nameResolver: NameResolver, + kind: AnnotatedCallableKind + ): List = emptyList() + override fun loadTypeAnnotations(proto: ProtoBuf.Type, nameResolver: NameResolver): List { // TODO: support type annotations in built-ins return listOf() diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java index 3bddc831d6c..d7720c80b17 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java @@ -47,6 +47,14 @@ public interface AnnotationAndConstantLoader { @NotNull ProtoBuf.Callable.ValueParameter proto ); + @NotNull + List loadExtensionReceiverParameterAnnotations( + @NotNull ProtoContainer container, + @NotNull ProtoBuf.Callable callable, + @NotNull NameResolver nameResolver, + @NotNull AnnotatedCallableKind kind + ); + @NotNull List loadTypeAnnotations( @NotNull ProtoBuf.Type type, diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt index 4b387166b94..7d6944c83d8 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt @@ -17,6 +17,9 @@ package org.jetbrains.kotlin.serialization.deserialization import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl @@ -29,6 +32,7 @@ import org.jetbrains.kotlin.serialization.ProtoBuf.Callable.CallableKind.VAL import org.jetbrains.kotlin.serialization.ProtoBuf.Callable.CallableKind.VAR import org.jetbrains.kotlin.serialization.deserialization.descriptors.* import org.jetbrains.kotlin.utils.toReadOnlyList +import org.jetbrains.kotlin.utils.sure public class MemberDeserializer(private val c: DeserializationContext) { public fun loadCallable(proto: Callable): CallableMemberDescriptor { @@ -43,9 +47,16 @@ public class MemberDeserializer(private val c: DeserializationContext) { private fun loadProperty(proto: Callable): PropertyDescriptor { val flags = proto.getFlags() + val hasGetter = Flags.HAS_GETTER.get(flags) + + val propertyAnnotations = if (hasGetter) + getAnnotationsWithReceiverTargeted(proto, AnnotatedCallableKind.PROPERTY, AnnotatedCallableKind.PROPERTY_GETTER) + else + getAnnotations(proto, flags, AnnotatedCallableKind.PROPERTY) + val property = DeserializedPropertyDescriptor( c.containingDeclaration, null, - getAnnotations(proto, flags, AnnotatedCallableKind.PROPERTY), + propertyAnnotations, modality(Flags.MODALITY.get(flags)), visibility(Flags.VISIBILITY.get(flags)), Flags.CALLABLE_KIND.get(flags) == Callable.CallableKind.VAR, @@ -63,7 +74,7 @@ public class MemberDeserializer(private val c: DeserializationContext) { if (proto.hasReceiverType()) local.typeDeserializer.type(proto.getReceiverType()) else null ) - val getter = if (Flags.HAS_GETTER.get(flags)) { + val getter = if (hasGetter) { val getterFlags = proto.getGetterFlags() val isNotDefault = proto.hasGetterFlags() && Flags.IS_NOT_DEFAULT.get(getterFlags) val getter = if (isNotDefault) { @@ -128,11 +139,13 @@ public class MemberDeserializer(private val c: DeserializationContext) { } private fun loadFunction(proto: Callable): CallableMemberDescriptor { - val annotations = getAnnotations(proto, proto.getFlags(), AnnotatedCallableKind.FUNCTION) + val annotations = getAnnotationsWithReceiverTargeted(proto, AnnotatedCallableKind.FUNCTION) val function = DeserializedSimpleFunctionDescriptor.create(c.containingDeclaration, proto, c.nameResolver, annotations) val local = c.childContext(function, proto.getTypeParameterList()) + val receiverParameterType = if (proto.hasReceiverType()) local.typeDeserializer.type(proto.getReceiverType()) else null + function.initialize( - if (proto.hasReceiverType()) local.typeDeserializer.type(proto.getReceiverType()) else null, + receiverParameterType, getDispatchReceiverParameter(), local.typeDeserializer.ownTypeParameters, local.memberDeserializer.valueParameters(proto, AnnotatedCallableKind.FUNCTION), @@ -163,6 +176,29 @@ public class MemberDeserializer(private val c: DeserializationContext) { return descriptor } + private fun getAnnotationsWithReceiverTargeted( + proto: Callable, + kind: AnnotatedCallableKind, + receiverTargetedKind: AnnotatedCallableKind = kind + ): Annotations { + return DeserializedAnnotationsWithPossibleTargets(c.storageManager) { + val annotations = arrayListOf() + val container = c.containingDeclaration.asProtoContainer() + + annotations += c.components.annotationAndConstantLoader + .loadCallableAnnotations(container, proto, c.nameResolver, kind) + .map { AnnotationWithTarget(it, null) } + + if (proto.hasReceiverType()) { + annotations += c.components.annotationAndConstantLoader + .loadExtensionReceiverParameterAnnotations(container, proto, c.nameResolver, receiverTargetedKind) + .map { AnnotationWithTarget(it, AnnotationUseSiteTarget.RECEIVER) } + } + + annotations + } + } + private fun getAnnotations(proto: Callable, flags: Int, kind: AnnotatedCallableKind): Annotations { if (!Flags.HAS_ANNOTATIONS.get(flags)) { return Annotations.EMPTY diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt index e9dadc99b8a..727e7c7edf0 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt @@ -28,22 +28,32 @@ import org.jetbrains.kotlin.utils.toReadOnlyList class DeserializedAnnotations( storageManager: StorageManager, compute: () -> List +) : DeserializedAnnotationsWithPossibleTargets( + storageManager, + { compute().map { AnnotationWithTarget(it, null) } }) + +open class DeserializedAnnotationsWithPossibleTargets( + storageManager: StorageManager, + compute: () -> List ) : Annotations { private val annotations = storageManager.createLazyValue { compute().toReadOnlyList() } override fun isEmpty(): Boolean = annotations().isEmpty() override fun findAnnotation(fqName: FqName) = annotations().firstOrNull { - annotation -> - val descriptor = annotation.getType().getConstructor().getDeclarationDescriptor() + annotationWithTarget -> + if (annotationWithTarget.target != null) return@firstOrNull false + val descriptor = annotationWithTarget.annotation.type.constructor.declarationDescriptor descriptor is ClassDescriptor && fqName.equalsTo(DescriptorUtils.getFqName(descriptor)) - } + }?.annotation override fun findExternalAnnotation(fqName: FqName) = null - override fun getUseSiteTargetedAnnotations() = emptyList() + override fun getUseSiteTargetedAnnotations() = annotations().filter { it.target != null } - override fun getAllAnnotations() = this.map { AnnotationWithTarget(it, null) } + override fun getAllAnnotations() = annotations() - override fun iterator(): Iterator = annotations().iterator() -} + override fun iterator(): Iterator { + return annotations().asSequence().filter { it.target == null }.map { it.annotation }.iterator() + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt index f97db57b72e..153a371511f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt @@ -49,6 +49,13 @@ public class AnnotationLoaderForKotlinJavaScriptStubBuilder() : AnnotationAndCon ): List = proto.getExtension(JsProtoBuf.parameterAnnotation).orEmpty().map { nameResolver.getClassId(it.getId()) } + override fun loadExtensionReceiverParameterAnnotations( + container: ProtoContainer, + callable: ProtoBuf.Callable, + nameResolver: NameResolver, + kind: AnnotatedCallableKind + ): List = emptyList() + override fun loadTypeAnnotations( proto: ProtoBuf.Type, nameResolver: NameResolver diff --git a/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java index 295e8ef3c35..c11d25d13e7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java @@ -468,6 +468,21 @@ public class ResolveByStubTestGenerated extends AbstractResolveByStubTest { doTest(fileName); } } + + @TestMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WithUseSiteTarget extends AbstractResolveByStubTest { + public void testAllFilesPresentInWithUseSiteTarget() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ReceiverTarget.kt") + public void testReceiverTarget() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/withUseSiteTarget/ReceiverTarget.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/loadJava/compiledKotlin/class") diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptAnnotationAndConstantLoader.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptAnnotationAndConstantLoader.kt index 880df62257e..5ab9eba382c 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptAnnotationAndConstantLoader.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptAnnotationAndConstantLoader.kt @@ -57,6 +57,13 @@ class KotlinJavascriptAnnotationAndConstantLoader( return annotations.map { proto -> deserializer.deserializeAnnotation(proto, nameResolver) } } + override fun loadExtensionReceiverParameterAnnotations( + container: ProtoContainer, + callable: ProtoBuf.Callable, + nameResolver: NameResolver, + kind: AnnotatedCallableKind + ): List = emptyList() + override fun loadTypeAnnotations(proto: ProtoBuf.Type, nameResolver: NameResolver): List { val annotations = proto.getExtension(JsProtoBuf.typeAnnotation).orEmpty() return annotations.map { proto -> deserializer.deserializeAnnotation(proto, nameResolver) }