Kapt: Support 'correctErrorTypes' in annotations (KT-19518)

This commit is contained in:
Yan Zhulanow
2017-09-27 23:20:49 +03:00
parent 91ed130286
commit 1b5b9f1fbb
4 changed files with 127 additions and 220 deletions
@@ -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();