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