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.Annotated;
027    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
028    import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
029    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationsImpl;
030    import org.jetbrains.jet.lang.diagnostics.Errors;
031    import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
032    import org.jetbrains.jet.lang.psi.*;
033    import org.jetbrains.jet.lang.resolve.calls.ArgumentTypeResolver;
034    import org.jetbrains.jet.lang.resolve.calls.CallResolver;
035    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
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.LazyAnnotationsContext;
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 final 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(
136                            new LazyAnnotationsContext(this, storageManager, trace) {
137    
138                                @NotNull
139                                @Override
140                                public JetScope getScope() {
141                                    return scope;
142                                }
143                            },
144                            entryElement
145                    );
146                }
147                if (shouldResolveArguments) {
148                    resolveAnnotationArguments(entryElement, trace);
149                }
150    
151                result.add(descriptor);
152            }
153            return new AnnotationsImpl(result);
154        }
155    
156        @NotNull
157        public JetType resolveAnnotationType(@NotNull JetScope scope, @NotNull JetAnnotationEntry entryElement) {
158            JetTypeReference typeReference = entryElement.getTypeReference();
159            if (typeReference == null) {
160                return ErrorUtils.createErrorType("No type reference: " + entryElement.getText());
161            }
162    
163            return typeResolver.resolveType(scope, typeReference, new BindingTraceContext(), true);
164        }
165    
166        public static void checkAnnotationType(
167                @NotNull JetAnnotationEntry entryElement,
168                @NotNull BindingTrace trace,
169                @NotNull OverloadResolutionResults<FunctionDescriptor> results
170        ) {
171            if (!results.isSingleResult()) return;
172            FunctionDescriptor descriptor = results.getResultingDescriptor();
173            if (!ErrorUtils.isError(descriptor)) {
174                if (descriptor instanceof ConstructorDescriptor) {
175                    ConstructorDescriptor constructor = (ConstructorDescriptor)descriptor;
176                    ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
177                    if (classDescriptor.getKind() != ClassKind.ANNOTATION_CLASS) {
178                        trace.report(NOT_AN_ANNOTATION_CLASS.on(entryElement, classDescriptor));
179                    }
180                }
181                else {
182                    trace.report(NOT_AN_ANNOTATION_CLASS.on(entryElement, descriptor));
183                }
184            }
185        }
186    
187        @NotNull
188        public OverloadResolutionResults<FunctionDescriptor> resolveAnnotationCall(
189                JetAnnotationEntry annotationEntry,
190                JetScope scope,
191                BindingTrace trace
192        ) {
193            return callResolver.resolveFunctionCall(
194                    trace, scope,
195                    CallMaker.makeCall(ReceiverValue.NO_RECEIVER, null, annotationEntry),
196                    NO_EXPECTED_TYPE,
197                    DataFlowInfo.EMPTY,
198                    true
199            );
200        }
201    
202        public static void resolveAnnotationsArguments(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
203            if (modifierList == null) {
204                return;
205            }
206    
207            for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
208                resolveAnnotationArguments(annotationEntry, trace);
209            }
210        }
211    
212        public static void resolveAnnotationsArguments(@NotNull Annotated descriptor, @NotNull BindingTrace trace) {
213            for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
214                JetAnnotationEntry annotationEntry = trace.getBindingContext().get(ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotationDescriptor);
215                assert annotationEntry != null : "Cannot find annotation entry: " + annotationDescriptor;
216                resolveAnnotationArguments(annotationEntry, trace);
217            }
218        }
219    
220        private static void resolveAnnotationArguments(
221                @NotNull JetAnnotationEntry annotationEntry,
222                @NotNull BindingTrace trace
223        ) {
224            AnnotationDescriptor annotationDescriptor = trace.getBindingContext().get(BindingContext.ANNOTATION, annotationEntry);
225            assert annotationDescriptor != null : "Annotation descriptor should be created before resolving arguments for " + annotationEntry.getText();
226            if (annotationDescriptor instanceof LazyAnnotationDescriptor) {
227                ((LazyAnnotationDescriptor) annotationDescriptor).forceResolveAllContents();
228            }
229        }
230    
231        @NotNull
232        public static Map<ValueParameterDescriptor, CompileTimeConstant<?>> resolveAnnotationArguments(
233                @NotNull ResolvedCall<?> resolvedCall,
234                @NotNull BindingTrace trace
235        ) {
236            Map<ValueParameterDescriptor, CompileTimeConstant<?>> arguments = new HashMap<ValueParameterDescriptor, CompileTimeConstant<?>>();
237            for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> descriptorToArgument : resolvedCall.getValueArguments().entrySet()) {
238                ValueParameterDescriptor parameterDescriptor = descriptorToArgument.getKey();
239                ResolvedValueArgument resolvedArgument = descriptorToArgument.getValue();
240    
241                CompileTimeConstant<?> value = getAnnotationArgumentValue(trace, parameterDescriptor, resolvedArgument);
242                if (value != null) {
243                    arguments.put(parameterDescriptor, value);
244                }
245            }
246            return arguments;
247        }
248    
249        @Nullable
250        public static CompileTimeConstant<?> getAnnotationArgumentValue(
251                BindingTrace trace,
252                ValueParameterDescriptor parameterDescriptor,
253                ResolvedValueArgument resolvedArgument
254        ) {
255            JetType varargElementType = parameterDescriptor.getVarargElementType();
256            boolean argumentsAsVararg = varargElementType != null && !hasSpread(resolvedArgument);
257            List<CompileTimeConstant<?>> constants = resolveValueArguments(resolvedArgument,
258                                                                           argumentsAsVararg ? varargElementType : parameterDescriptor.getType(),
259                                                                           trace);
260    
261            if (argumentsAsVararg) {
262    
263                boolean usesVariableAsConstant = KotlinPackage.any(constants, new Function1<CompileTimeConstant<?>, Boolean>() {
264                    @Override
265                    public Boolean invoke(CompileTimeConstant<?> constant) {
266                        return constant.usesVariableAsConstant();
267                    }
268                });
269    
270                JetType arrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(varargElementType);
271                if (arrayType == null) {
272                    arrayType = KotlinBuiltIns.getInstance().getArrayType(varargElementType);
273                }
274    
275                return new ArrayValue(constants, arrayType, true, usesVariableAsConstant);
276            }
277            else {
278                // we should actually get only one element, but just in case of getting many, we take the last one
279                return !constants.isEmpty() ? KotlinPackage.last(constants) : null;
280            }
281        }
282    
283        private static void checkCompileTimeConstant(
284                @NotNull JetExpression argumentExpression,
285                @NotNull JetType expectedType,
286                @NotNull BindingTrace trace
287        ) {
288            JetType expressionType = trace.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
289    
290            if (expressionType == null || !expressionType.equals(expectedType)) {
291                // TYPE_MISMATCH should be reported otherwise
292                return;
293            }
294    
295            // array(1, <!>null<!>, 3) - error should be reported on inner expression
296            if (argumentExpression instanceof JetCallExpression) {
297                Pair<List<JetExpression>, JetType> arrayArgument = getArgumentExpressionsForArrayCall((JetCallExpression) argumentExpression, trace);
298                if (arrayArgument != null) {
299                    for (JetExpression expression : arrayArgument.getFirst()) {
300                        checkCompileTimeConstant(expression, arrayArgument.getSecond(), trace);
301                    }
302                }
303            }
304    
305            CompileTimeConstant<?> constant = trace.get(BindingContext.COMPILE_TIME_VALUE, argumentExpression);
306            if (constant != null && constant.canBeUsedInAnnotations()) {
307                return;
308            }
309    
310            ClassifierDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor();
311            if (descriptor != null && DescriptorUtils.isEnumClass(descriptor)) {
312                trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST.on(argumentExpression));
313            }
314            else if (descriptor instanceof ClassDescriptor && CompileTimeConstantUtils.isJavaLangClass((ClassDescriptor) descriptor)) {
315                trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CLASS_LITERAL.on(argumentExpression));
316            }
317            else {
318                trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CONST.on(argumentExpression));
319            }
320        }
321    
322        @Nullable
323        private static Pair<List<JetExpression>, JetType> getArgumentExpressionsForArrayCall(
324                @NotNull JetCallExpression expression,
325                @NotNull BindingTrace trace
326        ) {
327            ResolvedCall<?> resolvedCall = trace.get(BindingContext.RESOLVED_CALL, (expression).getCalleeExpression());
328            if (resolvedCall == null || !CompileTimeConstantUtils.isArrayMethodCall(resolvedCall)) {
329                return null;
330            }
331    
332            assert resolvedCall.getValueArguments().size() == 1 : "Array function should have only one vararg parameter";
333            Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> argumentEntry = resolvedCall.getValueArguments().entrySet().iterator().next();
334    
335            List<JetExpression> result = Lists.newArrayList();
336            JetType elementType = argumentEntry.getKey().getVarargElementType();
337            for (ValueArgument valueArgument : argumentEntry.getValue().getArguments()) {
338                JetExpression valueArgumentExpression = valueArgument.getArgumentExpression();
339                if (valueArgumentExpression != null) {
340                    if (elementType != null) {
341                        result.add(valueArgumentExpression);
342                    }
343                }
344            }
345            return new Pair<List<JetExpression>, JetType>(result, elementType);
346        }
347    
348        private static boolean hasSpread(@NotNull ResolvedValueArgument argument) {
349            List<ValueArgument> arguments = argument.getArguments();
350            return arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
351        }
352    
353        @NotNull
354        private static List<CompileTimeConstant<?>> resolveValueArguments(
355                @NotNull ResolvedValueArgument resolvedValueArgument,
356                @NotNull JetType expectedType,
357                @NotNull BindingTrace trace
358        ) {
359            List<CompileTimeConstant<?>> constants = Lists.newArrayList();
360            for (ValueArgument argument : resolvedValueArgument.getArguments()) {
361                JetExpression argumentExpression = argument.getArgumentExpression();
362                if (argumentExpression != null) {
363                    CompileTimeConstant<?> constant = ConstantExpressionEvaluator.object$.evaluate(argumentExpression, trace, expectedType);
364                    if (constant instanceof IntegerValueTypeConstant) {
365                        JetType defaultType = ((IntegerValueTypeConstant) constant).getType(expectedType);
366                        ArgumentTypeResolver.updateNumberType(defaultType, argumentExpression, trace);
367                    }
368                    if (constant != null) {
369                        constants.add(constant);
370                    }
371                    checkCompileTimeConstant(argumentExpression, expectedType, trace);
372                }
373            }
374            return constants;
375        }
376    
377        @SuppressWarnings("MethodMayBeStatic")
378        @NotNull
379        public Annotations getResolvedAnnotations(@NotNull List<JetAnnotationEntry> annotations, @NotNull BindingTrace trace) {
380            List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>(annotations.size());
381            for (JetAnnotationEntry annotation : annotations) {
382                AnnotationDescriptor annotationDescriptor = trace.get(BindingContext.ANNOTATION, annotation);
383                if (annotationDescriptor == null) {
384                    throw new IllegalStateException("Annotation for annotation should have been resolved: \n" +
385                                                    JetPsiUtil.getElementTextWithContext(annotation));
386                }
387    
388                result.add(annotationDescriptor);
389            }
390    
391            return new AnnotationsImpl(result);
392        }
393    
394        public static void reportUnsupportedAnnotationForTypeParameter(@NotNull JetTypeParameter jetTypeParameter, @NotNull BindingTrace trace) {
395            JetModifierList modifierList = jetTypeParameter.getModifierList();
396            if (modifierList == null) return;
397    
398            for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
399                trace.report(Errors.UNSUPPORTED.on(annotationEntry, "Annotations for type parameters are not supported yet"));
400            }
401        }
402    }