Kapt: Find the right annotation descriptor in case of repeatable annotations (#KT-23427)
This commit is contained in:
+61
-10
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
|||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.*
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
@@ -94,6 +95,9 @@ class ClassFileToSourceStubConverter(
|
|||||||
val bindings: Map<String, KaptJavaFileObject>
|
val bindings: Map<String, KaptJavaFileObject>
|
||||||
get() = _bindings
|
get() = _bindings
|
||||||
|
|
||||||
|
private val typeMapper
|
||||||
|
get() = kaptContext.generationState.typeMapper
|
||||||
|
|
||||||
val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
|
val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
|
||||||
|
|
||||||
private val signatureParser = SignatureParser(treeMaker)
|
private val signatureParser = SignatureParser(treeMaker)
|
||||||
@@ -563,7 +567,7 @@ class ClassFileToSourceStubConverter(
|
|||||||
|
|
||||||
val superClassConstructorCall = if (superClassConstructor != null) {
|
val superClassConstructorCall = if (superClassConstructor != null) {
|
||||||
val args = mapJList(superClassConstructor.valueParameters) { param ->
|
val args = mapJList(superClassConstructor.valueParameters) { param ->
|
||||||
convertLiteralExpression(getDefaultValue(kaptContext.generationState.typeMapper.mapType(param.type)))
|
convertLiteralExpression(getDefaultValue(typeMapper.mapType(param.type)))
|
||||||
}
|
}
|
||||||
val call = treeMaker.Apply(JavacList.nil(), treeMaker.SimpleName("super"), args)
|
val call = treeMaker.Apply(JavacList.nil(), treeMaker.SimpleName("super"), args)
|
||||||
JavacList.of<JCStatement>(treeMaker.Exec(call))
|
JavacList.of<JCStatement>(treeMaker.Exec(call))
|
||||||
@@ -714,17 +718,14 @@ class ClassFileToSourceStubConverter(
|
|||||||
invisibleAnnotations: List<AnnotationNode>?,
|
invisibleAnnotations: List<AnnotationNode>?,
|
||||||
descriptorAnnotations: Annotations
|
descriptorAnnotations: Annotations
|
||||||
): JCModifiers {
|
): JCModifiers {
|
||||||
fun findDescriptorAnnotation(anno: AnnotationNode): AnnotationDescriptor? {
|
fun convertAndAdd(list: JavacList<JCAnnotation>, anno: AnnotationNode): JavacList<JCAnnotation> {
|
||||||
val annoFqName = treeMaker.getQualifiedName(Type.getType(anno.desc))
|
val annotationDescriptor = descriptorAnnotations.singleOrNull { checkIfAnnotationValueMatches(anno, AnnotationValue(it)) }
|
||||||
return descriptorAnnotations.findAnnotation(FqName(annoFqName))
|
val annotationTree = convertAnnotation(anno, packageFqName, annotationDescriptor) ?: return list
|
||||||
|
return list.prepend(annotationTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
var annotations = visibleAnnotations?.fold(JavacList.nil<JCAnnotation>()) { list, anno ->
|
var annotations = visibleAnnotations?.fold(JavacList.nil<JCAnnotation>(), ::convertAndAdd) ?: JavacList.nil()
|
||||||
convertAnnotation(anno, packageFqName, findDescriptorAnnotation(anno))?.let { list.prepend(it) } ?: list
|
annotations = invisibleAnnotations?.fold(annotations, ::convertAndAdd) ?: annotations
|
||||||
} ?: JavacList.nil()
|
|
||||||
annotations = invisibleAnnotations?.fold(annotations) { list, anno ->
|
|
||||||
convertAnnotation(anno, packageFqName, findDescriptorAnnotation(anno))?.let { list.prepend(it) } ?: list
|
|
||||||
} ?: annotations
|
|
||||||
|
|
||||||
val flags = when (kind) {
|
val flags = when (kind) {
|
||||||
ElementKind.ENUM -> access and CLASS_MODIFIERS and Opcodes.ACC_ABSTRACT.inv().toLong()
|
ElementKind.ENUM -> access and CLASS_MODIFIERS and Opcodes.ACC_ABSTRACT.inv().toLong()
|
||||||
@@ -877,6 +878,56 @@ class ClassFileToSourceStubConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkIfAnnotationValueMatches(asm: Any?, desc: ConstantValue<*>): Boolean {
|
||||||
|
return when (asm) {
|
||||||
|
null -> desc.value == null
|
||||||
|
is Char -> desc is CharValue && desc.value == asm
|
||||||
|
is Byte -> desc is ByteValue && desc.value == asm
|
||||||
|
is Short -> desc is ShortValue && desc.value == asm
|
||||||
|
is Boolean -> desc is BooleanValue && desc.value == asm
|
||||||
|
is Int -> desc is IntValue && desc.value == asm
|
||||||
|
is Long -> desc is LongValue && desc.value == asm
|
||||||
|
is Float -> desc is FloatValue && desc.value == asm
|
||||||
|
is Double -> desc is DoubleValue && desc.value == asm
|
||||||
|
is String -> desc is StringValue && desc.value == asm
|
||||||
|
is ByteArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is BooleanArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is CharArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is ShortArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is IntArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is LongArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is FloatArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is DoubleArray -> desc is ArrayValue && desc.value.size == asm.size
|
||||||
|
is Array<*> -> { // Two-element String array for enumerations ([desc, fieldName])
|
||||||
|
assert(asm.size == 2)
|
||||||
|
val valueName = (asm[1] as String).takeIf { isValidIdentifier(it) } ?: return false
|
||||||
|
// It's not that easy to check types here because of fqName/internalName differences.
|
||||||
|
// But enums can't extend other enums, so this should be enough.
|
||||||
|
desc is EnumValue && desc.enumEntryName.asString() == valueName
|
||||||
|
}
|
||||||
|
is List<*> -> {
|
||||||
|
desc is ArrayValue
|
||||||
|
&& asm.size == desc.value.size
|
||||||
|
&& asm.zip(desc.value).all { (eAsm, eDesc) -> checkIfAnnotationValueMatches(eAsm, eDesc) }
|
||||||
|
}
|
||||||
|
is Type -> desc is KClassValue && typeMapper.mapType(desc.value) == asm
|
||||||
|
is AnnotationNode -> {
|
||||||
|
val annotationDescriptor = (desc as? AnnotationValue)?.value ?: return false
|
||||||
|
if (typeMapper.mapType(annotationDescriptor.type).descriptor != asm.desc) return false
|
||||||
|
val asmAnnotationArgs = pairedListToMap(asm.values)
|
||||||
|
if (annotationDescriptor.allValueArguments.size != asmAnnotationArgs.size) return false
|
||||||
|
|
||||||
|
for ((descName, descValue) in annotationDescriptor.allValueArguments) {
|
||||||
|
val asmValue = asmAnnotationArgs[descName.asString()] ?: return false
|
||||||
|
if (!checkIfAnnotationValueMatches(asmValue, descValue)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun convertLiteralExpression(value: Any?): JCExpression {
|
private fun convertLiteralExpression(value: Any?): JCExpression {
|
||||||
convertValueOfPrimitiveTypeOrString(value)?.let { return it }
|
convertValueOfPrimitiveTypeOrString(value)?.let { return it }
|
||||||
|
|
||||||
|
|||||||
+6
@@ -343,6 +343,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("repeatableAnnotations.kt")
|
||||||
|
public void testRepeatableAnnotations() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/kapt3-compiler/testData/converter/repeatableAnnotations.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("severalPackageParts.kt")
|
@TestMetadata("severalPackageParts.kt")
|
||||||
public void testSeveralPackageParts() throws Exception {
|
public void testSeveralPackageParts() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/kapt3-compiler/testData/converter/severalPackageParts.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/kapt3-compiler/testData/converter/severalPackageParts.kt");
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// FILE: lib/Anno.java
|
||||||
|
package lib;
|
||||||
|
public @interface Anno {
|
||||||
|
String[] construct() default {};
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
//FILE: lib/R.java
|
||||||
|
package lib;
|
||||||
|
|
||||||
|
public class R {
|
||||||
|
public static class id {
|
||||||
|
public final static int textView = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
// WITH_RUNTIME
|
||||||
|
import lib.Anno
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
@Anno("1")
|
||||||
|
@Anno(value = "2", construct = ["A", "B"])
|
||||||
|
@Anno("3", construct = ["C"])
|
||||||
|
val value: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
annotation class AnnoChar(val x: Int, val chr: Char)
|
||||||
|
annotation class AnnoBoolean(val x: Int, val bool: Boolean)
|
||||||
|
annotation class AnnoInt(val x: Int, val i: Int)
|
||||||
|
annotation class AnnoLong(val x: Int, val l: Long)
|
||||||
|
annotation class AnnoFloat(val x: Int, val flt: Float)
|
||||||
|
annotation class AnnoDouble(val x: Int, val dbl: Double)
|
||||||
|
|
||||||
|
annotation class AnnoString(val x: Int, val s: String)
|
||||||
|
|
||||||
|
annotation class AnnoIntArray(val x: Int, val b: IntArray)
|
||||||
|
annotation class AnnoLongArray(val x: Int, val b: LongArray)
|
||||||
|
|
||||||
|
annotation class AnnoArray(val x: Int, val a: Array<String>)
|
||||||
|
|
||||||
|
annotation class AnnoClass(val x: Int, val c: KClass<Color>)
|
||||||
|
|
||||||
|
enum class Color { BLACK }
|
||||||
|
annotation class AnnoEnum(val x: Int, val c: Color)
|
||||||
|
|
||||||
|
@AnnoChar(lib.R.id.textView, 'c')
|
||||||
|
@AnnoBoolean(lib.R.id.textView, false)
|
||||||
|
@AnnoInt(lib.R.id.textView, 5)
|
||||||
|
@AnnoFloat(lib.R.id.textView, 1.0f)
|
||||||
|
@AnnoDouble(lib.R.id.textView, 4.0)
|
||||||
|
@AnnoString(lib.R.id.textView, "AAA")
|
||||||
|
@AnnoIntArray(lib.R.id.textView, [1, 2, 3])
|
||||||
|
@AnnoLongArray(lib.R.id.textView, [1L, 3L])
|
||||||
|
@AnnoArray(lib.R.id.textView, [ "A", "B" ])
|
||||||
|
@AnnoClass(lib.R.id.textView, Color::class)
|
||||||
|
class Test2
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoArray {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract java.lang.String[] a();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoBoolean {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract boolean bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoChar {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract char chr();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoClass {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract java.lang.Class<Color> c();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoDouble {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract double dbl();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoEnum {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract Color c();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoFloat {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract float flt();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoInt {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract int i();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoIntArray {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract int[] b();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoLong {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract long l();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoLongArray {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract long[] b();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||||
|
public abstract @interface AnnoString {
|
||||||
|
|
||||||
|
public abstract int x();
|
||||||
|
|
||||||
|
public abstract java.lang.String s();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
public enum Color {
|
||||||
|
/*public static final*/ BLACK /* = new Color() */;
|
||||||
|
|
||||||
|
Color() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
public final class Test {
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final java.lang.String value = "";
|
||||||
|
|
||||||
|
@lib.Anno(value = "3", construct = {"C"})
|
||||||
|
@lib.Anno(value = "2", construct = {"A", "B"})
|
||||||
|
@lib.Anno(value = "1")
|
||||||
|
public static void value$annotations() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public final java.lang.String getValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Test() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System;
|
||||||
|
|
||||||
|
@kotlin.Metadata()
|
||||||
|
@AnnoClass(x = lib.R.id.textView, c = Color.class)
|
||||||
|
@AnnoArray(x = lib.R.id.textView, a = {"A", "B"})
|
||||||
|
@AnnoLongArray(x = lib.R.id.textView, b = {1L, 3L})
|
||||||
|
@AnnoIntArray(x = lib.R.id.textView, b = {1, 2, 3})
|
||||||
|
@AnnoString(x = lib.R.id.textView, s = "AAA")
|
||||||
|
@AnnoDouble(x = lib.R.id.textView, dbl = 4.0)
|
||||||
|
@AnnoFloat(x = lib.R.id.textView, flt = 1.0F)
|
||||||
|
@AnnoInt(x = lib.R.id.textView, i = 5)
|
||||||
|
@AnnoBoolean(x = lib.R.id.textView, bool = false)
|
||||||
|
@AnnoChar(x = lib.R.id.textView, chr = 'c')
|
||||||
|
public final class Test2 {
|
||||||
|
|
||||||
|
public Test2() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
package lib;
|
||||||
|
|
||||||
|
public @interface Anno {
|
||||||
|
|
||||||
|
String[] construct() default {};
|
||||||
|
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
package lib;
|
||||||
|
|
||||||
|
public class R {
|
||||||
|
|
||||||
|
public R() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class id {
|
||||||
|
|
||||||
|
public id() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
public static final int textView = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user