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.types.TypeUtils.NO_EXPECTED_TYPE;
048    
049    public 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    }