From 1309c1f95fd6c99584507d47ce67c19505594f1c Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 15 Jul 2015 19:47:05 +0300 Subject: [PATCH] Annotation mapper is introduced to map java annotation targets to kotlin targets + a set of new / fixed tests --- .../annotations/JavaAnnotationConstructors.kt | 8 +- .../tests/annotations/options/targets/java.kt | 81 +++++++++++++ .../annotations/options/targets/java.txt | 92 +++++++++++++++ .../testsWithStdLib/annotations/targetuse.kt | 22 ++++ .../testsWithStdLib/annotations/targetuse.txt | 15 +++ .../annotations/AnnotationTargets.java | 57 +++++++++ .../annotations/AnnotationTargets.txt | 45 ++++++++ .../annotations/ArrayOfEnumInParam.txt | 2 +- .../checkers/JetDiagnosticsTestGenerated.java | 6 + ...JetDiagnosticsTestWithStdLibGenerated.java | 6 + .../jvm/compiler/LoadJavaTestGenerated.java | 6 + ...mRuntimeDescriptorLoaderTestGenerated.java | 6 + .../kotlin/load/java/JvmAnnotationNames.java | 1 + .../java/components/JavaAnnotationMapper.kt | 109 ++++++++++++++++++ .../load/java/lazy/LazyJavaAnnotations.kt | 6 +- .../kotlin/builtins/KotlinBuiltIns.java | 27 +++++ idea/testData/highlighter/JavaTypes.kt | 2 +- 17 files changed, 485 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/annotations/options/targets/java.kt create mode 100644 compiler/testData/diagnostics/tests/annotations/options/targets/java.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.txt create mode 100644 compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java create mode 100644 compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.txt create mode 100644 core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt diff --git a/compiler/testData/diagnostics/tests/annotations/JavaAnnotationConstructors.kt b/compiler/testData/diagnostics/tests/annotations/JavaAnnotationConstructors.kt index bbab0f2144b..65552282a72 100644 --- a/compiler/testData/diagnostics/tests/annotations/JavaAnnotationConstructors.kt +++ b/compiler/testData/diagnostics/tests/annotations/JavaAnnotationConstructors.kt @@ -1,4 +1,8 @@ import java.lang.annotation.* -annotation @java.lang.annotation.Retention(RetentionPolicy.CLASS) class my -annotation Retention(RetentionPolicy.RUNTIME) Target(ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR) class my1 +@java.lang.annotation.Retention(RetentionPolicy.CLASS) +annotation class my + +Retention(RetentionPolicy.RUNTIME) +Target(ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR) +annotation class my1 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/options/targets/java.kt b/compiler/testData/diagnostics/tests/annotations/options/targets/java.kt new file mode 100644 index 00000000000..2de8bfd70d1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/options/targets/java.kt @@ -0,0 +1,81 @@ +// FILE: test/AnnotationTargets.java + +package test; + +import java.lang.annotation.*; + +public class AnnotationTargets { + + public @interface base { + + } + + @Target(ElementType.ANNOTATION_TYPE) + public @interface meta { + + } + + @Target(ElementType.CONSTRUCTOR) + public @interface konstructor { + + } + + @Target(ElementType.FIELD) + public @interface field { + + } + + + @Target(ElementType.LOCAL_VARIABLE) + public @interface local { + + } + + @Target(ElementType.METHOD) + public @interface method { + + } + + @Target(ElementType.PARAMETER) + public @interface parameter { + + } + + + @Target(ElementType.TYPE) + public @interface type { + + } + + @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) + public @interface multiple { + + } +} + +// FILE: test/AnnotationTargets.kt + +package test + +import test.AnnotationTargets.* + +base meta type konstructor annotation class KMeta + +base meta type method multiple class KClass( + @base @field @parameter val y: + @base @type Int) { + + base multiple field local val x = 0 + @method @konstructor @type get + + base method multiple konstructor + fun foo(@parameter @type i: + @base @multiple Int + ): @field @parameter Int { + + @local @base @multiple @field val j = i + 1 + @base @multiple return j + } + + base method konstructor constructor(): this(0) +} diff --git a/compiler/testData/diagnostics/tests/annotations/options/targets/java.txt b/compiler/testData/diagnostics/tests/annotations/options/targets/java.txt new file mode 100644 index 00000000000..351330d6ace --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/options/targets/java.txt @@ -0,0 +1,92 @@ +package + +package test { + + public open class AnnotationTargets { + public constructor AnnotationTargets() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final class base : kotlin.Annotation { + public constructor base() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.FIELD}) public final class field : kotlin.Annotation { + public constructor field() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.CONSTRUCTOR}) public final class konstructor : kotlin.Annotation { + public constructor konstructor() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.LOCAL_VARIABLE}) public final class local : kotlin.Annotation { + public constructor local() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS}) public final class meta : kotlin.Annotation { + public constructor meta() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER}) public final class method : kotlin.Annotation { + public constructor method() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD}) public final class multiple : kotlin.Annotation { + public constructor multiple() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.VALUE_PARAMETER}) public final class parameter : kotlin.Annotation { + public constructor parameter() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.CLASSIFIER}) public final class type : kotlin.Annotation { + public constructor type() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + } + + test.AnnotationTargets.base() test.AnnotationTargets.meta() test.AnnotationTargets.type() test.AnnotationTargets.method() test.AnnotationTargets.multiple() internal final class KClass { + test.AnnotationTargets.base() test.AnnotationTargets.method() test.AnnotationTargets.konstructor() public constructor KClass() + public constructor KClass(/*0*/ test.AnnotationTargets.base() test.AnnotationTargets.field() test.AnnotationTargets.parameter() y: @[test.AnnotationTargets.base() test.AnnotationTargets.type()] kotlin.Int) + test.AnnotationTargets.base() test.AnnotationTargets.multiple() test.AnnotationTargets.field() test.AnnotationTargets.local() internal final val x: kotlin.Int = 0 + test.AnnotationTargets.base() test.AnnotationTargets.field() test.AnnotationTargets.parameter() internal final val y: @[test.AnnotationTargets.base() test.AnnotationTargets.type()] kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + test.AnnotationTargets.base() test.AnnotationTargets.method() test.AnnotationTargets.multiple() test.AnnotationTargets.konstructor() internal final fun foo(/*0*/ test.AnnotationTargets.parameter() test.AnnotationTargets.type() i: @[test.AnnotationTargets.base() test.AnnotationTargets.multiple()] kotlin.Int): @[test.AnnotationTargets.field() test.AnnotationTargets.parameter()] kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + test.AnnotationTargets.base() test.AnnotationTargets.meta() test.AnnotationTargets.type() test.AnnotationTargets.konstructor() kotlin.annotation.annotation() internal final class KMeta : kotlin.Annotation { + public constructor KMeta() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.kt new file mode 100644 index 00000000000..8b8c11c4383 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.kt @@ -0,0 +1,22 @@ +// FILE: TargetUse.java + +import kotlin.annotation.target; +import java.lang.annotation.Target; + +public class TargetUse { + public static String foo(target aTarget) { + return aTarget.toString(); + } + + public static String bar(Target aTarget) { + return aTarget.toString(); + } +} + +// FILE: TargetUse.kt + +import java.lang.annotation.Target + +fun fooUse(aTarget: target): String = TargetUse.foo(aTarget) + +fun barUse(aTarget: Target): String = TargetUse.bar(aTarget) diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.txt new file mode 100644 index 00000000000..30313eda340 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.txt @@ -0,0 +1,15 @@ +package + +internal fun barUse(/*0*/ aTarget: java.lang.annotation.Target): kotlin.String +internal fun fooUse(/*0*/ aTarget: kotlin.annotation.target): kotlin.String + +public open class TargetUse { + public constructor TargetUse() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun bar(/*0*/ aTarget: java.lang.annotation.Target!): kotlin.String! + public open fun foo(/*0*/ aTarget: kotlin.annotation.target!): kotlin.String! +} diff --git a/compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java b/compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java new file mode 100644 index 00000000000..7cf17bb8282 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java @@ -0,0 +1,57 @@ +package test; + +import java.lang.annotation.*; + +public class AnnotationTargets { + + public @interface base { + + } + + @Target(ElementType.ANNOTATION_TYPE) + public @interface annotation { + + } + + @Target(ElementType.CONSTRUCTOR) + public @interface constructor { + + } + + @Target(ElementType.FIELD) + public @interface field { + + } + + + @Target(ElementType.LOCAL_VARIABLE) + public @interface local { + + } + + @Target(ElementType.METHOD) + public @interface method { + + } + + @Target(ElementType.PACKAGE) + public @interface packag { + + } + + @Target(ElementType.PARAMETER) + public @interface parameter { + + } + + + @Target(ElementType.TYPE) + public @interface type { + + } + + @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) + public @interface multiple { + + } +} \ No newline at end of file diff --git a/compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.txt b/compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.txt new file mode 100644 index 00000000000..c28f3147389 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.txt @@ -0,0 +1,45 @@ +package test + +public open class AnnotationTargets { + public constructor AnnotationTargets() + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS}) public final class annotation : kotlin.Annotation { + public constructor annotation() + } + + public final class base : kotlin.Annotation { + public constructor base() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.CONSTRUCTOR}) public final class constructor : kotlin.Annotation { + public constructor constructor() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.FIELD}) public final class field : kotlin.Annotation { + public constructor field() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.LOCAL_VARIABLE}) public final class local : kotlin.Annotation { + public constructor local() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER}) public final class method : kotlin.Annotation { + public constructor method() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD}) public final class multiple : kotlin.Annotation { + public constructor multiple() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.PACKAGE}) public final class packag : kotlin.Annotation { + public constructor packag() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.VALUE_PARAMETER}) public final class parameter : kotlin.Annotation { + public constructor parameter() + } + + kotlin.annotation.target(allowedTargets = {AnnotationTarget.CLASSIFIER}) public final class type : kotlin.Annotation { + public constructor type() + } +} diff --git a/compiler/testData/loadJava/compiledJava/annotations/ArrayOfEnumInParam.txt b/compiler/testData/loadJava/compiledJava/annotations/ArrayOfEnumInParam.txt index 08cd4f72ffe..ff291941228 100644 --- a/compiler/testData/loadJava/compiledJava/annotations/ArrayOfEnumInParam.txt +++ b/compiler/testData/loadJava/compiledJava/annotations/ArrayOfEnumInParam.txt @@ -2,7 +2,7 @@ package test public interface ArrayOfEnumInParam { - public final class targetAnnotation : kotlin.Annotation { + kotlin.annotation.target(allowedTargets = {AnnotationTarget.FIELD, AnnotationTarget.CONSTRUCTOR}) public final class targetAnnotation : kotlin.Annotation { public constructor targetAnnotation(/*0*/ value: kotlin.String) public final val value: kotlin.String public abstract fun value(): kotlin.String diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 03ba06db697..5501b63a16a 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -1108,6 +1108,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("java.kt") + public void testJava() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/options/targets/java.kt"); + doTest(fileName); + } + @TestMetadata("local.kt") public void testLocal() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/options/targets/local.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index 12336ebc0b2..db1e5b9663c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -85,6 +85,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic doTest(fileName); } + @TestMetadata("targetuse.kt") + public void testTargetuse() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index 1e88eca050c..57b8d005e5e 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -327,6 +327,12 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledJava(fileName); } + @TestMetadata("AnnotationTargets.java") + public void testAnnotationTargets() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java"); + doTestCompiledJava(fileName); + } + @TestMetadata("ArithmeticExpressionInParam.java") public void testArithmeticExpressionInParam() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/ArithmeticExpressionInParam.java"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index cf574eae3eb..538f65a201e 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -3378,6 +3378,12 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD doTest(fileName); } + @TestMetadata("AnnotationTargets.java") + public void testAnnotationTargets() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java"); + doTest(fileName); + } + @TestMetadata("ArithmeticExpressionInParam.java") public void testArithmeticExpressionInParam() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/ArithmeticExpressionInParam.java"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java index c3ff64321d4..8fcb16d049d 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java @@ -38,6 +38,7 @@ public final class JvmAnnotationNames { public static final String KIND_FIELD_NAME = "kind"; public static final String DATA_FIELD_NAME = "data"; public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value"); + public static final Name TARGET_ANNOTATION_MEMBER_NAME = Name.identifier("allowedTargets"); public static final FqName JETBRAINS_NOT_NULL_ANNOTATION = new FqName("org.jetbrains.annotations.NotNull"); public static final FqName JETBRAINS_NULLABLE_ANNOTATION = new FqName("org.jetbrains.annotations.Nullable"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt new file mode 100644 index 00000000000..1f45611e0c5 --- /dev/null +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.load.java.components + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext +import org.jetbrains.kotlin.load.java.lazy.descriptors.resolveAnnotation +import org.jetbrains.kotlin.load.java.structure.JavaAnnotation +import org.jetbrains.kotlin.load.java.structure.JavaAnnotationArgument +import org.jetbrains.kotlin.load.java.structure.JavaArrayAnnotationArgument +import org.jetbrains.kotlin.load.java.structure.JavaEnumValueAnnotationArgument +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.JavaToKotlinClassMap +import org.jetbrains.kotlin.resolve.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.constants.EnumValue +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.descriptors.annotations.AnnotationTarget +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.storage.StorageManager +import java.lang.annotation.Target +import java.util.* +import kotlin.annotation + +public object JavaAnnotationMapper { + + private val targetClassId = ClassId.topLevel(FqName(javaClass().getCanonicalName())) + + public fun mapJavaAnnotation(annotation: JavaAnnotation, c: LazyJavaResolverContext): AnnotationDescriptor? = + when (annotation.classId) { + targetClassId -> JavaTargetAnnotationDescriptor(annotation, c, c.module.builtIns) + else -> null + } + + public val kotlinToJavaNameMap: Map = + mapOf(KotlinBuiltIns.FQ_NAMES.target to FqName(javaClass().getCanonicalName())) +} + +class JavaTargetAnnotationDescriptor( + annotation: JavaAnnotation, + c: LazyJavaResolverContext, + val builtIns: KotlinBuiltIns +): AnnotationDescriptor { + + private val valueArguments = c.storageManager.createLazyValue { + val firstArgument = annotation.arguments.firstOrNull() + val targetArgument = when (firstArgument) { + is JavaArrayAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(firstArgument.getElements(), builtIns) + is JavaEnumValueAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(listOf(firstArgument), builtIns) + else -> return@createLazyValue emptyMap>() + } + val parameterDescriptor = builtIns.targetAnnotation.constructors.firstOrNull()?.valueParameters?.firstOrNull() + parameterDescriptor?.let { mapOf(it to targetArgument) } ?: emptyMap() + } + + override fun getAllValueArguments() = valueArguments() + + override fun getType() = builtIns.targetAnnotation.defaultType +} + +public object JavaAnnotationTargetMapper { + private val targetNameLists = mapOf("PACKAGE" to EnumSet.of(AnnotationTarget.PACKAGE), + "TYPE" to EnumSet.of(AnnotationTarget.CLASSIFIER), + "ANNOTATION_TYPE" to EnumSet.of(AnnotationTarget.ANNOTATION_CLASS), + "TYPE_PARAMETER" to EnumSet.of(AnnotationTarget.TYPE_PARAMETER), + "FIELD" to EnumSet.of(AnnotationTarget.FIELD), + "LOCAL_VARIABLE" to EnumSet.of(AnnotationTarget.LOCAL_VARIABLE), + "PARAMETER" to EnumSet.of(AnnotationTarget.VALUE_PARAMETER), + "CONSTRUCTOR" to EnumSet.of(AnnotationTarget.CONSTRUCTOR), + "METHOD" to EnumSet.of(AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER), + "TYPE_USE" to EnumSet.of(AnnotationTarget.TYPE) + ) + + public fun mapJavaTargetArgumentByName(argumentName: String?): Set = targetNameLists[argumentName] ?: emptySet() + + public fun mapJavaTargetArguments(arguments: List, builtIns: KotlinBuiltIns): ConstantValue<*>? { + // Map arguments: java.lang.annotation.Target -> kotlin.annotation.target + val kotlinTargets = arguments.filterIsInstance() + .flatMap { mapJavaTargetArgumentByName(it.resolve()?.name?.asString()) } + .map { builtIns.getAnnotationTargetEnumEntry(Name.identifier(it.name())) } + .filterNotNull() + .map { EnumValue(it) } + val parameterDescriptor = DescriptorResolverUtils.getAnnotationParameterByName(JvmAnnotationNames.TARGET_ANNOTATION_MEMBER_NAME, + builtIns.targetAnnotation) + return ArrayValue(kotlinTargets, parameterDescriptor?.type ?: ErrorUtils.createErrorType("Error: AnnotationTarget[]"), builtIns) + } +} diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt index af921d85906..f0be45995bc 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.load.java.lazy import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.load.java.components.JavaAnnotationMapper import org.jetbrains.kotlin.load.java.lazy.descriptors.resolveAnnotation import org.jetbrains.kotlin.load.java.structure.JavaAnnotation import org.jetbrains.kotlin.load.java.structure.JavaAnnotationOwner @@ -29,17 +30,18 @@ class LazyJavaAnnotations( ) : Annotations { private val annotationDescriptors = c.storageManager.createMemoizedFunctionWithNullableValues { annotation: JavaAnnotation -> - c.resolveAnnotation(annotation) + JavaAnnotationMapper.mapJavaAnnotation(annotation, c) ?: c.resolveAnnotation(annotation) } override fun findAnnotation(fqName: FqName) = annotationOwner.findAnnotation(fqName)?.let(annotationDescriptors) + ?: JavaAnnotationMapper.kotlinToJavaNameMap[fqName]?.let { annotationOwner.findAnnotation(it)?.let(annotationDescriptors) } override fun findExternalAnnotation(fqName: FqName) = c.externalAnnotationResolver.findExternalAnnotation(annotationOwner, fqName)?.let(annotationDescriptors) override fun iterator() = - annotationOwner.getAnnotations().asSequence().map(annotationDescriptors).filterNotNull().iterator() + annotationOwner.annotations.asSequence().map(annotationDescriptors).filterNotNull().iterator() override fun isEmpty() = !iterator().hasNext() } diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 434ec5375b0..5507a22eb2f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -101,6 +101,7 @@ public class KotlinBuiltIns { private final ModuleDescriptorImpl builtInsModule; private final BuiltinsPackageFragment builtinsPackageFragment; + private final BuiltinsPackageFragment annotationPackageFragment; private final Map primitiveTypeToArrayJetType; private final Map primitiveJetTypeToJetArrayType; @@ -130,6 +131,7 @@ public class KotlinBuiltIns { builtInsModule.setDependencies(builtInsModule); builtinsPackageFragment = (BuiltinsPackageFragment) single(packageFragmentProvider.getPackageFragments(BUILT_INS_PACKAGE_FQ_NAME)); + annotationPackageFragment = (BuiltinsPackageFragment) single(packageFragmentProvider.getPackageFragments(ANNOTATION_PACKAGE_FQ_NAME)); primitiveTypeToArrayJetType = new EnumMap(PrimitiveType.class); primitiveJetTypeToJetArrayType = new HashMap(); @@ -178,6 +180,7 @@ public class KotlinBuiltIns { public final FqName extension = fqName("extension"); public final FqName target = annotationName("target"); public final FqName annotation = annotationName("annotation"); + public final FqName annotationTarget = annotationName("AnnotationTarget"); public final FqNameUnsafe kClass = new FqName("kotlin.reflect.KClass").toUnsafe(); @@ -231,6 +234,14 @@ public class KotlinBuiltIns { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @NotNull + private ClassDescriptor getAnnotationClassByName(@NotNull Name simpleName) { + ClassifierDescriptor classifier = annotationPackageFragment.getMemberScope().getClassifier(simpleName); + assert classifier instanceof ClassDescriptor : "Must be a class descriptor " + simpleName + ", but was " + + (classifier == null ? "null" : classifier.toString()); + return (ClassDescriptor) classifier; + } + @NotNull public ClassDescriptor getBuiltInClassByName(@NotNull Name simpleName) { ClassDescriptor classDescriptor = getBuiltInClassByNameNullable(simpleName); @@ -377,6 +388,22 @@ public class KotlinBuiltIns { return getBuiltInClassByName("deprecated"); } + @NotNull + public ClassDescriptor getTargetAnnotation() { + return getAnnotationClassByName(FQ_NAMES.target.shortName()); + } + + @NotNull + public ClassDescriptor getAnnotationTargetEnum() { + return getAnnotationClassByName(FQ_NAMES.annotationTarget.shortName()); + } + + @Nullable + public ClassDescriptor getAnnotationTargetEnumEntry(@NotNull Name name) { + ClassifierDescriptor result = getAnnotationTargetEnum().getUnsubstitutedInnerClassesScope().getClassifier(name); + return result instanceof ClassDescriptor ? (ClassDescriptor) result : null; + } + @NotNull public ClassDescriptor getString() { return getBuiltInClassByName("String"); diff --git a/idea/testData/highlighter/JavaTypes.kt b/idea/testData/highlighter/JavaTypes.kt index 5ebcdd3cf16..13d2c1a3a95 100644 --- a/idea/testData/highlighter/JavaTypes.kt +++ b/idea/testData/highlighter/JavaTypes.kt @@ -1,2 +1,2 @@ -Override class TheClass : Runnable, Thread() { +SuppressWarnings class TheClass : Runnable, Thread() { } \ No newline at end of file