Apply constant folding for collection literals to use in annotations
Currently this is achieved with several hacks: - Postpone computation of argument type info when there is no candidate resolver. We have to do this, because we don't have expected type and therefore we could write wrong information to trace - Presume that for annotation calls there is only one candidate resolver and then resolve arguments with expected type (see `getArgumentTypeInfo`), otherwise because of quadratic complexity of the algorithm resolve would be slow
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,6 +36,8 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
import org.jetbrains.kotlin.storage.StorageManager;
|
||||
import org.jetbrains.kotlin.types.ErrorUtils;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -216,6 +216,14 @@ public class ArgumentTypeResolver {
|
||||
return getCallableReferenceTypeInfo(expression, callableReferenceExpression, context, resolveArgumentsMode);
|
||||
}
|
||||
|
||||
if (isCollectionLiteralInsideAnnotation(expression, context)) {
|
||||
// We assume that there is only one candidate resolver for annotation call
|
||||
// And to resolve collection literal correctly, we need mapping of argument to parameter to get expected type and
|
||||
// to choose corresponding call (i.e arrayOf/intArrayOf...)
|
||||
ResolutionContext newContext = context.replaceContextDependency(INDEPENDENT);
|
||||
return expressionTypingServices.getTypeInfo(expression, newContext);
|
||||
}
|
||||
|
||||
KotlinTypeInfo recordedTypeInfo = getRecordedTypeInfo(expression, context.trace.getBindingContext());
|
||||
if (recordedTypeInfo != null) {
|
||||
return recordedTypeInfo;
|
||||
@@ -356,6 +364,10 @@ public class ArgumentTypeResolver {
|
||||
KtExpression expression = argument.getArgumentExpression();
|
||||
if (expression == null) continue;
|
||||
|
||||
if (isCollectionLiteralInsideAnnotation(expression, context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CallResolutionContext<?> newContext = context.replaceDataFlowInfo(infoForArguments.getInfo(argument));
|
||||
// Here we go inside arguments and determine additional data flow information for them
|
||||
KotlinTypeInfo typeInfoForCall = getArgumentTypeInfo(expression, newContext, resolveArgumentsMode);
|
||||
@@ -379,4 +391,8 @@ public class ArgumentTypeResolver {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isCollectionLiteralInsideAnnotation(KtExpression expression, CallResolutionContext<?> context) {
|
||||
return expression instanceof KtCollectionLiteralExpression && context.call.getCallElement() instanceof KtAnnotationEntry;
|
||||
}
|
||||
}
|
||||
|
||||
+46
-18
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.COLLECTION_LITERAL_CALL
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
@@ -125,12 +126,10 @@ class ConstantExpressionEvaluator(
|
||||
|
||||
// array(1, <!>null<!>, 3) - error should be reported on inner expression
|
||||
if (argumentExpression is KtCallExpression) {
|
||||
val arrayArgument = getArgumentExpressionsForArrayCall(argumentExpression, trace)
|
||||
if (arrayArgument != null) {
|
||||
for (expression in arrayArgument.first) {
|
||||
checkCompileTimeConstant(expression, arrayArgument.second!!, trace)
|
||||
}
|
||||
}
|
||||
getArgumentExpressionsForArrayCall(argumentExpression, trace)?.let { checkArgumentsAreCompileTimeConstants(it, trace) }
|
||||
}
|
||||
if (argumentExpression is KtCollectionLiteralExpression) {
|
||||
getArgumentExpressionsForCollectionLiteralCall(argumentExpression, trace)?.let { checkArgumentsAreCompileTimeConstants(it, trace) }
|
||||
}
|
||||
|
||||
val constant = ConstantExpressionEvaluator.getConstant(argumentExpression, trace.bindingContext)
|
||||
@@ -164,12 +163,30 @@ class ConstantExpressionEvaluator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkArgumentsAreCompileTimeConstants(argumentsWithComponentType: Pair<List<KtExpression>, KotlinType?>, trace: BindingTrace) {
|
||||
val (arguments, componentType) = argumentsWithComponentType
|
||||
for (expression in arguments) {
|
||||
checkCompileTimeConstant(expression, componentType!!, trace)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getArgumentExpressionsForArrayCall(
|
||||
expression: KtCallExpression,
|
||||
trace: BindingTrace
|
||||
): Pair<List<KtExpression>, KotlinType?>? {
|
||||
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
|
||||
if (resolvedCall == null || !CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)) {
|
||||
val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null
|
||||
return getArgumentExpressionsForArrayLikeCall(resolvedCall)
|
||||
}
|
||||
|
||||
fun getArgumentExpressionsForCollectionLiteralCall(
|
||||
expression: KtCollectionLiteralExpression,
|
||||
trace: BindingTrace): Pair<List<KtExpression>, KotlinType?>? {
|
||||
val resolvedCall = trace[COLLECTION_LITERAL_CALL, expression] ?: return null
|
||||
return getArgumentExpressionsForArrayLikeCall(resolvedCall)
|
||||
}
|
||||
|
||||
private fun getArgumentExpressionsForArrayLikeCall(resolvedCall: ResolvedCall<*>): Pair<List<KtExpression>, KotlinType>? {
|
||||
if (!CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -448,6 +465,11 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCollectionLiteralExpression(expression: KtCollectionLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
||||
val resolvedCall = trace.bindingContext[COLLECTION_LITERAL_CALL, expression] ?: return null
|
||||
return createConstantValueForArrayFunctionCall(resolvedCall)
|
||||
}
|
||||
|
||||
private fun evaluateCall(callExpression: KtExpression, receiverExpression: KtExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
||||
val resolvedCall = callExpression.getResolvedCall(trace.bindingContext) ?: return null
|
||||
|
||||
@@ -673,16 +695,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
|
||||
// arrayOf() or emptyArray()
|
||||
if (CompileTimeConstantUtils.isArrayFunctionCall(call)) {
|
||||
val returnType = resultingDescriptor.returnType ?: return null
|
||||
val componentType = constantExpressionEvaluator.builtIns.getArrayElementType(returnType)
|
||||
|
||||
val arguments = call.valueArguments.values.flatMap { resolveArguments(it.arguments, componentType) }
|
||||
|
||||
return factory.createArrayValue(arguments.map { it.toConstantValue(componentType) }, resultingDescriptor.returnType!!).
|
||||
wrap(
|
||||
usesVariableAsConstant = arguments.any { it.usesVariableAsConstant },
|
||||
usesNonConstValAsConstant = arguments.any { it.usesNonConstValAsConstant }
|
||||
)
|
||||
return createConstantValueForArrayFunctionCall(call)
|
||||
}
|
||||
|
||||
// Ann()
|
||||
@@ -701,6 +714,21 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createConstantValueForArrayFunctionCall(
|
||||
call: ResolvedCall<*>
|
||||
): TypedCompileTimeConstant<List<ConstantValue<*>>>? {
|
||||
val returnType = call.resultingDescriptor.returnType ?: return null
|
||||
val componentType = constantExpressionEvaluator.builtIns.getArrayElementType(returnType)
|
||||
|
||||
val arguments = call.valueArguments.values.flatMap { resolveArguments(it.arguments, componentType) }
|
||||
|
||||
return factory.createArrayValue(arguments.map { it.toConstantValue(componentType) }, returnType)
|
||||
.wrap(
|
||||
usesVariableAsConstant = arguments.any { it.usesVariableAsConstant },
|
||||
usesNonConstValAsConstant = arguments.any { it.usesNonConstValAsConstant }
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
||||
val jetType = trace.getType(expression)!!
|
||||
if (jetType.isError) return null
|
||||
|
||||
Reference in New Issue
Block a user