Kapt: Support 'correctErrorTypes' in annotations (KT-19518)
This commit is contained in:
+95
-19
@@ -35,7 +35,10 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -557,7 +560,7 @@ class ClassFileToSourceStubConverter(
|
||||
return name.pathSegments().all { getValidIdentifierName(it.asString(), false) != null }
|
||||
}
|
||||
|
||||
fun getValidIdentifierName(name: String, canBeConstructor: Boolean = false): String? {
|
||||
private fun getValidIdentifierName(name: String, canBeConstructor: Boolean = false): String? {
|
||||
if (canBeConstructor && name == "<init>") {
|
||||
return name
|
||||
}
|
||||
@@ -632,29 +635,102 @@ class ClassFileToSourceStubConverter(
|
||||
val argMapping = ktAnnotation?.calleeExpression
|
||||
?.getResolvedCall(kaptContext.bindingContext)?.valueArguments
|
||||
?.mapKeys { it.key.name.asString() }
|
||||
|
||||
fun getKtElementsForArgument(name: String): List<KtExpression?>? {
|
||||
val args = argMapping?.get(name) ?: return null
|
||||
return args.arguments.map { it.getArgumentExpression() }
|
||||
}
|
||||
?: emptyMap()
|
||||
|
||||
val useSimpleName = '.' in fqName && fqName.substringBeforeLast('.', "") == packageFqName
|
||||
val name = if (useSimpleName) treeMaker.FqName(fqName.substring(packageFqName!!.length + 1)) else treeMaker.Type(annotationType)
|
||||
val values = mapPairedValuesJList<JCExpression>(annotation.values) { key, value ->
|
||||
val name = getValidIdentifierName(key) ?: return@mapPairedValuesJList null
|
||||
val convertedExpression = convertLiteralExpression(value)
|
||||
|
||||
fun assign(expr: JCExpression) = treeMaker.Assign(treeMaker.SimpleName(name), expr)
|
||||
|
||||
if (value is Int) { // This may be a resource identifier
|
||||
val ktExpression = getKtElementsForArgument(name)?.singleOrNull()
|
||||
tryParseReferenceToAndroidResource(ktExpression)?.let { return@mapPairedValuesJList assign(it) }
|
||||
}
|
||||
|
||||
assign(convertedExpression)
|
||||
val annotationFqName = when {
|
||||
useSimpleName -> treeMaker.FqName(fqName.substring(packageFqName!!.length + 1))
|
||||
else -> treeMaker.Type(annotationType)
|
||||
}
|
||||
|
||||
return treeMaker.Annotation(name, values)
|
||||
val constantValues = pairedListToMap(annotation.values)
|
||||
|
||||
val values = if (argMapping.isNotEmpty()) {
|
||||
argMapping.mapNotNull { (parameterName, arg) ->
|
||||
if (arg is DefaultValueArgument) return@mapNotNull null
|
||||
convertAnnotationArgumentWithName(constantValues[parameterName], arg, parameterName)
|
||||
}
|
||||
} else {
|
||||
constantValues.mapNotNull { (parameterName, arg) ->
|
||||
convertAnnotationArgumentWithName(arg, null, parameterName)
|
||||
}
|
||||
}
|
||||
|
||||
return treeMaker.Annotation(annotationFqName, JavacList.from(values))
|
||||
}
|
||||
|
||||
private fun convertAnnotationArgumentWithName(constantValue: Any?, value: ResolvedValueArgument?, name: String): JCExpression? {
|
||||
val validName = getValidIdentifierName(name) ?: return null
|
||||
val expr = convertAnnotationArgument(constantValue, value) ?: return null
|
||||
return treeMaker.Assign(treeMaker.SimpleName(validName), expr)
|
||||
}
|
||||
|
||||
private fun convertAnnotationArgument(constantValue: Any?, value: ResolvedValueArgument?): JCExpression? {
|
||||
val args = value?.arguments?.mapNotNull { it.getArgumentExpression() } ?: emptyList()
|
||||
val singleArg by lazy { args.singleOrNull() }
|
||||
|
||||
if (constantValue is Int) {
|
||||
// This may be a resource identifier, we should not inline the id int
|
||||
tryParseReferenceToAndroidResource(singleArg)?.let { return it }
|
||||
}
|
||||
|
||||
fun tryParseTypeExpression(expression: KtExpression?): JCExpression? {
|
||||
return when (expression) {
|
||||
is KtSimpleNameExpression -> treeMaker.SimpleName(expression.getReferencedName())
|
||||
is KtDotQualifiedExpression -> {
|
||||
val selector = expression.selectorExpression as? KtSimpleNameExpression ?: return null
|
||||
val receiver = tryParseTypeExpression(expression.receiverExpression) ?: return null
|
||||
return treeMaker.Select(receiver, treeMaker.name(selector.getReferencedName()))
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun tryParseTypeLiteralExpression(expression: KtExpression?): JCExpression? {
|
||||
val literalExpression = expression as? KtClassLiteralExpression ?: return null
|
||||
val typeExpression = tryParseTypeExpression(literalExpression.receiverExpression) ?: return null
|
||||
return treeMaker.Select(typeExpression, treeMaker.name("class"))
|
||||
}
|
||||
|
||||
// Unresolved class literal
|
||||
if (constantValue == null && singleArg is KtClassLiteralExpression) {
|
||||
tryParseTypeLiteralExpression(singleArg)?.let { return it }
|
||||
}
|
||||
|
||||
// Some of class literals in vararg list are unresolved
|
||||
if (args.isNotEmpty() && args[0] is KtClassLiteralExpression && constantValue is List<*> && args.size != constantValue.size) {
|
||||
val literalExpressions = mapJList(args, ::tryParseTypeLiteralExpression)
|
||||
if (literalExpressions.size == args.size) {
|
||||
return treeMaker.NewArray(null, null, literalExpressions)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// So we make sure something is absent in the constant value
|
||||
if (callArgs != null && callArgs.size != constantValue.size) {
|
||||
val literalExpressions = mapJList(callArgs, ::tryParseTypeLiteralExpression)
|
||||
if (literalExpressions.size == callArgs.size) {
|
||||
return treeMaker.NewArray(null, null, literalExpressions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return convertLiteralExpression(constantValue)
|
||||
}
|
||||
|
||||
private fun tryParseReferenceToAndroidResource(expression: KtExpression?): JCExpression? {
|
||||
|
||||
@@ -54,6 +54,16 @@ internal inline fun <T> mapPairedValuesJList(valuePairs: List<Any>?, f: (String,
|
||||
return result.reverse()
|
||||
}
|
||||
|
||||
internal fun pairedListToMap(valuePairs: List<Any>?): Map<String, Any?> {
|
||||
val map = mutableMapOf<String, Any?>()
|
||||
|
||||
mapPairedValuesJList(valuePairs) { key, value ->
|
||||
map.put(key, value)
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
internal operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList<T> {
|
||||
return this.appendList(other)
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
// CORRECT_ERROR_TYPES
|
||||
// NON_EXISTENT_CLASS
|
||||
// NO_VALIDATION
|
||||
// WITH_RUNTIME
|
||||
|
||||
import java.util.Calendar
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Suppress("UNRESOLVED_REFERENCE")
|
||||
class Test {
|
||||
@Suppress("UNRESOLVED_REFERENCE", "ANNOTATION_PARAMETER_MUST_BE_CONST", "NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION", "UNSUPPORTED_FEATURE")
|
||||
@Anno(Blah::class, arrayOf(NoFoo1::class, NoBar1::class), [NoFoo2::class, String::class], Boolean::class, NoBar3::class)
|
||||
class Test/* {
|
||||
lateinit var a: ABC
|
||||
val b: ABC? = null
|
||||
val c: List<ABC>? = null
|
||||
@@ -43,4 +46,7 @@ class Test {
|
||||
fun <T> MyType<T>.f5(): java.lang.Class<Enum<*>>? = null
|
||||
}
|
||||
|
||||
class MyType<T>
|
||||
class MyType<T>
|
||||
|
||||
*/
|
||||
annotation class Anno(val a: KClass<*>, val b: Array<KClass<*>>, val c: Array<KClass<*>>, vararg val d: KClass<*>)
|
||||
@@ -1,214 +1,29 @@
|
||||
import java.util.Calendar;
|
||||
import kotlin.reflect.KClass;
|
||||
|
||||
@kotlin.Metadata()
|
||||
public final class MyType<T extends java.lang.Object> {
|
||||
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
public abstract @interface Anno {
|
||||
|
||||
public MyType() {
|
||||
super();
|
||||
}
|
||||
public abstract java.lang.Class<?> a();
|
||||
|
||||
public abstract java.lang.Class<?>[] b();
|
||||
|
||||
public abstract java.lang.Class<?>[] c();
|
||||
|
||||
public abstract java.lang.Class<?>[] d();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
import java.util.Calendar;
|
||||
import kotlin.reflect.KClass;
|
||||
|
||||
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"})
|
||||
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE", "ANNOTATION_PARAMETER_MUST_BE_CONST", "NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION", "UNSUPPORTED_FEATURE"})
|
||||
@kotlin.Metadata()
|
||||
@Anno(a = Blah.class, b = {NoFoo1.class, NoBar1.class}, c = {NoFoo2.class, String.class}, d = {Boolean.class, NoBar3.class})
|
||||
public final class Test {
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public ABC a;
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
private final ABC b = null;
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
private final java.util.List<ABC> c = null;
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
private final java.util.List<java.util.Map<BCD, ABC<? extends java.util.List<BCD>>>> d = null;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public java.util.List<? extends java.util.Map<? extends ABC, ? extends BCD>> e;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public ABC<?> f;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public java.util.List<?> g;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public ABC<java.lang.Integer, java.lang.String> h;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public Function2<ABC, java.util.List<BCD>, CDE> i;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public Function0<CDE> j;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public Function2<ABC, java.util.List<BCD>, CDE> k;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public ABC.BCD.EFG l;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
private final error.NonExistentClass m = null;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
private final java.lang.String n = "";
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends error.NonExistentClass>>>>>>>>>> o11;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<ABC>>>>>>>>> o10;
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public java.util.Calendar.Builder p;
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final ABC getA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setA(@org.jetbrains.annotations.NotNull()
|
||||
ABC p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
public final ABC getB() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
public final java.util.List<ABC> getC() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
public final java.util.List<java.util.Map<BCD, ABC<? extends java.util.List<BCD>>>> getD() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.util.List<? extends java.util.Map<? extends ABC, ? extends BCD>> getE() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setE(@org.jetbrains.annotations.NotNull()
|
||||
java.util.List<? extends java.util.Map<? extends ABC, ? extends BCD>> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final ABC<?> getF() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setF(@org.jetbrains.annotations.NotNull()
|
||||
ABC<?> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.util.List<?> getG() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setG(@org.jetbrains.annotations.NotNull()
|
||||
java.util.List<?> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final ABC<java.lang.Integer, java.lang.String> getH() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setH(@org.jetbrains.annotations.NotNull()
|
||||
ABC<java.lang.Integer, java.lang.String> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final Function2<ABC, java.util.List<BCD>, CDE> getI() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setI(@org.jetbrains.annotations.NotNull()
|
||||
Function2<ABC, java.util.List<BCD>, CDE> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final Function0<CDE> getJ() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setJ(@org.jetbrains.annotations.NotNull()
|
||||
Function0<CDE> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final Function2<ABC, java.util.List<BCD>, CDE> getK() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setK(@org.jetbrains.annotations.NotNull()
|
||||
Function2<ABC, java.util.List<BCD>, CDE> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final ABC.BCD.EFG getL() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setL(@org.jetbrains.annotations.NotNull()
|
||||
ABC.BCD.EFG p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final error.NonExistentClass getM() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String getN() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.util.List<java.util.List<java.util.List<java.util.List<java.util.List<java.util.List<java.util.List<java.util.List<java.util.List<java.util.List<error.NonExistentClass>>>>>>>>>> getO11() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setO11(@org.jetbrains.annotations.NotNull()
|
||||
java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends error.NonExistentClass>>>>>>>>>> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<ABC>>>>>>>>> getO10() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setO10(@org.jetbrains.annotations.NotNull()
|
||||
java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<? extends java.util.List<ABC>>>>>>>>> p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.util.Calendar.Builder getP() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setP(@org.jetbrains.annotations.NotNull()
|
||||
java.util.Calendar.Builder p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
public final BCD f1(@org.jetbrains.annotations.NotNull()
|
||||
ABC a) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final <T extends java.lang.Object>void f2(@org.jetbrains.annotations.NotNull()
|
||||
ABC<java.lang.String, java.lang.Integer, Function0<BCD>> a) {
|
||||
}
|
||||
|
||||
public final <T extends java.lang.Object>long f3(@org.jetbrains.annotations.NotNull()
|
||||
ABC a, int b) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final error.NonExistentClass f4() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.Nullable()
|
||||
public final <T extends java.lang.Object>java.lang.Class<java.lang.Enum<?>> f5(@org.jetbrains.annotations.NotNull()
|
||||
MyType<T> $receiver) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Test() {
|
||||
super();
|
||||
|
||||
Reference in New Issue
Block a user