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
017package org.jetbrains.jet.lang.resolve;
018
019import com.google.common.collect.Lists;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.*;
023import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
024import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
025import org.jetbrains.jet.lang.diagnostics.Errors;
026import org.jetbrains.jet.lang.psi.*;
027import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
029import org.jetbrains.jet.lang.resolve.calls.CallResolver;
030import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
031import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
032import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
033import org.jetbrains.jet.lang.resolve.constants.*;
034import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
036import org.jetbrains.jet.lang.types.ErrorUtils;
037import org.jetbrains.jet.lang.types.JetType;
038import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
039import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
040
041import javax.inject.Inject;
042import java.util.Collections;
043import java.util.List;
044import java.util.Map;
045
046import static org.jetbrains.jet.lang.resolve.BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT;
047import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
048
049public class AnnotationResolver {
050
051    private ExpressionTypingServices expressionTypingServices;
052    private CallResolver callResolver;
053
054    @Inject
055    public void setExpressionTypingServices(ExpressionTypingServices expressionTypingServices) {
056        this.expressionTypingServices = expressionTypingServices;
057    }
058
059    @Inject
060    public void setCallResolver(CallResolver callResolver) {
061        this.callResolver = callResolver;
062    }
063
064    @NotNull
065    public List<AnnotationDescriptor> resolveAnnotations(
066            @NotNull JetScope scope,
067            @Nullable JetModifierList modifierList,
068            @NotNull BindingTrace trace)
069    {
070        return resolveAnnotations(scope, modifierList, trace, false);
071    }
072
073    @NotNull
074    public List<AnnotationDescriptor> resolveAnnotationsWithArguments(
075            @NotNull JetScope scope,
076            @Nullable JetModifierList modifierList,
077            @NotNull BindingTrace trace
078    ) {
079        return resolveAnnotations(scope, modifierList, trace, true);
080    }
081
082    private List<AnnotationDescriptor> resolveAnnotations(
083            @NotNull JetScope scope,
084            @Nullable JetModifierList modifierList,
085            @NotNull BindingTrace trace,
086            boolean shouldResolveArguments
087    ) {
088        if (modifierList == null) {
089            return Collections.emptyList();
090        }
091        List<JetAnnotationEntry> annotationEntryElements = modifierList.getAnnotationEntries();
092
093        if (annotationEntryElements.isEmpty()) return Collections.emptyList();
094        List<AnnotationDescriptor> result = Lists.newArrayList();
095        for (JetAnnotationEntry entryElement : annotationEntryElements) {
096            AnnotationDescriptor descriptor = new AnnotationDescriptor();
097            resolveAnnotationStub(scope, entryElement, descriptor, trace);
098            trace.record(BindingContext.ANNOTATION, entryElement, descriptor);
099            if (shouldResolveArguments) {
100                resolveAnnotationArguments(entryElement, scope, trace);
101            }
102            result.add(descriptor);
103        }
104        return result;
105    }
106
107    public void resolveAnnotationStub(
108            @NotNull JetScope scope,
109            @NotNull JetAnnotationEntry entryElement,
110            @NotNull AnnotationDescriptor annotationDescriptor,
111            @NotNull BindingTrace trace
112    ) {
113        TemporaryBindingTrace temporaryBindingTrace = new TemporaryBindingTrace(trace, "Trace for resolve annotation type");
114        OverloadResolutionResults<FunctionDescriptor> results = resolveAnnotationCall(entryElement, scope, temporaryBindingTrace);
115        if (results.isSuccess()) {
116            FunctionDescriptor descriptor = results.getResultingDescriptor();
117            if (!ErrorUtils.isError(descriptor)) {
118                if (descriptor instanceof ConstructorDescriptor) {
119                    ConstructorDescriptor constructor = (ConstructorDescriptor)descriptor;
120                    ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
121                    if (classDescriptor.getKind() != ClassKind.ANNOTATION_CLASS) {
122                        trace.report(Errors.NOT_AN_ANNOTATION_CLASS.on(entryElement, classDescriptor.getName().asString()));
123                    }
124                }
125                else {
126                    trace.report(Errors.NOT_AN_ANNOTATION_CLASS.on(entryElement, descriptor.getName().asString()));
127                }
128            }
129            JetType annotationType = results.getResultingDescriptor().getReturnType();
130            annotationDescriptor.setAnnotationType(annotationType);
131        }
132        else {
133            annotationDescriptor.setAnnotationType(ErrorUtils.createErrorType("Unresolved annotation type"));
134        }
135    }
136
137    private OverloadResolutionResults<FunctionDescriptor> resolveAnnotationCall(
138            JetAnnotationEntry annotationEntry,
139            JetScope scope,
140            BindingTrace trace
141    ) {
142        return callResolver.resolveFunctionCall(
143                trace, scope,
144                CallMaker.makeCall(ReceiverValue.NO_RECEIVER, null, annotationEntry),
145                NO_EXPECTED_TYPE,
146                DataFlowInfo.EMPTY);
147    }
148
149    public void resolveAnnotationsArguments(@NotNull JetScope scope, @Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
150        if (modifierList == null) {
151            return;
152        }
153
154        for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
155            resolveAnnotationArguments(annotationEntry, scope, trace);
156        }
157    }
158
159    public void resolveAnnotationsArguments(@NotNull Annotated descriptor, @NotNull BindingTrace trace, @NotNull JetScope scope) {
160        for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
161            JetAnnotationEntry annotationEntry = trace.getBindingContext().get(ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotationDescriptor);
162            assert annotationEntry != null : "Cannot find annotation entry: " + annotationDescriptor;
163            resolveAnnotationArguments(annotationEntry, scope, trace);
164        }
165    }
166
167    private void resolveAnnotationArguments(
168            @NotNull JetAnnotationEntry annotationEntry,
169            @NotNull JetScope scope,
170            @NotNull BindingTrace trace
171    ) {
172        OverloadResolutionResults<FunctionDescriptor> results = resolveAnnotationCall(annotationEntry, scope, trace);
173        if (results.isSuccess()) {
174            AnnotationDescriptor annotationDescriptor = trace.getBindingContext().get(BindingContext.ANNOTATION, annotationEntry);
175            assert annotationDescriptor != null : "Annotation descriptor should be created before resolving arguments for " + annotationEntry.getText();
176            resolveAnnotationArgument(annotationDescriptor, results.getResultingCall(), trace);
177        }
178    }
179
180    private void resolveAnnotationArgument(
181            @NotNull AnnotationDescriptor annotationDescriptor,
182            @NotNull ResolvedCall<? extends CallableDescriptor> call,
183            @NotNull BindingTrace trace
184    ) {
185        for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> descriptorToArgument :
186                call.getValueArguments().entrySet()) {
187            ValueParameterDescriptor parameterDescriptor = descriptorToArgument.getKey();
188
189            JetType varargElementType = parameterDescriptor.getVarargElementType();
190            List<CompileTimeConstant<?>> constants = resolveValueArguments(descriptorToArgument.getValue(), parameterDescriptor.getType(), trace);
191            if (varargElementType == null) {
192                for (CompileTimeConstant<?> constant : constants) {
193                    annotationDescriptor.setValueArgument(parameterDescriptor, constant);
194                }
195            }
196            else {
197                JetType arrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(varargElementType);
198                if (arrayType == null) {
199                    arrayType = KotlinBuiltIns.getInstance().getArrayType(varargElementType);
200                }
201                annotationDescriptor.setValueArgument(parameterDescriptor, new ArrayValue(constants, arrayType));
202            }
203        }
204    }
205
206    @NotNull
207    private List<CompileTimeConstant<?>> resolveValueArguments(
208            @NotNull ResolvedValueArgument resolvedValueArgument,
209            @NotNull JetType expectedType,
210            @NotNull BindingTrace trace
211    ) {
212        List<CompileTimeConstant<?>> constants = Lists.newArrayList();
213        for (ValueArgument argument : resolvedValueArgument.getArguments()) {
214            JetExpression argumentExpression = argument.getArgumentExpression();
215            if (argumentExpression != null) {
216                CompileTimeConstant<?> constant = resolveAnnotationArgument(argumentExpression, expectedType, trace);
217                if (constant != null) {
218                    constants.add(constant);
219                }
220            }
221        }
222        return constants;
223    }
224
225    @Nullable
226    private CompileTimeConstant<?> resolveAnnotationArgument(@NotNull JetExpression expression, @NotNull final JetType expectedType, final BindingTrace trace) {
227        JetVisitor<CompileTimeConstant<?>, Void> visitor = new JetVisitor<CompileTimeConstant<?>, Void>() {
228            @Override
229            public CompileTimeConstant<?> visitConstantExpression(JetConstantExpression expression, Void nothing) {
230                JetType type = expressionTypingServices.getType(JetScope.EMPTY, expression, expectedType, DataFlowInfo.EMPTY, trace);
231                if (type == null) {
232                    // TODO:
233                    //  trace.report(ANNOTATION_PARAMETER_SHOULD_BE_CONSTANT.on(expression));
234                }
235                return trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
236            }
237
238
239            // @Override
240//            public CompileTimeConstant visitAnnotation(JetAnnotation annotation, Void nothing) {
241//                super.visitAnnotation(annotation, null); // TODO
242//            }
243//
244//            @Override
245//            public CompileTimeConstant visitAnnotationEntry(JetAnnotationEntry annotationEntry, Void nothing) {
246//                return super.visitAnnotationEntry(annotationEntry, null); // TODO
247//            }
248
249            @Override
250            public CompileTimeConstant<?> visitParenthesizedExpression(JetParenthesizedExpression expression, Void nothing) {
251                JetExpression innerExpression = expression.getExpression();
252                if (innerExpression == null) return null;
253                return innerExpression.accept(this, null);
254            }
255
256            @Override
257            public CompileTimeConstant<?> visitStringTemplateExpression(JetStringTemplateExpression expression,
258                                                                        Void nothing) {
259                return trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
260            }
261
262            @Override
263            public CompileTimeConstant<?> visitSimpleNameExpression(JetSimpleNameExpression expression, Void data) {
264                ResolvedCall<? extends CallableDescriptor> resolvedCall =
265                        trace.getBindingContext().get(BindingContext.RESOLVED_CALL, expression);
266                if (resolvedCall != null) {
267                    CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor();
268                    if (callableDescriptor instanceof PropertyDescriptor) {
269                        PropertyDescriptor propertyDescriptor = (PropertyDescriptor) callableDescriptor;
270                        if (isEnumProperty(propertyDescriptor)) {
271                            return new EnumValue(propertyDescriptor);
272                        }
273                    }
274                }
275                return null;
276            }
277
278            @Override
279            public CompileTimeConstant<?> visitQualifiedExpression(JetQualifiedExpression expression, Void data) {
280                JetExpression selectorExpression = expression.getSelectorExpression();
281                if (selectorExpression != null) {
282                    return selectorExpression.accept(this, null);
283                }
284                return super.visitQualifiedExpression(expression, data);
285            }
286
287            @Override
288            public CompileTimeConstant<?> visitCallExpression(JetCallExpression expression, Void data) {
289                ResolvedCall<? extends CallableDescriptor> call =
290                        trace.getBindingContext().get(BindingContext.RESOLVED_CALL, (expression).getCalleeExpression());
291                if (call != null) {
292                    CallableDescriptor resultingDescriptor = call.getResultingDescriptor();
293                    if (AnnotationUtils.isArrayMethodCall(call)) {
294                        JetType type = resultingDescriptor.getValueParameters().iterator().next().getVarargElementType();
295                        List<CompileTimeConstant<?>> arguments = Lists.newArrayList();
296                        for (ResolvedValueArgument descriptorToArgument : call.getValueArguments().values()) {
297                            arguments.addAll(resolveValueArguments(descriptorToArgument, type, trace));
298                        }
299                        return new ArrayValue(arguments, resultingDescriptor.getReturnType());
300                    }
301
302                    if (resultingDescriptor instanceof ConstructorDescriptor) {
303                        JetType constructorReturnType = resultingDescriptor.getReturnType();
304                        assert constructorReturnType != null : "Constructor should have return type";
305                        if (DescriptorUtils.isAnnotationClass(constructorReturnType.getConstructor().getDeclarationDescriptor())) {
306                            AnnotationDescriptor descriptor = new AnnotationDescriptor();
307                            descriptor.setAnnotationType(constructorReturnType);
308                            resolveAnnotationArgument(descriptor, call, trace);
309                            return new AnnotationValue(descriptor);
310                        }
311                    }
312
313                    if (AnnotationUtils.isJavaClassMethodCall(call)) {
314                        return new JavaClassValue(resultingDescriptor.getReturnType());
315                    }
316                }
317                return null;
318            }
319
320            @Override
321            public CompileTimeConstant<?> visitJetElement(JetElement element, Void nothing) {
322                // TODO:
323                //trace.report(ANNOTATION_PARAMETER_SHOULD_BE_CONSTANT.on(element));
324                return null;
325            }
326        };
327        return expression.accept(visitor, null);
328    }
329
330    private static boolean isEnumProperty(@NotNull PropertyDescriptor descriptor) {
331        if (DescriptorUtils.isKindOf(descriptor.getType(), ClassKind.ENUM_CLASS)) {
332            DeclarationDescriptor enumClassObject = descriptor.getContainingDeclaration();
333            if (DescriptorUtils.isKindOf(enumClassObject, ClassKind.CLASS_OBJECT))  {
334                return DescriptorUtils.isKindOf(enumClassObject.getContainingDeclaration(), ClassKind.ENUM_CLASS);
335            }
336        }
337        return false;
338    }
339
340    @NotNull
341    public List<AnnotationDescriptor> getResolvedAnnotations(@Nullable JetModifierList modifierList, BindingTrace trace) {
342        if (modifierList == null) {
343            return Collections.emptyList();
344        }
345        return getResolvedAnnotations(modifierList.getAnnotationEntries(), trace);
346    }
347
348    @SuppressWarnings("MethodMayBeStatic")
349    @NotNull
350    public List<AnnotationDescriptor> getResolvedAnnotations(List<JetAnnotationEntry> annotations, BindingTrace trace) {
351        List<AnnotationDescriptor> result = Lists.newArrayList();
352        for (JetAnnotationEntry annotation : annotations) {
353            AnnotationDescriptor annotationDescriptor = trace.get(BindingContext.ANNOTATION, annotation);
354            if (annotationDescriptor == null) {
355                // TODO: Unresolved annotation
356                annotationDescriptor = new AnnotationDescriptor();
357                annotationDescriptor.setAnnotationType(ErrorUtils.createErrorType("Unresolved annotation type"));
358
359                trace.record(BindingContext.ANNOTATION, annotation, annotationDescriptor);
360            }
361
362            result.add(annotationDescriptor);
363        }
364        return result;
365    }
366}