[KAPT] Keep constant references in annotations with array values

#KT-29929 Fixed
This commit is contained in:
Andrey Zinovyev
2021-02-10 16:17:02 +03:00
committed by TeamCityServer
parent 9e9450caa2
commit 8071a1e246
6 changed files with 141 additions and 34 deletions
@@ -55,4 +55,6 @@ fun pairedListToMap(valuePairs: List<Any>?): Map<String, Any?> {
operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList<T> {
return this.appendList(other)
}
}
fun <T : Any> Iterable<T>.toJavacList(): JavacList<T> = JavacList.from(this)
@@ -34,12 +34,9 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.kapt3.base.*
import org.jetbrains.kotlin.kapt3.base.javac.kaptError
import org.jetbrains.kotlin.kapt3.base.javac.reportKaptError
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.kapt3.base.pairedListToMap
import org.jetbrains.kotlin.kapt3.base.plus
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.base.stubs.KotlinPosition
import org.jetbrains.kotlin.kapt3.base.util.TopLevelJava9Aware
@@ -53,6 +50,7 @@ import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isOneSegmentFQN
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.ArrayFqNames
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -111,6 +109,10 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
private val JAVA_KEYWORDS = Tokens.TokenKind.values()
.filter { JAVA_KEYWORD_FILTER_REGEX.matches(it.toString().orEmpty()) }
.mapTo(hashSetOf(), Any::toString)
private val KOTLIN_PACKAGE = FqName("kotlin")
private val ARRAY_OF_FUNCTIONS = (ArrayFqNames.PRIMITIVE_TYPE_TO_ARRAY.values + ArrayFqNames.ARRAY_OF_FUNCTION).toSet()
}
private val correctErrorTypes = kaptContext.options[KaptFlag.CORRECT_ERROR_TYPES]
@@ -1240,11 +1242,6 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
private fun convertConstantValueArguments(containingClass: ClassNode, constantValue: Any?, args: List<KtExpression>): JCExpression? {
val singleArg = args.singleOrNull()
if (constantValue.isOfPrimitiveType()) {
// Do not inline primitive constants
tryParseReferenceToIntConstant(singleArg)?.let { return it }
}
fun tryParseTypeExpression(expression: KtExpression?): JCExpression? {
if (expression is KtReferenceExpression) {
val descriptor = kaptContext.bindingContext[BindingContext.REFERENCE_TARGET, expression]
@@ -1272,6 +1269,37 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
return treeMaker.Select(typeExpression, treeMaker.name("class"))
}
fun unwrapArgumentExpression(): List<KtExpression?>? =
when (singleArg) {
is KtCallExpression -> {
val resultingDescriptor = singleArg.getResolvedCall(kaptContext.bindingContext)?.resultingDescriptor
if (resultingDescriptor is FunctionDescriptor && isArrayOfFunction(resultingDescriptor))
singleArg.valueArguments.map { it.getArgumentExpression() }
else
null
}
is KtCollectionLiteralExpression -> singleArg.getInnerExpressions()
else -> null
}
if (constantValue.isOfPrimitiveType()) {
// Do not inline primitive constants
tryParseReferenceToIntConstant(singleArg)?.let { return it }
} else if (constantValue is List<*> &&
constantValue.isNotEmpty() &&
args.isNotEmpty() &&
constantValue.all { it.isOfPrimitiveType() }
) {
unwrapArgumentExpression()?.let { argumentExpressions ->
val parsed = argumentExpressions.mapNotNull(::tryParseReferenceToIntConstant).toJavacList()
if (parsed.size == argumentExpressions.size) {
return treeMaker.NewArray(null, null, parsed)
}
}
}
// Unresolved class literal
if (constantValue == null && singleArg is KtClassLiteralExpression) {
tryParseTypeLiteralExpression(singleArg)?.let { return it }
@@ -1287,19 +1315,7 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
// Probably arrayOf(SomeUnresolvedType::class, ...)
if (constantValue is List<*>) {
val callArgs = when (singleArg) {
is KtCallExpression -> {
val resultingDescriptor = singleArg.getResolvedCall(kaptContext.bindingContext)?.resultingDescriptor
if (resultingDescriptor is FunctionDescriptor && resultingDescriptor.fqNameSafe.asString() == "kotlin.arrayOf")
singleArg.valueArguments.map { it.getArgumentExpression() }
else
null
}
is KtCollectionLiteralExpression -> singleArg.getInnerExpressions()
else -> null
}
val callArgs = unwrapArgumentExpression()
// So we make sure something is absent in the constant value
if (callArgs != null && callArgs.size != constantValue.size) {
val literalExpressions = mapJList(callArgs, ::tryParseTypeLiteralExpression)
@@ -1498,6 +1514,10 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
file to importsFromRoot.mapTo(mutableSetOf()) { it.asString() }
}.toMap()
private fun isArrayOfFunction(d: FunctionDescriptor): Boolean {
val name = d.fqNameSafe
return name.parent() == KOTLIN_PACKAGE && ARRAY_OF_FUNCTIONS.contains(name.shortName())
}
}
@@ -64,6 +64,11 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotations3.kt");
}
@TestMetadata("annotationsWithConstants.kt")
public void testAnnotationsWithConstants() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotationsWithConstants.kt");
}
@TestMetadata("annotationsWithTargets.kt")
public void testAnnotationsWithTargets() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotationsWithTargets.kt");
@@ -314,11 +319,6 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
runTest("plugins/kapt3/kapt3-compiler/testData/converter/kt18682.kt");
}
@TestMetadata("kt18791.kt")
public void testKt18791() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/kt18791.kt");
}
@TestMetadata("kt19700.kt")
public void testKt19700() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/kt19700.kt");
@@ -65,6 +65,11 @@ public class IrClassFileToSourceStubConverterTestGenerated extends AbstractIrCla
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotations3.kt");
}
@TestMetadata("annotationsWithConstants.kt")
public void testAnnotationsWithConstants() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotationsWithConstants.kt");
}
@TestMetadata("annotationsWithTargets.kt")
public void testAnnotationsWithTargets() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/annotationsWithTargets.kt");
@@ -315,11 +320,6 @@ public class IrClassFileToSourceStubConverterTestGenerated extends AbstractIrCla
runTest("plugins/kapt3/kapt3-compiler/testData/converter/kt18682.kt");
}
@TestMetadata("kt18791.kt")
public void testKt18791() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/kt18791.kt");
}
@TestMetadata("kt19700.kt")
public void testKt19700() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/kt19700.kt");
@@ -52,6 +52,10 @@ import lib.R.id.textView
annotation class Bind(val id: Int)
annotation class MultiValue(val ids: IntArray)
annotation class MultiValueString(val ids: Array<String>)
annotation class MultiValueByte(val ids: ByteArray)
@Target(AnnotationTarget.FIELD)
annotation class BindField(val id: Int)
@@ -111,6 +115,24 @@ class MyActivity {
@Bind(B.id.textView)
fun plainIntConstant() {}
@MultiValue(ids = [])
fun multi0() {}
@MultiValue(ids = [B.id.textView])
fun multi1() {}
@MultiValue(ids = [B.id.textView, B.a3])
fun multi2() {}
@MultiValue(ids = intArrayOf(B.id.textView, B.a3))
fun multi3() {}
@MultiValueString(ids = arrayOf(B.a9))
fun multi4() {}
@MultiValueByte(ids = byteArrayOf(B.a2))
fun multi5() {}
const val propA = B.id.textView
val propB = B.id.textView
var propC = B.id.textView
@@ -129,4 +151,4 @@ object JJ {
// EXPECTED_ERROR class B is public, should be declared in a file named B.java
// EXPECTED_ERROR class R is public, should be declared in a file named R.java
// EXPECTED_ERROR class R is public, should be declared in a file named R.java
// EXPECTED_ERROR class R2 is public, should be declared in a file named R2.java
// EXPECTED_ERROR class R2 is public, should be declared in a file named R2.java
@@ -115,6 +115,45 @@ package app;
import java.lang.System;
@kotlin.Metadata()
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
public abstract @interface MultiValue {
public abstract int[] ids();
}
////////////////////
package app;
import java.lang.System;
@kotlin.Metadata()
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
public abstract @interface MultiValueByte {
public abstract byte[] ids();
}
////////////////////
package app;
import java.lang.System;
@kotlin.Metadata()
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
public abstract @interface MultiValueString {
public abstract java.lang.String[] ids();
}
////////////////////
package app;
import java.lang.System;
@kotlin.Metadata()
public final class MyActivity {
@BindField(id = lib.R.id.textView)
@@ -222,6 +261,30 @@ public final class MyActivity {
public final void plainIntConstant() {
}
@MultiValue(ids = {})
public final void multi0() {
}
@MultiValue(ids = {app.B.id.textView})
public final void multi1() {
}
@MultiValue(ids = {app.B.id.textView, app.B.a3})
public final void multi2() {
}
@MultiValue(ids = {app.B.id.textView, app.B.a3})
public final void multi3() {
}
@MultiValueString(ids = {"A"})
public final void multi4() {
}
@MultiValueByte(ids = {app.B.a2})
public final void multi5() {
}
public final int getPropB() {
return 0;
}