Support new repeatable annotations in kotlin-reflect
- Unwrap Kotlin-repeatable annotations (with implicit container) - Introduce `KAnnotatedElement.findAnnotations` to find instances of repeated annotations #KT-12794
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JS_IR_ES6
|
||||
// IGNORE_BACKEND: JS, NATIVE
|
||||
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.findAnnotations
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
annotation class Yes(val value: String)
|
||||
@@ -19,5 +22,7 @@ fun box(): String {
|
||||
assertNull(Bar::class.findAnnotation<Yes>())
|
||||
assertNull(Bar::class.findAnnotation<No>())
|
||||
|
||||
assertEquals("OK", Foo::class.findAnnotations<Yes>().single().value)
|
||||
|
||||
return Foo::class.findAnnotation<Yes>()?.value ?: "Fail: no annotation"
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// !LANGUAGE: +RepeatableAnnotations
|
||||
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// JVM_TARGET: 1.8
|
||||
// FULL_JDK
|
||||
// WITH_REFLECT
|
||||
// FILE: box.kt
|
||||
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.findAnnotations
|
||||
import kotlin.reflect.full.hasAnnotation
|
||||
|
||||
@A("O")
|
||||
@A("")
|
||||
@A("K")
|
||||
fun f() {}
|
||||
|
||||
fun box(): String {
|
||||
val element = ::f
|
||||
if (element.hasAnnotation<A>()) return "Fail hasAnnotation $element"
|
||||
val find = element.findAnnotation<A>()
|
||||
if (find != null) return "Fail findAnnotation $element: $find"
|
||||
|
||||
val all = (element.annotations.single() as A.Container).value.asList()
|
||||
val findAll = element.findAnnotations<A>()
|
||||
if (all != findAll) throw AssertionError("Fail findAnnotations $element: $all != $findAll")
|
||||
|
||||
return all.fold("") { acc, it -> acc + it.value }
|
||||
}
|
||||
|
||||
// FILE: A.java
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Repeatable(A.Container.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface A {
|
||||
String value();
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Container {
|
||||
A[] value();
|
||||
}
|
||||
}
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
// !LANGUAGE: +RepeatableAnnotations
|
||||
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// JVM_TARGET: 1.8
|
||||
// FULL_JDK
|
||||
// WITH_REFLECT
|
||||
|
||||
// Android doesn't have @Repeatable, so findAnnotations can't unpack repeatable annotations.
|
||||
// IGNORE_BACKEND: ANDROID
|
||||
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.findAnnotations
|
||||
import kotlin.reflect.full.hasAnnotation
|
||||
|
||||
@java.lang.annotation.Repeatable(A.Container::class)
|
||||
annotation class A(val value: String) {
|
||||
annotation class Container(val value: Array<A>)
|
||||
}
|
||||
|
||||
@A("O")
|
||||
@A("")
|
||||
@A("K")
|
||||
fun f() {}
|
||||
|
||||
fun box(): String {
|
||||
val element = ::f
|
||||
if (element.hasAnnotation<A>()) return "Fail hasAnnotation $element"
|
||||
val find = element.findAnnotation<A>()
|
||||
if (find != null) return "Fail findAnnotation $element: $find"
|
||||
|
||||
val all = (element.annotations.single() as A.Container).value.asList()
|
||||
val findAll = element.findAnnotations<A>()
|
||||
if (all != findAll) throw AssertionError("Fail findAnnotations $element: $all != $findAll")
|
||||
|
||||
return all.fold("") { acc, it -> acc + it.value }
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// !LANGUAGE: +RepeatableAnnotations
|
||||
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// JVM_TARGET: 1.8
|
||||
// FULL_JDK
|
||||
// WITH_REFLECT
|
||||
|
||||
// Test failed to run to completion. Reason: 'Instrumentation run failed due to 'Native crash''. Check device logcat for details
|
||||
// IGNORE_BACKEND: ANDROID
|
||||
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
import kotlin.reflect.KAnnotatedElement
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.findAnnotations
|
||||
import kotlin.reflect.full.hasAnnotation
|
||||
|
||||
fun check(element: KAnnotatedElement) {
|
||||
if (!element.hasAnnotation<A>()) throw AssertionError("Fail hasAnnotation $element")
|
||||
|
||||
val find = element.findAnnotation<A>()
|
||||
if (find == null || find.value != "O") throw AssertionError("Fail findAnnotation $element: $find")
|
||||
|
||||
val all = element.annotations
|
||||
val findAll = element.findAnnotations<A>()
|
||||
if (all != findAll) throw AssertionError("Fail findAnnotations $element: $all != $findAll")
|
||||
|
||||
if (all.any { it !is A })
|
||||
throw AssertionError("Fail 1 $element: $all")
|
||||
if (all.fold("") { acc, it -> acc + (it as A).value } != "OK")
|
||||
throw AssertionError("Fail 2 $element: $all")
|
||||
}
|
||||
|
||||
@Repeatable
|
||||
@Target(CLASS, FUNCTION, PROPERTY, TYPE)
|
||||
annotation class A(val value: String)
|
||||
|
||||
@A("O") @A("") @A("K")
|
||||
fun f() {}
|
||||
|
||||
@A("O") @A("") @A("") @A("K")
|
||||
var p = 1
|
||||
|
||||
@A("O") @A("K")
|
||||
class Z
|
||||
|
||||
fun g(): @A("O") @A("K") @A("") Unit {}
|
||||
|
||||
fun box(): String {
|
||||
check(::f)
|
||||
check(::p)
|
||||
check(Z::class)
|
||||
check(::g.returnType)
|
||||
return "OK"
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// !LANGUAGE: +RepeatableAnnotations
|
||||
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// JVM_TARGET: 1.8
|
||||
// FULL_JDK
|
||||
// WITH_REFLECT
|
||||
|
||||
// Android doesn't have @Repeatable.
|
||||
// IGNORE_BACKEND: ANDROID
|
||||
|
||||
package test
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.reflect.full.*
|
||||
|
||||
@java.lang.annotation.Repeatable(As::class)
|
||||
annotation class A(val value: Int)
|
||||
|
||||
annotation class As(val value: Array<A>)
|
||||
|
||||
@A(1)
|
||||
@As([A(2), A(3)])
|
||||
class Z
|
||||
|
||||
@As([A(1), A(2)])
|
||||
@A(3)
|
||||
class ZZ
|
||||
|
||||
// JDK 9+ uses {} for array arguments instead of [], JDK 15+ doesn't render "value="
|
||||
fun Any?.render(): String =
|
||||
toString().replace("value=", "").replace("{", "[").replace("}", "]")
|
||||
|
||||
// Explicit container is not unwrapped.
|
||||
fun box(): String {
|
||||
assertEquals("[@test.A(1), @test.As([@test.A(2), @test.A(3)])]", Z::class.annotations.render())
|
||||
assertEquals("[@test.A(1)]", Z::class.findAnnotations<A>().render())
|
||||
assertEquals("@test.A(1)", Z::class.findAnnotation<A>().render())
|
||||
|
||||
assertEquals("[@test.As([@test.A(1), @test.A(2)]), @test.A(3)]", ZZ::class.annotations.render())
|
||||
assertEquals("[@test.A(3)]", ZZ::class.findAnnotations<A>().render())
|
||||
assertEquals("@test.A(3)", ZZ::class.findAnnotation<A>().render())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user