
org.jetbrains.jet.lang.resolve.AnnotationResolver Maven / Gradle / Ivy
/*
* Copyright 2010-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationsImpl;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.ArgumentTypeResolver;
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyAnnotationDescriptor;
import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyAnnotationsContext;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.storage.StorageManager;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.jetbrains.jet.lang.diagnostics.Errors.NOT_AN_ANNOTATION_CLASS;
import static org.jetbrains.jet.lang.resolve.BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT;
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
public class AnnotationResolver {
private CallResolver callResolver;
private StorageManager storageManager;
private TypeResolver typeResolver;
@Inject
public void setCallResolver(CallResolver callResolver) {
this.callResolver = callResolver;
}
@Inject
public void setStorageManager(StorageManager storageManager) {
this.storageManager = storageManager;
}
@Inject
public void setTypeResolver(TypeResolver typeResolver) {
this.typeResolver = typeResolver;
}
@NotNull
public Annotations resolveAnnotationsWithoutArguments(
@NotNull JetScope scope,
@Nullable JetModifierList modifierList,
@NotNull BindingTrace trace
) {
return resolveAnnotations(scope, modifierList, trace, false);
}
@NotNull
public Annotations resolveAnnotationsWithArguments(
@NotNull JetScope scope,
@Nullable JetModifierList modifierList,
@NotNull BindingTrace trace
) {
return resolveAnnotations(scope, modifierList, trace, true);
}
@NotNull
public Annotations resolveAnnotationsWithArguments(
@NotNull JetScope scope,
@NotNull List annotationEntries,
@NotNull BindingTrace trace
) {
return resolveAnnotationEntries(scope, annotationEntries, trace, true);
}
private Annotations resolveAnnotations(
@NotNull JetScope scope,
@Nullable JetModifierList modifierList,
@NotNull BindingTrace trace,
boolean shouldResolveArguments
) {
if (modifierList == null) {
return Annotations.EMPTY;
}
List annotationEntryElements = modifierList.getAnnotationEntries();
return resolveAnnotationEntries(scope, annotationEntryElements, trace, shouldResolveArguments);
}
private Annotations resolveAnnotationEntries(
@NotNull final JetScope scope,
@NotNull List annotationEntryElements,
@NotNull BindingTrace trace,
boolean shouldResolveArguments
) {
if (annotationEntryElements.isEmpty()) return Annotations.EMPTY;
List result = Lists.newArrayList();
for (JetAnnotationEntry entryElement : annotationEntryElements) {
AnnotationDescriptor descriptor = trace.get(BindingContext.ANNOTATION, entryElement);
if (descriptor == null) {
descriptor = new LazyAnnotationDescriptor(
new LazyAnnotationsContext(this, storageManager, trace) {
@NotNull
@Override
public JetScope getScope() {
return scope;
}
},
entryElement
);
}
if (shouldResolveArguments) {
resolveAnnotationArguments(entryElement, trace);
}
result.add(descriptor);
}
return new AnnotationsImpl(result);
}
@NotNull
public JetType resolveAnnotationType(@NotNull JetScope scope, @NotNull JetAnnotationEntry entryElement) {
JetTypeReference typeReference = entryElement.getTypeReference();
if (typeReference == null) {
return ErrorUtils.createErrorType("No type reference: " + entryElement.getText());
}
return typeResolver.resolveType(scope, typeReference, new BindingTraceContext(), true);
}
public static void checkAnnotationType(
@NotNull JetAnnotationEntry entryElement,
@NotNull BindingTrace trace,
@NotNull OverloadResolutionResults results
) {
if (!results.isSingleResult()) return;
FunctionDescriptor descriptor = results.getResultingDescriptor();
if (!ErrorUtils.isError(descriptor)) {
if (descriptor instanceof ConstructorDescriptor) {
ConstructorDescriptor constructor = (ConstructorDescriptor)descriptor;
ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
if (classDescriptor.getKind() != ClassKind.ANNOTATION_CLASS) {
trace.report(NOT_AN_ANNOTATION_CLASS.on(entryElement, classDescriptor));
}
}
else {
trace.report(NOT_AN_ANNOTATION_CLASS.on(entryElement, descriptor));
}
}
}
@NotNull
public OverloadResolutionResults resolveAnnotationCall(
JetAnnotationEntry annotationEntry,
JetScope scope,
BindingTrace trace
) {
return callResolver.resolveFunctionCall(
trace, scope,
CallMaker.makeCall(ReceiverValue.NO_RECEIVER, null, annotationEntry),
NO_EXPECTED_TYPE,
DataFlowInfo.EMPTY,
true
);
}
public static void resolveAnnotationsArguments(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
if (modifierList == null) {
return;
}
for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
resolveAnnotationArguments(annotationEntry, trace);
}
}
public static void resolveAnnotationsArguments(@NotNull Annotated descriptor, @NotNull BindingTrace trace) {
for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
JetAnnotationEntry annotationEntry = trace.getBindingContext().get(ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotationDescriptor);
assert annotationEntry != null : "Cannot find annotation entry: " + annotationDescriptor;
resolveAnnotationArguments(annotationEntry, trace);
}
}
private static void resolveAnnotationArguments(
@NotNull JetAnnotationEntry annotationEntry,
@NotNull BindingTrace trace
) {
AnnotationDescriptor annotationDescriptor = trace.getBindingContext().get(BindingContext.ANNOTATION, annotationEntry);
assert annotationDescriptor != null : "Annotation descriptor should be created before resolving arguments for " + annotationEntry.getText();
if (annotationDescriptor instanceof LazyAnnotationDescriptor) {
((LazyAnnotationDescriptor) annotationDescriptor).forceResolveAllContents();
}
}
@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();
CompileTimeConstant> value = getAnnotationArgumentValue(trace, parameterDescriptor, resolvedArgument);
if (value != null) {
arguments.put(parameterDescriptor, value);
}
}
return arguments;
}
@Nullable
public static CompileTimeConstant> getAnnotationArgumentValue(
BindingTrace trace,
ValueParameterDescriptor parameterDescriptor,
ResolvedValueArgument resolvedArgument
) {
JetType varargElementType = parameterDescriptor.getVarargElementType();
boolean argumentsAsVararg = varargElementType != null && !hasSpread(resolvedArgument);
List> constants = resolveValueArguments(resolvedArgument,
argumentsAsVararg ? varargElementType : parameterDescriptor.getType(),
trace);
if (argumentsAsVararg) {
boolean usesVariableAsConstant = KotlinPackage.any(constants, new Function1, Boolean>() {
@Override
public Boolean invoke(CompileTimeConstant> constant) {
return constant.usesVariableAsConstant();
}
});
JetType arrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(varargElementType);
if (arrayType == null) {
arrayType = KotlinBuiltIns.getInstance().getArrayType(varargElementType);
}
return new ArrayValue(constants, arrayType, true, usesVariableAsConstant);
}
else {
// we should actually get only one element, but just in case of getting many, we take the last one
return !constants.isEmpty() ? KotlinPackage.last(constants) : null;
}
}
private static void checkCompileTimeConstant(
@NotNull JetExpression argumentExpression,
@NotNull JetType expectedType,
@NotNull BindingTrace trace
) {
JetType expressionType = trace.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
if (expressionType == null || !expressionType.equals(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 = trace.get(BindingContext.COMPILE_TIME_VALUE, argumentExpression);
if (constant != null && constant.canBeUsedInAnnotations()) {
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 && CompileTimeConstantUtils.isJavaLangClass((ClassDescriptor) descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CLASS_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 = trace.get(BindingContext.RESOLVED_CALL, (expression).getCalleeExpression());
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.object$.evaluate(argumentExpression, trace, expectedType);
if (constant instanceof IntegerValueTypeConstant) {
JetType defaultType = ((IntegerValueTypeConstant) constant).getType(expectedType);
ArgumentTypeResolver.updateNumberType(defaultType, argumentExpression, trace);
}
if (constant != null) {
constants.add(constant);
}
checkCompileTimeConstant(argumentExpression, expectedType, trace);
}
}
return constants;
}
@SuppressWarnings("MethodMayBeStatic")
@NotNull
public Annotations getResolvedAnnotations(@NotNull List annotations, @NotNull BindingTrace trace) {
List result = new ArrayList(annotations.size());
for (JetAnnotationEntry annotation : annotations) {
AnnotationDescriptor annotationDescriptor = trace.get(BindingContext.ANNOTATION, annotation);
if (annotationDescriptor == null) {
throw new IllegalStateException("Annotation for annotation should have been resolved: \n" +
JetPsiUtil.getElementTextWithContext(annotation));
}
result.add(annotationDescriptor);
}
return new AnnotationsImpl(result);
}
public static void reportUnsupportedAnnotationForTypeParameter(@NotNull JetTypeParameter jetTypeParameter, @NotNull BindingTrace trace) {
JetModifierList modifierList = jetTypeParameter.getModifierList();
if (modifierList == null) return;
for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
trace.report(Errors.UNSUPPORTED.on(annotationEntry, "Annotations for type parameters are not supported yet"));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy