Refactor: move annotation arguments resolve related utils from AnnotationResolver to ConstantExpressionEvaluator

This commit is contained in:
Pavel V. Talanov
2015-07-16 19:52:37 +03:00
parent a84da2bb0c
commit 855bff39fe
3 changed files with 169 additions and 181 deletions
@@ -17,51 +17,35 @@
package org.jetbrains.kotlin.resolve;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import kotlin.KotlinPackage;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
import org.jetbrains.kotlin.psi.JetAnnotationEntry;
import org.jetbrains.kotlin.psi.JetModifierList;
import org.jetbrains.kotlin.psi.JetTypeParameter;
import org.jetbrains.kotlin.psi.JetTypeReference;
import org.jetbrains.kotlin.resolve.calls.CallResolver;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker;
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker;
import org.jetbrains.kotlin.resolve.calls.checkers.CompositeChecker;
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
import org.jetbrains.kotlin.resolve.calls.context.SimpleResolutionContext;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor;
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationsContextImpl;
import org.jetbrains.kotlin.resolve.scopes.JetScope;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
import org.jetbrains.kotlin.storage.StorageManager;
import org.jetbrains.kotlin.types.ErrorUtils;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.jetbrains.kotlin.diagnostics.Errors.NOT_AN_ANNOTATION_CLASS;
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
@@ -210,153 +194,6 @@ public class AnnotationResolver {
);
}
@NotNull
public static Map<ValueParameterDescriptor, ConstantValue<?>> resolveAnnotationArguments(
@NotNull ResolvedCall<?> resolvedCall,
@NotNull BindingTrace trace
) {
Map<ValueParameterDescriptor, ConstantValue<?>> arguments = new HashMap<ValueParameterDescriptor, ConstantValue<?>>();
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> descriptorToArgument : resolvedCall.getValueArguments().entrySet()) {
ValueParameterDescriptor parameterDescriptor = descriptorToArgument.getKey();
ResolvedValueArgument resolvedArgument = descriptorToArgument.getValue();
ConstantValue<?> value = getAnnotationArgumentValue(trace, parameterDescriptor, resolvedArgument);
if (value != null) {
arguments.put(parameterDescriptor, value);
}
}
return arguments;
}
@Nullable
public static ConstantValue<?> getAnnotationArgumentValue(
BindingTrace trace,
ValueParameterDescriptor parameterDescriptor,
ResolvedValueArgument resolvedArgument
) {
JetType varargElementType = parameterDescriptor.getVarargElementType();
boolean argumentsAsVararg = varargElementType != null && !hasSpread(resolvedArgument);
final JetType constantType = argumentsAsVararg ? varargElementType : parameterDescriptor.getType();
List<CompileTimeConstant<?>> compileTimeConstants = resolveValueArguments(resolvedArgument, constantType, trace);
List<ConstantValue<?>> constants = KotlinPackage.map(compileTimeConstants, new Function1<CompileTimeConstant<?>, ConstantValue<?>>() {
@Override
public ConstantValue<?> invoke(CompileTimeConstant<?> constant) {
return constant.toConstantValue(constantType);
}
});
if (argumentsAsVararg) {
if (parameterDescriptor.declaresDefaultValue() && compileTimeConstants.isEmpty()) return null;
return new ArrayValue(constants, parameterDescriptor.getType());
}
else {
// we should actually get only one element, but just in case of getting many, we take the last one
return KotlinPackage.lastOrNull(constants);
}
}
private static void checkCompileTimeConstant(
@NotNull JetExpression argumentExpression,
@NotNull JetType expectedType,
@NotNull BindingTrace trace
) {
JetType expressionType = trace.getType(argumentExpression);
if (expressionType == null || !JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, expectedType)) {
// TYPE_MISMATCH should be reported otherwise
return;
}
// array(1, <!>null<!>, 3) - error should be reported on inner expression
if (argumentExpression instanceof JetCallExpression) {
Pair<List<JetExpression>, JetType> arrayArgument = getArgumentExpressionsForArrayCall((JetCallExpression) argumentExpression, trace);
if (arrayArgument != null) {
for (JetExpression expression : arrayArgument.getFirst()) {
checkCompileTimeConstant(expression, arrayArgument.getSecond(), trace);
}
}
}
CompileTimeConstant<?> constant = ConstantExpressionEvaluator.getConstant(argumentExpression, trace.getBindingContext());
if (constant != null && constant.getCanBeUsedInAnnotations()) {
return;
}
ClassifierDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor();
if (descriptor != null && DescriptorUtils.isEnumClass(descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST.on(argumentExpression));
}
else if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isKClass((ClassDescriptor) descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_KCLASS_LITERAL.on(argumentExpression));
}
else {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CONST.on(argumentExpression));
}
}
@Nullable
private static Pair<List<JetExpression>, JetType> getArgumentExpressionsForArrayCall(
@NotNull JetCallExpression expression,
@NotNull BindingTrace trace
) {
ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(expression, trace.getBindingContext());
if (resolvedCall == null || !CompileTimeConstantUtils.isArrayMethodCall(resolvedCall)) {
return null;
}
assert resolvedCall.getValueArguments().size() == 1 : "Array function should have only one vararg parameter";
Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> argumentEntry = resolvedCall.getValueArguments().entrySet().iterator().next();
List<JetExpression> result = Lists.newArrayList();
JetType elementType = argumentEntry.getKey().getVarargElementType();
for (ValueArgument valueArgument : argumentEntry.getValue().getArguments()) {
JetExpression valueArgumentExpression = valueArgument.getArgumentExpression();
if (valueArgumentExpression != null) {
if (elementType != null) {
result.add(valueArgumentExpression);
}
}
}
return new Pair<List<JetExpression>, JetType>(result, elementType);
}
private static boolean hasSpread(@NotNull ResolvedValueArgument argument) {
List<ValueArgument> arguments = argument.getArguments();
return arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
}
@NotNull
private static List<CompileTimeConstant<?>> resolveValueArguments(
@NotNull ResolvedValueArgument resolvedValueArgument,
@NotNull JetType expectedType,
@NotNull BindingTrace trace
) {
List<CompileTimeConstant<?>> constants = Lists.newArrayList();
for (ValueArgument argument : resolvedValueArgument.getArguments()) {
JetExpression argumentExpression = argument.getArgumentExpression();
if (argumentExpression != null) {
CompileTimeConstant<?> constant = ConstantExpressionEvaluator.evaluate(argumentExpression, trace, expectedType);
if (constant instanceof IntegerValueTypeConstant) {
JetType defaultType = ((IntegerValueTypeConstant) constant).getType(expectedType);
SimpleResolutionContext context =
new SimpleResolutionContext(trace, JetScope.Empty.INSTANCE$, NO_EXPECTED_TYPE, DataFlowInfo.EMPTY,
ContextDependency.INDEPENDENT,
new CompositeChecker(Lists.<CallChecker>newArrayList()),
SymbolUsageValidator.Empty,
new AdditionalTypeChecker.Composite(Lists.<AdditionalTypeChecker>newArrayList()),
StatementFilter.NONE);
ArgumentTypeResolver.updateNumberType(defaultType, argumentExpression, context);
}
if (constant != null) {
constants.add(constant);
}
checkCompileTimeConstant(argumentExpression, expectedType, trace);
}
}
return constants;
}
public static void reportUnsupportedAnnotationForTypeParameter(@NotNull JetTypeParameter jetTypeParameter, @NotNull BindingTrace trace) {
JetModifierList modifierList = jetTypeParameter.getModifierList();
if (modifierList == null) return;
@@ -365,4 +202,13 @@ public class AnnotationResolver {
trace.report(Errors.UNSUPPORTED.on(annotationEntry, "Annotations for type parameters are not supported yet"));
}
}
@Nullable
public ConstantValue<?> getAnnotationArgumentValue(
@NotNull BindingTrace trace,
@NotNull ValueParameterDescriptor valueParameter,
@NotNull ResolvedValueArgument resolvedArgument
) {
return ConstantExpressionEvaluator.getAnnotationArgumentValue(trace, valueParameter, resolvedArgument);
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve.constants.evaluate
import com.google.common.collect.Lists
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -29,19 +30,163 @@ import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CompositeChecker
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.context.SimpleResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import java.math.BigInteger
import java.util.HashMap
import kotlin.platform.platformStatic
public class ConstantExpressionEvaluator private constructor() {
public class ConstantExpressionEvaluator private constructor() {
companion object {
platformStatic public fun resolveAnnotationArguments(
resolvedCall: ResolvedCall<*>,
trace: BindingTrace
): Map<ValueParameterDescriptor, ConstantValue<*>> {
val arguments = HashMap<ValueParameterDescriptor, ConstantValue<*>>()
for ((parameterDescriptor, resolvedArgument) in resolvedCall.getValueArguments().entrySet()) {
val value = getAnnotationArgumentValue(trace, parameterDescriptor, resolvedArgument)
if (value != null) {
arguments.put(parameterDescriptor, value)
}
}
return arguments
}
platformStatic public fun getAnnotationArgumentValue(
trace: BindingTrace,
parameterDescriptor: ValueParameterDescriptor,
resolvedArgument: ResolvedValueArgument
): ConstantValue<*>? {
val varargElementType = parameterDescriptor.getVarargElementType()
val argumentsAsVararg = varargElementType != null && !hasSpread(resolvedArgument)
val constantType = if (argumentsAsVararg) varargElementType else parameterDescriptor.getType()
val compileTimeConstants = resolveValueArguments(resolvedArgument, constantType!!, trace)
val constants = compileTimeConstants.map { it.toConstantValue(constantType) }
if (argumentsAsVararg) {
if (parameterDescriptor.declaresDefaultValue() && compileTimeConstants.isEmpty()) return null
return ArrayValue(constants, parameterDescriptor.getType())
}
else {
// we should actually get only one element, but just in case of getting many, we take the last one
return constants.lastOrNull()
}
}
private fun checkCompileTimeConstant(
argumentExpression: JetExpression,
expectedType: JetType,
trace: BindingTrace
) {
val expressionType = trace.getType(argumentExpression)
if (expressionType == null || !JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, expectedType)) {
// TYPE_MISMATCH should be reported otherwise
return
}
// array(1, <!>null<!>, 3) - error should be reported on inner expression
if (argumentExpression is JetCallExpression) {
val arrayArgument = getArgumentExpressionsForArrayCall(argumentExpression, trace)
if (arrayArgument != null) {
for (expression in arrayArgument.first) {
checkCompileTimeConstant(expression, arrayArgument.second!!, trace)
}
}
}
val constant = ConstantExpressionEvaluator.getConstant(argumentExpression, trace.getBindingContext())
if (constant != null && constant.canBeUsedInAnnotations) {
return
}
val descriptor = expressionType.getConstructor().getDeclarationDescriptor()
if (descriptor != null && DescriptorUtils.isEnumClass(descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST.on(argumentExpression))
}
else if (descriptor is ClassDescriptor && KotlinBuiltIns.isKClass(descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_KCLASS_LITERAL.on(argumentExpression))
}
else {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CONST.on(argumentExpression))
}
}
private fun getArgumentExpressionsForArrayCall(
expression: JetCallExpression,
trace: BindingTrace
): Pair<List<JetExpression>, JetType?>? {
val resolvedCall = expression.getResolvedCall(trace.getBindingContext())
if (resolvedCall == null || !CompileTimeConstantUtils.isArrayMethodCall(resolvedCall)) {
return null
}
assert(resolvedCall.getValueArguments().size() == 1, "Array function should have only one vararg parameter")
val argumentEntry = resolvedCall.getValueArguments().entrySet().iterator().next()
val elementType = argumentEntry.getKey().getVarargElementType() ?: return null
val result = arrayListOf<JetExpression>()
for (valueArgument in argumentEntry.getValue().getArguments()) {
val valueArgumentExpression = valueArgument.getArgumentExpression()
if (valueArgumentExpression != null) {
result.add(valueArgumentExpression)
}
}
return Pair<List<JetExpression>, JetType>(result, elementType)
}
private fun hasSpread(argument: ResolvedValueArgument): Boolean {
val arguments = argument.getArguments()
return arguments.size() == 1 && arguments.get(0).getSpreadElement() != null
}
private fun resolveValueArguments(
resolvedValueArgument: ResolvedValueArgument,
expectedType: JetType,
trace: BindingTrace): List<CompileTimeConstant<*>> {
val constants = Lists.newArrayList<CompileTimeConstant<*>>()
for (argument in resolvedValueArgument.getArguments()) {
val argumentExpression = argument.getArgumentExpression() ?: continue
val constant = ConstantExpressionEvaluator.evaluate(argumentExpression, trace, expectedType)
if (constant is IntegerValueTypeConstant) {
val defaultType = constant.getType(expectedType)
val context = SimpleResolutionContext(trace, JetScope.Empty, TypeUtils.NO_EXPECTED_TYPE, DataFlowInfo.EMPTY,
ContextDependency.INDEPENDENT,
CompositeChecker(Lists.newArrayList<CallChecker>()),
SymbolUsageValidator.Empty,
AdditionalTypeChecker.Composite(Lists.newArrayList<AdditionalTypeChecker>()),
StatementFilter.NONE)
ArgumentTypeResolver.updateNumberType(defaultType, argumentExpression, context)
}
if (constant != null) {
constants.add(constant)
}
checkCompileTimeConstant(argumentExpression, expectedType, trace)
}
return constants
}
platformStatic public fun evaluate(expression: JetExpression, trace: BindingTrace, expectedType: JetType? = TypeUtils.NO_EXPECTED_TYPE): CompileTimeConstant<*>? {
val visitor = ConstantExpressionEvaluatorVisitor(trace, KotlinBuiltIns.getInstance())
val constant = visitor.evaluate(expression, expectedType) ?: return null
@@ -118,12 +263,12 @@ private class ConstantExpressionEvaluatorVisitor(private val trace: BindingTrace
if (nodeElementType == JetNodeTypes.NULL) return factory.createNullValue().wrap()
val result: Any? = when (nodeElementType) {
JetNodeTypes.INTEGER_CONSTANT -> parseLong(text)
JetNodeTypes.FLOAT_CONSTANT -> parseFloatingLiteral(text)
JetNodeTypes.BOOLEAN_CONSTANT -> parseBoolean(text)
JetNodeTypes.CHARACTER_CONSTANT -> CompileTimeConstantChecker.parseChar(expression)
else -> throw IllegalArgumentException("Unsupported constant: " + expression)
} ?: return null
JetNodeTypes.INTEGER_CONSTANT -> parseLong(text)
JetNodeTypes.FLOAT_CONSTANT -> parseFloatingLiteral(text)
JetNodeTypes.BOOLEAN_CONSTANT -> parseBoolean(text)
JetNodeTypes.CHARACTER_CONSTANT -> CompileTimeConstantChecker.parseChar(expression)
else -> throw IllegalArgumentException("Unsupported constant: " + expression)
} ?: return null
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createConstant(result, expectedType, CompileTimeConstant.Parameters(true, !isLongWithSuffix(), false))
@@ -427,7 +572,7 @@ private class ConstantExpressionEvaluatorVisitor(private val trace: BindingTrace
if (DescriptorUtils.isAnnotationClass(classDescriptor)) {
val descriptor = AnnotationDescriptorImpl(
classDescriptor.getDefaultType(),
AnnotationResolver.resolveAnnotationArguments(call, trace)
ConstantExpressionEvaluator.resolveAnnotationArguments(call, trace)
)
return AnnotationValue(descriptor).wrap()
}
@@ -536,7 +681,7 @@ private class ConstantExpressionEvaluatorVisitor(private val trace: BindingTrace
= TypedCompileTimeConstant(this, parameters)
private fun <T> ConstantValue<T>.wrap(
canBeUsedInAnnotation: Boolean = this !is NullValue,
canBeUsedInAnnotation: Boolean = this !is NullValue,
isPure: Boolean = false,
usesVariableAsConstant: Boolean = false
): TypedCompileTimeConstant<T>
@@ -21,10 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetAnnotationEntry
import org.jetbrains.kotlin.resolve.AnnotationResolver
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.lazy.LazyEntity
@@ -119,7 +116,7 @@ public class LazyAnnotationDescriptor(
return resolutionResults.getResultingCall().getValueArguments()
.mapValues { val (valueParameter, resolvedArgument) = it;
if (resolvedArgument == null) null
else AnnotationResolver.getAnnotationArgumentValue(c.trace, valueParameter, resolvedArgument)
else c.annotationResolver.getAnnotationArgumentValue(c.trace, valueParameter, resolvedArgument)
}
.filterValues { it != null } as Map<ValueParameterDescriptor, ConstantValue<*>>
}