[FE] Relax rules of matching @Target annotation on expect and actual
Allow `expect` targets to be subset of `actual`. ^KT-58551
This commit is contained in:
committed by
Space Team
parent
ad84c83ee9
commit
6611a55a60
+6
@@ -226,6 +226,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/annotationArgumentsDefaults.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationTarget.kt")
|
||||
public void testAnnotationTarget() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/annotationTarget.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationTypeParameters.kt")
|
||||
public void testAnnotationTypeParameters() throws Exception {
|
||||
|
||||
+6
@@ -226,6 +226,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/annotationArgumentsDefaults.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationTarget.kt")
|
||||
public void testAnnotationTarget() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/annotationTarget.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationTypeParameters.kt")
|
||||
public void testAnnotationTypeParameters() throws Exception {
|
||||
|
||||
+6
-1
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.mpp.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
@@ -317,7 +318,11 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
override val DeclarationSymbolMarker.annotations: List<AnnotationCallInfo>
|
||||
get() = asSymbol().resolvedAnnotationsWithArguments.map(::AnnotationCallInfoImpl)
|
||||
|
||||
override fun areAnnotationArgumentsEqual(annotation1: AnnotationCallInfo, annotation2: AnnotationCallInfo): Boolean {
|
||||
override fun areAnnotationArgumentsEqual(
|
||||
annotation1: AnnotationCallInfo,
|
||||
annotation2: AnnotationCallInfo,
|
||||
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
|
||||
): Boolean {
|
||||
fun AnnotationCallInfo.getFirAnnotation(): FirAnnotation {
|
||||
return (this as AnnotationCallInfoImpl).annotation
|
||||
}
|
||||
|
||||
+10
-2
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.mpp.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
@@ -453,10 +454,17 @@ internal abstract class IrExpectActualMatchingContext(
|
||||
override val DeclarationSymbolMarker.annotations: List<AnnotationCallInfo>
|
||||
get() = asIr().annotations.map(::AnnotationCallInfoImpl)
|
||||
|
||||
override fun areAnnotationArgumentsEqual(annotation1: AnnotationCallInfo, annotation2: AnnotationCallInfo): Boolean {
|
||||
override fun areAnnotationArgumentsEqual(
|
||||
annotation1: AnnotationCallInfo, annotation2: AnnotationCallInfo,
|
||||
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
|
||||
): Boolean {
|
||||
fun AnnotationCallInfo.getIrElement(): IrConstructorCall = (this as AnnotationCallInfoImpl).irElement
|
||||
|
||||
return areIrExpressionConstValuesEqual(annotation1.getIrElement(), annotation2.getIrElement())
|
||||
return areIrExpressionConstValuesEqual(
|
||||
annotation1.getIrElement(),
|
||||
annotation2.getIrElement(),
|
||||
collectionArgumentsCompatibilityCheckStrategy,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun getClassIdAfterActualization(classId: ClassId): ClassId {
|
||||
|
||||
+14
-4
@@ -8,8 +8,13 @@ package org.jetbrains.kotlin.backend.common.actualizer
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.classOrFail
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
|
||||
|
||||
internal fun IrExpectActualMatchingContext.areIrExpressionConstValuesEqual(a: IrElement?, b: IrElement?): Boolean {
|
||||
internal fun IrExpectActualMatchingContext.areIrExpressionConstValuesEqual(
|
||||
a: IrElement?,
|
||||
b: IrElement?,
|
||||
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
|
||||
): Boolean {
|
||||
return when {
|
||||
a == null || b == null -> (a == null) == (b == null)
|
||||
|
||||
@@ -25,15 +30,20 @@ internal fun IrExpectActualMatchingContext.areIrExpressionConstValuesEqual(a: Ir
|
||||
a is IrGetEnumValue && b is IrGetEnumValue -> equalBy(a, b) { it.symbol.signature?.toString() }
|
||||
|
||||
a is IrVararg && b is IrVararg -> {
|
||||
equalBy(a, b) { it.elements.size } &&
|
||||
a.elements.zip(b.elements).all { (f, s) -> areIrExpressionConstValuesEqual(f, s) }
|
||||
collectionArgumentsCompatibilityCheckStrategy.areCompatible(a.elements, b.elements) { f, s ->
|
||||
areIrExpressionConstValuesEqual(f, s, collectionArgumentsCompatibilityCheckStrategy)
|
||||
}
|
||||
}
|
||||
|
||||
a is IrConstructorCall && b is IrConstructorCall -> {
|
||||
equalBy(a, b) { it.valueArgumentsCount } &&
|
||||
areCompatibleExpectActualTypes(a.type, b.type) &&
|
||||
(0..<a.valueArgumentsCount).all { i ->
|
||||
areIrExpressionConstValuesEqual(a.getValueArgument(i), b.getValueArgument(i))
|
||||
areIrExpressionConstValuesEqual(
|
||||
a.getValueArgument(i),
|
||||
b.getValueArgument(i),
|
||||
collectionArgumentsCompatibilityCheckStrategy
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-3
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.mpp
|
||||
|
||||
import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker
|
||||
import org.jetbrains.kotlin.mpp.TypeAliasSymbolMarker
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
object AbstractExpectActualAnnotationMatchChecker {
|
||||
@@ -46,17 +47,30 @@ object AbstractExpectActualAnnotationMatchChecker {
|
||||
val actualAnnotationsByName = actualSymbol.annotations.groupBy { it.classId }
|
||||
|
||||
for (expectAnnotation in expectSymbol.annotations) {
|
||||
if (expectAnnotation.classId in SKIPPED_CLASS_IDS || expectAnnotation.isOptIn) {
|
||||
val expectClassId = expectAnnotation.classId ?: continue
|
||||
if (expectClassId in SKIPPED_CLASS_IDS || expectAnnotation.isOptIn) {
|
||||
continue
|
||||
}
|
||||
if (expectAnnotation.isRetentionSource && skipSourceAnnotations) {
|
||||
continue
|
||||
}
|
||||
val actualAnnotationsWithSameClassId = actualAnnotationsByName[expectAnnotation.classId] ?: emptyList()
|
||||
if (actualAnnotationsWithSameClassId.none { areAnnotationArgumentsEqual(expectAnnotation, it) }) {
|
||||
val actualAnnotationsWithSameClassId = actualAnnotationsByName[expectClassId] ?: emptyList()
|
||||
val collectionCompatibilityChecker = getAnnotationCollectionArgumentsCompatibilityChecker(expectClassId)
|
||||
if (actualAnnotationsWithSameClassId.none {
|
||||
areAnnotationArgumentsEqual(expectAnnotation, it, collectionCompatibilityChecker)
|
||||
}) {
|
||||
return Incompatibility(expectSymbol, actualSymbol)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getAnnotationCollectionArgumentsCompatibilityChecker(annotationClassId: ClassId):
|
||||
ExpectActualCollectionArgumentsCompatibilityCheckStrategy {
|
||||
return if (annotationClassId == StandardClassIds.Annotations.Target) {
|
||||
ExpectActualCollectionArgumentsCompatibilityCheckStrategy.ExpectIsSubsetOfActual
|
||||
} else {
|
||||
ExpectActualCollectionArgumentsCompatibilityCheckStrategy.Default
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.mpp
|
||||
|
||||
|
||||
sealed class ExpectActualCollectionArgumentsCompatibilityCheckStrategy {
|
||||
abstract fun <T> areCompatible(
|
||||
expectArg: Collection<T>,
|
||||
actualArg: Collection<T>,
|
||||
elementsEqual: (T, T) -> Boolean,
|
||||
): Boolean
|
||||
|
||||
internal data object Default : ExpectActualCollectionArgumentsCompatibilityCheckStrategy() {
|
||||
override fun <T> areCompatible(
|
||||
expectArg: Collection<T>,
|
||||
actualArg: Collection<T>,
|
||||
elementsEqual: (T, T) -> Boolean,
|
||||
): Boolean {
|
||||
return expectArg.size == actualArg.size && expectArg.zip(actualArg).all { (e1, e2) -> elementsEqual(e1, e2) }
|
||||
}
|
||||
}
|
||||
|
||||
internal data object ExpectIsSubsetOfActual : ExpectActualCollectionArgumentsCompatibilityCheckStrategy() {
|
||||
override fun <T> areCompatible(
|
||||
expectArg: Collection<T>,
|
||||
actualArg: Collection<T>,
|
||||
elementsEqual: (T, T) -> Boolean,
|
||||
): Boolean {
|
||||
return expectArg.all { e1 ->
|
||||
actualArg.any { e2 -> elementsEqual(e1, e2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -160,7 +160,11 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
|
||||
|
||||
val DeclarationSymbolMarker.annotations: List<AnnotationCallInfo>
|
||||
|
||||
fun areAnnotationArgumentsEqual(annotation1: AnnotationCallInfo, annotation2: AnnotationCallInfo): Boolean
|
||||
fun areAnnotationArgumentsEqual(
|
||||
annotation1: AnnotationCallInfo,
|
||||
annotation2: AnnotationCallInfo,
|
||||
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
|
||||
): Boolean
|
||||
|
||||
val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean
|
||||
|
||||
|
||||
+11
-3
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.components.ClassicTypeSystemContextForCS
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
@@ -337,11 +338,18 @@ class ClassicExpectActualMatchingContext(val platformModule: ModuleDescriptor) :
|
||||
override val DeclarationSymbolMarker.annotations: List<AnnotationCallInfo>
|
||||
get() = asDescriptor().annotations.map(::AnnotationCallInfoImpl)
|
||||
|
||||
|
||||
override fun areAnnotationArgumentsEqual(annotation1: AnnotationCallInfo, annotation2: AnnotationCallInfo): Boolean {
|
||||
override fun areAnnotationArgumentsEqual(
|
||||
annotation1: AnnotationCallInfo,
|
||||
annotation2: AnnotationCallInfo,
|
||||
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
|
||||
): Boolean {
|
||||
fun AnnotationCallInfo.getDescriptor(): AnnotationDescriptor = (this as AnnotationCallInfoImpl).annotationDescriptor
|
||||
|
||||
return areExpressionConstValuesEqual(annotation1.getDescriptor(), annotation2.getDescriptor())
|
||||
return areExpressionConstValuesEqual(
|
||||
annotation1.getDescriptor(),
|
||||
annotation2.getDescriptor(),
|
||||
collectionArgumentsCompatibilityCheckStrategy,
|
||||
)
|
||||
}
|
||||
|
||||
private class AnnotationCallInfoImpl(
|
||||
|
||||
+14
-5
@@ -6,10 +6,15 @@
|
||||
package org.jetbrains.kotlin.resolve.multiplatform
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
|
||||
internal fun ExpectActualMatchingContext<*>.areExpressionConstValuesEqual(a: Any?, b: Any?): Boolean {
|
||||
internal fun ExpectActualMatchingContext<*>.areExpressionConstValuesEqual(
|
||||
a: Any?,
|
||||
b: Any?,
|
||||
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
|
||||
): Boolean {
|
||||
return when {
|
||||
a is AnnotationDescriptor && b is AnnotationDescriptor -> {
|
||||
val aArgs = a.allValueArguments
|
||||
@@ -17,16 +22,20 @@ internal fun ExpectActualMatchingContext<*>.areExpressionConstValuesEqual(a: Any
|
||||
a.fqName == b.fqName &&
|
||||
aArgs.size == bArgs.size &&
|
||||
areCompatibleExpectActualTypes(a.type, b.type) &&
|
||||
aArgs.keys.all { k -> areExpressionConstValuesEqual(aArgs[k], bArgs[k]) }
|
||||
aArgs.keys.all { k -> areExpressionConstValuesEqual(aArgs[k], bArgs[k], collectionArgumentsCompatibilityCheckStrategy) }
|
||||
}
|
||||
a is ConstantValue<*> && b is ConstantValue<*> -> {
|
||||
areExpressionConstValuesEqual(a.value, b.value)
|
||||
areExpressionConstValuesEqual(a.value, b.value, collectionArgumentsCompatibilityCheckStrategy)
|
||||
}
|
||||
a is Collection<*> && b is Collection<*> -> {
|
||||
a.size == b.size && a.zip(b).all { (f, s) -> areExpressionConstValuesEqual(f, s) }
|
||||
collectionArgumentsCompatibilityCheckStrategy.areCompatible(a, b) { f, s ->
|
||||
areExpressionConstValuesEqual(f, s, collectionArgumentsCompatibilityCheckStrategy)
|
||||
}
|
||||
}
|
||||
a is Array<*> && b is Array<*> -> {
|
||||
a.size == b.size && a.zip(b).all { (f, s) -> areExpressionConstValuesEqual(f, s) }
|
||||
collectionArgumentsCompatibilityCheckStrategy.areCompatible(a.toList(), b.toList()) { f, s ->
|
||||
areExpressionConstValuesEqual(f, s, collectionArgumentsCompatibilityCheckStrategy)
|
||||
}
|
||||
}
|
||||
else -> a == b
|
||||
}
|
||||
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
expect annotation class ExpectIsSubsetOfActual
|
||||
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
expect annotation class ExpectIsSubsetOfActualDifferentOrder
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
expect annotation class MoreTargetsOnExpect
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
expect annotation class RepeatedTargetsInExpect
|
||||
|
||||
@Target(allowedTargets = [])
|
||||
expect annotation class EmptyTargetsActual
|
||||
|
||||
// MODULE: m1-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.CLASS)
|
||||
actual annotation class ExpectIsSubsetOfActual
|
||||
|
||||
@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.CLASS)
|
||||
actual annotation class ExpectIsSubsetOfActualDifferentOrder
|
||||
|
||||
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>@Target(AnnotationTarget.FUNCTION)
|
||||
actual annotation class MoreTargetsOnExpect<!>
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPEALIAS)
|
||||
actual annotation class RepeatedTargetsInExpect
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
actual annotation class EmptyTargetsActual
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
expect annotation class ExpectIsSubsetOfActual
|
||||
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
expect annotation class ExpectIsSubsetOfActualDifferentOrder
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
expect annotation class MoreTargetsOnExpect
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
expect annotation class RepeatedTargetsInExpect
|
||||
|
||||
@Target(allowedTargets = [])
|
||||
expect annotation class EmptyTargetsActual
|
||||
|
||||
// MODULE: m1-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.CLASS)
|
||||
actual annotation class ExpectIsSubsetOfActual
|
||||
|
||||
@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.CLASS)
|
||||
actual annotation class ExpectIsSubsetOfActualDifferentOrder
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
actual annotation class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>MoreTargetsOnExpect<!>
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPEALIAS)
|
||||
actual annotation class RepeatedTargetsInExpect
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
actual annotation class EmptyTargetsActual
|
||||
Vendored
+2
-1
@@ -10,7 +10,8 @@
|
||||
AnnotationTarget.CONSTRUCTOR,
|
||||
AnnotationTarget.PROPERTY_SETTER,
|
||||
AnnotationTarget.PROPERTY_GETTER,
|
||||
// removed target TYPEALIAS
|
||||
AnnotationTarget.TYPEALIAS,
|
||||
AnnotationTarget.TYPE, // added target
|
||||
)
|
||||
expect annotation class MyDeprecatedNotMatch
|
||||
|
||||
|
||||
Vendored
+2
-1
@@ -10,7 +10,8 @@
|
||||
AnnotationTarget.CONSTRUCTOR,
|
||||
AnnotationTarget.PROPERTY_SETTER,
|
||||
AnnotationTarget.PROPERTY_GETTER,
|
||||
// removed target TYPEALIAS
|
||||
AnnotationTarget.TYPEALIAS,
|
||||
AnnotationTarget.TYPE, // added target
|
||||
)
|
||||
expect annotation class MyDeprecatedNotMatch
|
||||
|
||||
|
||||
Generated
+6
@@ -22771,6 +22771,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/annotationArgumentsDefaults.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationTarget.kt")
|
||||
public void testAnnotationTarget() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/annotationTarget.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationTypeParameters.kt")
|
||||
public void testAnnotationTypeParameters() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user