diff --git a/plugins/annotation-processing/testData/processors/Nested.kt b/plugins/annotation-processing/testData/processors/Nested.kt new file mode 100644 index 00000000000..55feac868b4 --- /dev/null +++ b/plugins/annotation-processing/testData/processors/Nested.kt @@ -0,0 +1,7 @@ +import kotlin.reflect.KClass + +annotation class Anno(val a: Anno2, val b: Array, val c: String, val d: KClass<*>, val e: Array>) +annotation class Anno2(val name: String) + +@Anno(a = Anno2("Tim"), b = arrayOf(Anno2("Kate"), Anno2("Mary")), c = "ABC", d = Anno2::class, e = arrayOf(Anno::class)) +class Test \ No newline at end of file diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeAnnotationValue.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeAnnotationValue.kt index 6afd7065a5e..820c554f80b 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeAnnotationValue.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/elements/JeAnnotationValue.kt @@ -47,22 +47,22 @@ fun JeAnnotationValue(psi: PsiAnnotationMemberValue): AnnotationValue { return annotationValue } -internal class JeAnnotationAnnotationValue(val psi: PsiAnnotation) : AnnotationValue { +class JeAnnotationAnnotationValue(val psi: PsiAnnotation) : AnnotationValue { override fun getValue() = JeAnnotationMirror(psi) override fun accept(v: AnnotationValueVisitor, p: P) = v.visitAnnotation(value, p) } -internal class JeEnumValueAnnotationValue(val psi: PsiEnumConstant) : AnnotationValue { +class JeEnumValueAnnotationValue(val psi: PsiEnumConstant) : AnnotationValue { override fun getValue() = JeVariableElement(psi) override fun accept(v: AnnotationValueVisitor, p: P) = v.visitEnumConstant(value, p) } -internal class JeTypeAnnotationValue(val psi: PsiClassObjectAccessExpression) : AnnotationValue { +class JeTypeAnnotationValue(val psi: PsiClassObjectAccessExpression) : AnnotationValue { override fun getValue() = psi.operand.type.toJeType(psi.manager) override fun accept(v: AnnotationValueVisitor, p: P) = v.visitType(value, p) } -internal abstract class JePrimitiveAnnotationValue : AnnotationValue { +abstract class JePrimitiveAnnotationValue : AnnotationValue { override fun accept(v: AnnotationValueVisitor, p: P): R { val value = this.value return when (value) { @@ -80,20 +80,20 @@ internal abstract class JePrimitiveAnnotationValue : AnnotationValue { } } -internal class JeLiteralAnnotationValue(val psi: PsiLiteral) : JePrimitiveAnnotationValue() { +class JeLiteralAnnotationValue(val psi: PsiLiteral) : JePrimitiveAnnotationValue() { override fun getValue() = psi.value } -internal class JeExpressionAnnotationValue(val psi: PsiExpression) : JePrimitiveAnnotationValue() { +class JeExpressionAnnotationValue(val psi: PsiExpression) : JePrimitiveAnnotationValue() { override fun getValue() = psi.calcConstantValue() } -internal class JeArrayAnnotationValue(val psi: PsiArrayInitializerMemberValue) : AnnotationValue { +class JeArrayAnnotationValue(val psi: PsiArrayInitializerMemberValue) : AnnotationValue { override fun getValue() = psi.initializers.map(::JeAnnotationValue) override fun accept(v: AnnotationValueVisitor, p: P) = v.visitArray(value, p) } -internal class JeErrorAnnotationValue(val psi: PsiElement) : AnnotationValue { +class JeErrorAnnotationValue(val psi: PsiElement) : AnnotationValue { override fun accept(v: AnnotationValueVisitor, p: P) = v.visitString(psi.text, p) override fun getValue() = null } \ No newline at end of file diff --git a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/KotlinAnnotationProxyMaker.kt b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/KotlinAnnotationProxyMaker.kt index be7f10752a8..3379d5b448b 100644 --- a/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/KotlinAnnotationProxyMaker.kt +++ b/plugins/java-model-wrappers/src/org/jetbrains/kotlin/java/model/internal/KotlinAnnotationProxyMaker.kt @@ -17,7 +17,7 @@ package org.jetbrains.kotlin.java.model.internal import com.intellij.psi.* -import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation +import com.intellij.psi.util.PsiTypesUtil import org.jetbrains.kotlin.java.model.types.toJeType import sun.reflect.annotation.AnnotationParser import sun.reflect.annotation.ExceptionProxy @@ -74,10 +74,13 @@ private fun getConstantValue( when { returnType == PsiType.NULL || returnType == PsiType.VOID -> unexpectedType("void") - jReturnType == String::class.java -> return (psiValue as? PsiExpression)?.calcConstantValue(evaluator) - jReturnType == Class::class.java -> { - val type = getObjectType(psiValue).toJeType(manager) - return MirroredTypeExceptionProxy(type) + returnType.fqName == "java.lang.String" -> return (psiValue as? PsiExpression)?.calcConstantValue(evaluator) + jReturnType.isAnnotation -> { + if (psiValue !is PsiAnnotation) error("psiValue is not a PsiAnnotation") + val annotationClass = PsiTypesUtil.getPsiClass(returnType) ?: error("Can't resolve type $returnType") + @Suppress("UNCHECKED_CAST") + val annotation = KotlinAnnotationProxyMaker(psiValue, annotationClass, jReturnType as Class) + return jReturnType.cast(annotation.generate()) } jReturnType.isArray -> { val jComponentType = jReturnType.componentType ?: unexpectedType("no component type for $jReturnType") @@ -88,7 +91,7 @@ private fun getConstantValue( else -> listOf(psiValue) } - if (jComponentType == Class::class.java) { + if (!jComponentType.isPrimitive && !jComponentType.isAnnotation) { val typeMirrors = arrayValues.map { getObjectType(it).toJeType(manager) } return MirroredTypesExceptionProxy(Collections.unmodifiableList(typeMirrors)) } else { @@ -105,10 +108,18 @@ private fun getConstantValue( ?: error("$psiValue can not be resolved to enum constant") return AnnotationUtil.createEnumValue(jReturnType, enumConstant.name) } - else -> return castPrimitiveValue(returnType, (psiValue as? PsiExpression)?.calcConstantValue(evaluator)) + else -> return if (returnType is PsiClassType) { + val type = getObjectType(psiValue).toJeType(manager) + MirroredTypeExceptionProxy(type) + } else { + castPrimitiveValue(returnType, (psiValue as? PsiExpression)?.calcConstantValue(evaluator)) + } } } +private val PsiType.fqName: String? + get() = (this as? PsiClassType)?.resolve()?.qualifiedName + private fun getObjectType(value: PsiAnnotationMemberValue): PsiType { when (value) { is PsiClassObjectAccessExpression -> return value.operand.type diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt index 29d3329d782..369e587a5e7 100644 --- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/ProcessorTests.kt @@ -16,11 +16,9 @@ package org.jetbrains.kotlin.annotation.processing.test.processor -import org.jetbrains.kotlin.java.model.elements.JeAnnotationMirror -import org.jetbrains.kotlin.java.model.elements.JeMethodExecutableElement -import org.jetbrains.kotlin.java.model.elements.JeTypeElement -import org.jetbrains.kotlin.java.model.elements.JeVariableElement +import org.jetbrains.kotlin.java.model.elements.* import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance +import javax.lang.model.element.AnnotationMirror class ProcessorTests : AbstractProcessorTest() { override val testDataDir = "plugins/annotation-processing/testData/processors" @@ -98,4 +96,19 @@ class ProcessorTests : AbstractProcessorTest() { assertEquals(1, implAnnotations.size) assertEquals("Tom", implAnnotations.first().elementValues.values.first().value) } + + fun testNested() = test("Nested", "Anno") { set, roundEnv, env -> + assertEquals(1, set.size) + val annotatedElements = roundEnv.getElementsAnnotatedWith(set.first()) + assertEquals(1, annotatedElements.size) + val test = annotatedElements.first() + val anno2 = test.annotationMirrors.first { it is JeAnnotationMirror && it.psi.qualifiedName == "Anno" } + + fun AnnotationMirror.getParam(name: String) = elementValues.entries.first { it.key.simpleName.toString() == name }.value + assertTrue(anno2.getParam("a") is JeAnnotationAnnotationValue) + assertTrue((anno2.getParam("b") as JeArrayAnnotationValue).value.first() is JeAnnotationAnnotationValue) + assertTrue(anno2.getParam("c") is JePrimitiveAnnotationValue) + assertTrue(anno2.getParam("d") is JeTypeAnnotationValue) + assertTrue((anno2.getParam("e") as JeArrayAnnotationValue).value.first() is JeTypeAnnotationValue) + } } \ No newline at end of file