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