diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java index 0ad61eb1e58..94ab977b442 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java @@ -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> resolveAnnotationArguments( - @NotNull ResolvedCall resolvedCall, - @NotNull BindingTrace trace - ) { - Map> arguments = new HashMap>(); - for (Map.Entry 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> compileTimeConstants = resolveValueArguments(resolvedArgument, constantType, trace); - List> constants = KotlinPackage.map(compileTimeConstants, new Function1, 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, 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, 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 argumentEntry = resolvedCall.getValueArguments().entrySet().iterator().next(); - - List 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, JetType>(result, elementType); - } - - private static boolean hasSpread(@NotNull ResolvedValueArgument argument) { - List arguments = argument.getArguments(); - return arguments.size() == 1 && arguments.get(0).getSpreadElement() != null; - } - - @NotNull - private static List> resolveValueArguments( - @NotNull ResolvedValueArgument resolvedValueArgument, - @NotNull JetType expectedType, - @NotNull BindingTrace trace - ) { - List> 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.newArrayList()), - SymbolUsageValidator.Empty, - new AdditionalTypeChecker.Composite(Lists.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); + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt index 1fdccda7bff..5a48adfa2ea 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt @@ -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> { + val arguments = HashMap>() + 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, 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() + for (valueArgument in argumentEntry.getValue().getArguments()) { + val valueArgumentExpression = valueArgument.getArgumentExpression() + if (valueArgumentExpression != null) { + result.add(valueArgumentExpression) + } + } + + return Pair, 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> { + val constants = Lists.newArrayList>() + 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()), + SymbolUsageValidator.Empty, + AdditionalTypeChecker.Composite(Lists.newArrayList()), + 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 ConstantValue.wrap( - canBeUsedInAnnotation: Boolean = this !is NullValue, + canBeUsedInAnnotation: Boolean = this !is NullValue, isPure: Boolean = false, usesVariableAsConstant: Boolean = false ): TypedCompileTimeConstant diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt index 48868f41499..68fbd9bd134 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt @@ -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> }