001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.resolve;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.openapi.util.Pair;
021    import kotlin.Function1;
022    import kotlin.KotlinPackage;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.jet.lang.descriptors.*;
026    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
028    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationsImpl;
029    import org.jetbrains.jet.lang.diagnostics.Errors;
030    import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
031    import org.jetbrains.jet.lang.psi.*;
032    import org.jetbrains.jet.lang.resolve.calls.ArgumentTypeResolver;
033    import org.jetbrains.jet.lang.resolve.calls.CallResolver;
034    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
035    import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
036    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
037    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
038    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
039    import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
040    import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
041    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
042    import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
043    import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyAnnotationDescriptor;
044    import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyAnnotationsContextImpl;
045    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
046    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
047    import org.jetbrains.jet.lang.types.ErrorUtils;
048    import org.jetbrains.jet.lang.types.JetType;
049    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
050    import org.jetbrains.jet.storage.StorageManager;
051    
052    import javax.inject.Inject;
053    import java.util.ArrayList;
054    import java.util.HashMap;
055    import java.util.List;
056    import java.util.Map;
057    
058    import static org.jetbrains.jet.lang.diagnostics.Errors.NOT_AN_ANNOTATION_CLASS;
059    import static org.jetbrains.jet.lang.resolve.BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT;
060    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
061    
062    public class AnnotationResolver {
063    
064        private CallResolver callResolver;
065        private StorageManager storageManager;
066        private TypeResolver typeResolver;
067    
068        @Inject
069        public void setCallResolver(CallResolver callResolver) {
070            this.callResolver = callResolver;
071        }
072    
073        @Inject
074        public void setStorageManager(StorageManager storageManager) {
075            this.storageManager = storageManager;
076        }
077    
078        @Inject
079        public void setTypeResolver(TypeResolver typeResolver) {
080            this.typeResolver = typeResolver;
081        }
082    
083        @NotNull
084        public Annotations resolveAnnotationsWithoutArguments(
085                @NotNull JetScope scope,
086                @Nullable JetModifierList modifierList,
087                @NotNull BindingTrace trace
088        ) {
089            return resolveAnnotations(scope, modifierList, trace, false);
090        }
091    
092        @NotNull
093        public Annotations resolveAnnotationsWithArguments(
094                @NotNull JetScope scope,
095                @Nullable JetModifierList modifierList,
096                @NotNull BindingTrace trace
097        ) {
098            return resolveAnnotations(scope, modifierList, trace, true);
099        }
100    
101        @NotNull
102        public Annotations resolveAnnotationsWithArguments(
103                @NotNull JetScope scope,
104                @NotNull List<JetAnnotationEntry> annotationEntries,
105                @NotNull BindingTrace trace
106        ) {
107            return resolveAnnotationEntries(scope, annotationEntries, trace, true);
108        }
109    
110        private Annotations resolveAnnotations(
111                @NotNull JetScope scope,
112                @Nullable JetModifierList modifierList,
113                @NotNull BindingTrace trace,
114                boolean shouldResolveArguments
115        ) {
116            if (modifierList == null) {
117                return Annotations.EMPTY;
118            }
119            List<JetAnnotationEntry> annotationEntryElements = modifierList.getAnnotationEntries();
120    
121            return resolveAnnotationEntries(scope, annotationEntryElements, trace, shouldResolveArguments);
122        }
123    
124        private Annotations resolveAnnotationEntries(
125                @NotNull JetScope scope,
126                @NotNull List<JetAnnotationEntry> annotationEntryElements,
127                @NotNull BindingTrace trace,
128                boolean shouldResolveArguments
129        ) {
130            if (annotationEntryElements.isEmpty()) return Annotations.EMPTY;
131            List<AnnotationDescriptor> result = Lists.newArrayList();
132            for (JetAnnotationEntry entryElement : annotationEntryElements) {
133                AnnotationDescriptor descriptor = trace.get(BindingContext.ANNOTATION, entryElement);
134                if (descriptor == null) {
135                    descriptor = new LazyAnnotationDescriptor(new LazyAnnotationsContextImpl(this, storageManager, trace, scope), entryElement);
136                }
137                if (shouldResolveArguments) {
138                    resolveAnnotationArguments(entryElement, trace);
139                }
140    
141                result.add(descriptor);
142            }
143            return new AnnotationsImpl(result);
144        }
145    
146        @NotNull
147        public JetType resolveAnnotationType(@NotNull JetScope scope, @NotNull JetAnnotationEntry entryElement) {
148            JetTypeReference typeReference = entryElement.getTypeReference();
149            if (typeReference == null) {
150                return ErrorUtils.createErrorType("No type reference: " + entryElement.getText());
151            }
152    
153            return typeResolver.resolveType(scope, typeReference, new BindingTraceContext(), true);
154        }
155    
156        public static void checkAnnotationType(
157                @NotNull JetAnnotationEntry entryElement,
158                @NotNull BindingTrace trace,
159                @NotNull OverloadResolutionResults<FunctionDescriptor> results
160        ) {
161            if (!results.isSingleResult()) return;
162            FunctionDescriptor descriptor = results.getResultingDescriptor();
163            if (!ErrorUtils.isError(descriptor)) {
164                if (descriptor instanceof ConstructorDescriptor) {
165                    ConstructorDescriptor constructor = (ConstructorDescriptor)descriptor;
166                    ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
167                    if (classDescriptor.getKind() != ClassKind.ANNOTATION_CLASS) {
168                        trace.report(NOT_AN_ANNOTATION_CLASS.on(entryElement, classDescriptor));
169                    }
170                }
171                else {
172                    trace.report(NOT_AN_ANNOTATION_CLASS.on(entryElement, descriptor));
173                }
174            }
175        }
176    
177        @NotNull
178        public OverloadResolutionResults<FunctionDescriptor> resolveAnnotationCall(
179                JetAnnotationEntry annotationEntry,
180                JetScope scope,
181                BindingTrace trace
182        ) {
183            return callResolver.resolveFunctionCall(
184                    trace, scope,
185                    CallMaker.makeCall(ReceiverValue.NO_RECEIVER, null, annotationEntry),
186                    NO_EXPECTED_TYPE,
187                    DataFlowInfo.EMPTY,
188                    true
189            );
190        }
191    
192        public static void resolveAnnotationsArguments(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
193            if (modifierList == null) {
194                return;
195            }
196    
197            for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
198                resolveAnnotationArguments(annotationEntry, trace);
199            }
200        }
201    
202        public static void resolveAnnotationsArguments(@NotNull Annotations annotations, @NotNull BindingTrace trace) {
203            for (AnnotationDescriptor annotationDescriptor : annotations) {
204                JetAnnotationEntry annotationEntry = trace.getBindingContext().get(ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotationDescriptor);
205                assert annotationEntry != null : "Cannot find annotation entry: " + annotationDescriptor;
206                resolveAnnotationArguments(annotationEntry, trace);
207            }
208        }
209    
210        private static void resolveAnnotationArguments(
211                @NotNull JetAnnotationEntry annotationEntry,
212                @NotNull BindingTrace trace
213        ) {
214            AnnotationDescriptor annotationDescriptor = trace.getBindingContext().get(BindingContext.ANNOTATION, annotationEntry);
215            assert annotationDescriptor != null : "Annotation descriptor should be created before resolving arguments for " + annotationEntry.getText();
216            if (annotationDescriptor instanceof LazyAnnotationDescriptor) {
217                ((LazyAnnotationDescriptor) annotationDescriptor).forceResolveAllContents();
218            }
219        }
220    
221        @NotNull
222        public static Map<ValueParameterDescriptor, CompileTimeConstant<?>> resolveAnnotationArguments(
223                @NotNull ResolvedCall<?> resolvedCall,
224                @NotNull BindingTrace trace
225        ) {
226            Map<ValueParameterDescriptor, CompileTimeConstant<?>> arguments = new HashMap<ValueParameterDescriptor, CompileTimeConstant<?>>();
227            for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> descriptorToArgument : resolvedCall.getValueArguments().entrySet()) {
228                ValueParameterDescriptor parameterDescriptor = descriptorToArgument.getKey();
229                ResolvedValueArgument resolvedArgument = descriptorToArgument.getValue();
230    
231                CompileTimeConstant<?> value = getAnnotationArgumentValue(trace, parameterDescriptor, resolvedArgument);
232                if (value != null) {
233                    arguments.put(parameterDescriptor, value);
234                }
235            }
236            return arguments;
237        }
238    
239        @Nullable
240        public static CompileTimeConstant<?> getAnnotationArgumentValue(
241                BindingTrace trace,
242                ValueParameterDescriptor parameterDescriptor,
243                ResolvedValueArgument resolvedArgument
244        ) {
245            JetType varargElementType = parameterDescriptor.getVarargElementType();
246            boolean argumentsAsVararg = varargElementType != null && !hasSpread(resolvedArgument);
247            List<CompileTimeConstant<?>> constants = resolveValueArguments(resolvedArgument,
248                                                                           argumentsAsVararg ? varargElementType : parameterDescriptor.getType(),
249                                                                           trace);
250    
251            if (argumentsAsVararg) {
252    
253                boolean usesVariableAsConstant = KotlinPackage.any(constants, new Function1<CompileTimeConstant<?>, Boolean>() {
254                    @Override
255                    public Boolean invoke(CompileTimeConstant<?> constant) {
256                        return constant.usesVariableAsConstant();
257                    }
258                });
259    
260                JetType arrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(varargElementType);
261                if (arrayType == null) {
262                    arrayType = KotlinBuiltIns.getInstance().getArrayType(varargElementType);
263                }
264    
265                return new ArrayValue(constants, arrayType, true, usesVariableAsConstant);
266            }
267            else {
268                // we should actually get only one element, but just in case of getting many, we take the last one
269                return !constants.isEmpty() ? KotlinPackage.last(constants) : null;
270            }
271        }
272    
273        private static void checkCompileTimeConstant(
274                @NotNull JetExpression argumentExpression,
275                @NotNull JetType expectedType,
276                @NotNull BindingTrace trace
277        ) {
278            JetType expressionType = trace.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
279    
280            if (expressionType == null || !expressionType.equals(expectedType)) {
281                // TYPE_MISMATCH should be reported otherwise
282                return;
283            }
284    
285            // array(1, <!>null<!>, 3) - error should be reported on inner expression
286            if (argumentExpression instanceof JetCallExpression) {
287                Pair<List<JetExpression>, JetType> arrayArgument = getArgumentExpressionsForArrayCall((JetCallExpression) argumentExpression, trace);
288                if (arrayArgument != null) {
289                    for (JetExpression expression : arrayArgument.getFirst()) {
290                        checkCompileTimeConstant(expression, arrayArgument.getSecond(), trace);
291                    }
292                }
293            }
294    
295            CompileTimeConstant<?> constant = trace.get(BindingContext.COMPILE_TIME_VALUE, argumentExpression);
296            if (constant != null && constant.canBeUsedInAnnotations()) {
297                return;
298            }
299    
300            ClassifierDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor();
301            if (descriptor != null && DescriptorUtils.isEnumClass(descriptor)) {
302                trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST.on(argumentExpression));
303            }
304            else if (descriptor instanceof ClassDescriptor && CompileTimeConstantUtils.isJavaLangClass((ClassDescriptor) descriptor)) {
305                trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CLASS_LITERAL.on(argumentExpression));
306            }
307            else {
308                trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CONST.on(argumentExpression));
309            }
310        }
311    
312        @Nullable
313        private static Pair<List<JetExpression>, JetType> getArgumentExpressionsForArrayCall(
314                @NotNull JetCallExpression expression,
315                @NotNull BindingTrace trace
316        ) {
317            ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(expression, trace.getBindingContext());
318            if (resolvedCall == null || !CompileTimeConstantUtils.isArrayMethodCall(resolvedCall)) {
319                return null;
320            }
321    
322            assert resolvedCall.getValueArguments().size() == 1 : "Array function should have only one vararg parameter";
323            Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> argumentEntry = resolvedCall.getValueArguments().entrySet().iterator().next();
324    
325            List<JetExpression> result = Lists.newArrayList();
326            JetType elementType = argumentEntry.getKey().getVarargElementType();
327            for (ValueArgument valueArgument : argumentEntry.getValue().getArguments()) {
328                JetExpression valueArgumentExpression = valueArgument.getArgumentExpression();
329                if (valueArgumentExpression != null) {
330                    if (elementType != null) {
331                        result.add(valueArgumentExpression);
332                    }
333                }
334            }
335            return new Pair<List<JetExpression>, JetType>(result, elementType);
336        }
337    
338        private static boolean hasSpread(@NotNull ResolvedValueArgument argument) {
339            List<ValueArgument> arguments = argument.getArguments();
340            return arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
341        }
342    
343        @NotNull
344        private static List<CompileTimeConstant<?>> resolveValueArguments(
345                @NotNull ResolvedValueArgument resolvedValueArgument,
346                @NotNull JetType expectedType,
347                @NotNull BindingTrace trace
348        ) {
349            List<CompileTimeConstant<?>> constants = Lists.newArrayList();
350            for (ValueArgument argument : resolvedValueArgument.getArguments()) {
351                JetExpression argumentExpression = argument.getArgumentExpression();
352                if (argumentExpression != null) {
353                    CompileTimeConstant<?> constant = ConstantExpressionEvaluator.OBJECT$.evaluate(argumentExpression, trace, expectedType);
354                    if (constant instanceof IntegerValueTypeConstant) {
355                        JetType defaultType = ((IntegerValueTypeConstant) constant).getType(expectedType);
356                        ArgumentTypeResolver.updateNumberType(defaultType, argumentExpression, trace);
357                    }
358                    if (constant != null) {
359                        constants.add(constant);
360                    }
361                    checkCompileTimeConstant(argumentExpression, expectedType, trace);
362                }
363            }
364            return constants;
365        }
366    
367        @SuppressWarnings("MethodMayBeStatic")
368        @NotNull
369        public Annotations getResolvedAnnotations(@NotNull List<JetAnnotationEntry> annotations, @NotNull BindingTrace trace) {
370            List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>(annotations.size());
371            for (JetAnnotationEntry annotation : annotations) {
372                AnnotationDescriptor annotationDescriptor = trace.get(BindingContext.ANNOTATION, annotation);
373                if (annotationDescriptor == null) {
374                    throw new IllegalStateException("Annotation for annotation should have been resolved: \n" +
375                                                    JetPsiUtil.getElementTextWithContext(annotation));
376                }
377    
378                result.add(annotationDescriptor);
379            }
380    
381            return new AnnotationsImpl(result);
382        }
383    
384        public static void reportUnsupportedAnnotationForTypeParameter(@NotNull JetTypeParameter jetTypeParameter, @NotNull BindingTrace trace) {
385            JetModifierList modifierList = jetTypeParameter.getModifierList();
386            if (modifierList == null) return;
387    
388            for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
389                trace.report(Errors.UNSUPPORTED.on(annotationEntry, "Annotations for type parameters are not supported yet"));
390            }
391        }
392    }