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.calls;
018    
019    import com.intellij.lang.ASTNode;
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.psi.*;
024    import org.jetbrains.jet.lang.resolve.BindingContext;
025    import org.jetbrains.jet.lang.resolve.BindingTrace;
026    import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
027    import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
028    import org.jetbrains.jet.lang.resolve.calls.context.CheckValueArgumentsMode;
029    import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
030    import org.jetbrains.jet.lang.resolve.calls.context.TemporaryTraceAndCache;
031    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
032    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
033    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
034    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
035    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsUtil;
036    import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
037    import org.jetbrains.jet.lang.resolve.constants.ConstantUtils;
038    import org.jetbrains.jet.lang.resolve.name.Name;
039    import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
040    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
041    import org.jetbrains.jet.lang.resolve.scopes.JetScopeImpl;
042    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
043    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
044    import org.jetbrains.jet.lang.types.*;
045    import org.jetbrains.jet.lang.types.expressions.DataFlowUtils;
046    import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
047    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
048    import org.jetbrains.jet.lexer.JetTokens;
049    import org.jetbrains.jet.utils.Printer;
050    
051    import javax.inject.Inject;
052    import java.util.ArrayList;
053    import java.util.Collections;
054    import java.util.List;
055    
056    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
057    import static org.jetbrains.jet.lang.psi.JetPsiUtil.isLHSOfDot;
058    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
059    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getStaticNestedClassesScope;
060    import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
061    import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
062    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
063    
064    public class CallExpressionResolver {
065        @NotNull
066        private ExpressionTypingServices expressionTypingServices;
067    
068        @Inject
069        public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
070            this.expressionTypingServices = expressionTypingServices;
071        }
072    
073        @Nullable
074        private JetType lookupNamespaceOrClassObject(@NotNull JetSimpleNameExpression expression, @NotNull ResolutionContext context) {
075            Name referencedName = expression.getReferencedNameAsName();
076            final ClassifierDescriptor classifier = context.scope.getClassifier(referencedName);
077            if (classifier != null) {
078                JetType classObjectType = classifier.getClassObjectType();
079                if (classObjectType != null) {
080                    context.trace.record(REFERENCE_TARGET, expression, classifier);
081                    JetType result = getExtendedClassObjectType(expression, classObjectType, classifier, context);
082                    checkClassObjectVisibility(classifier, expression, context);
083                    return DataFlowUtils.checkType(result, expression, context);
084                }
085            }
086            JetType[] result = new JetType[1];
087            TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(
088                    context.trace, "trace for namespace/class object lookup of name", referencedName);
089            if (furtherNameLookup(expression, result, context.replaceBindingTrace(temporaryTrace))) {
090                temporaryTrace.commit();
091                return DataFlowUtils.checkType(result[0], expression, context);
092            }
093            // To report NO_CLASS_OBJECT when no namespace found
094            if (classifier != null) {
095                if (classifier instanceof TypeParameterDescriptor) {
096                    if (isLHSOfDot(expression)) {
097                        context.trace.report(TYPE_PARAMETER_ON_LHS_OF_DOT.on(expression, (TypeParameterDescriptor) classifier));
098                    }
099                    else {
100                        context.trace.report(TYPE_PARAMETER_IS_NOT_AN_EXPRESSION.on(expression, (TypeParameterDescriptor) classifier));
101                    }
102                }
103                else if (!isLHSOfDot(expression)) {
104                    context.trace.report(NO_CLASS_OBJECT.on(expression, classifier));
105                }
106                context.trace.record(REFERENCE_TARGET, expression, classifier);
107                JetScope scopeForStaticMembersResolution =
108                        classifier instanceof ClassDescriptor
109                        ? getStaticNestedClassesScope((ClassDescriptor) classifier)
110                        : new JetScopeImpl() {
111                                @NotNull
112                                @Override
113                                public DeclarationDescriptor getContainingDeclaration() {
114                                    return classifier;
115                                }
116    
117                                @Override
118                                public String toString() {
119                                    return "Scope for the type parameter on the left hand side of dot";
120                                }
121    
122                                @Override
123                                public void printScopeStructure(@NotNull Printer p) {
124                                    p.println(toString(), " for ", classifier);
125                                }
126                        };
127                return new NamespaceType(referencedName, scopeForStaticMembersResolution, NO_RECEIVER);
128            }
129            temporaryTrace.commit();
130            return result[0];
131        }
132    
133        private static void checkClassObjectVisibility(
134                @NotNull ClassifierDescriptor classifier,
135                @NotNull JetSimpleNameExpression expression,
136                @NotNull ResolutionContext context
137        ) {
138            if (!(classifier instanceof ClassDescriptor)) return;
139            ClassDescriptor classObject = ((ClassDescriptor) classifier).getClassObjectDescriptor();
140            assert classObject != null : "This check should be done only for classes with class objects: " + classifier;
141            DeclarationDescriptor from = context.scope.getContainingDeclaration();
142            if (!Visibilities.isVisible(classObject, from)) {
143                context.trace.report(INVISIBLE_MEMBER.on(expression, classObject, classObject.getVisibility(), from));
144            }
145        }
146    
147        @NotNull
148        private static JetType getExtendedClassObjectType(
149                @NotNull JetSimpleNameExpression expression,
150                @NotNull JetType classObjectType,
151                @NotNull ClassifierDescriptor classifier,
152                @NotNull ResolutionContext context
153        ) {
154            if (isLHSOfDot(expression) && classifier instanceof ClassDescriptor) {
155                List<JetScope> scopes = new ArrayList<JetScope>(3);
156    
157                scopes.add(classObjectType.getMemberScope());
158                scopes.add(getStaticNestedClassesScope((ClassDescriptor) classifier));
159    
160                Name referencedName = expression.getReferencedNameAsName();
161                NamespaceDescriptor namespace = context.scope.getNamespace(referencedName);
162                if (namespace != null) {
163                    //for enums loaded from java binaries
164                    scopes.add(namespace.getMemberScope());
165                }
166    
167                JetScope scope = new ChainedScope(classifier, scopes.toArray(new JetScope[scopes.size()]));
168                return new NamespaceType(referencedName, scope, new ExpressionReceiver(expression, classObjectType));
169            }
170            return classObjectType;
171        }
172    
173        private boolean furtherNameLookup(
174                @NotNull JetSimpleNameExpression expression,
175                @NotNull JetType[] result,
176                @NotNull ResolutionContext context
177        ) {
178            NamespaceType namespaceType = lookupNamespaceType(expression, context);
179            if (namespaceType == null) {
180                return false;
181            }
182            if (isLHSOfDot(expression)) {
183                result[0] = namespaceType;
184                return true;
185            }
186            context.trace.report(EXPRESSION_EXPECTED_NAMESPACE_FOUND.on(expression));
187            result[0] = ErrorUtils.createErrorType("Type for " + expression.getReferencedNameAsName());
188            return false;
189        }
190    
191        @Nullable
192        private NamespaceType lookupNamespaceType(@NotNull JetSimpleNameExpression expression, @NotNull ResolutionContext context) {
193            Name name = expression.getReferencedNameAsName();
194            NamespaceDescriptor namespace = context.scope.getNamespace(name);
195            if (namespace == null) {
196                return null;
197            }
198            context.trace.record(REFERENCE_TARGET, expression, namespace);
199    
200            // Construct a NamespaceType with everything from the namespace and with nested classes of the corresponding class (if any)
201            JetScope scope;
202            ClassifierDescriptor classifier = context.scope.getClassifier(name);
203            if (classifier instanceof ClassDescriptor) {
204                scope = new ChainedScope(namespace, namespace.getMemberScope(), getStaticNestedClassesScope((ClassDescriptor) classifier));
205            }
206            else {
207                scope = namespace.getMemberScope();
208            }
209            return new NamespaceType(name, scope, NO_RECEIVER);
210        }
211    
212        @Nullable
213        public ResolvedCallWithTrace<FunctionDescriptor> getResolvedCallForFunction(
214                @NotNull Call call, @NotNull JetExpression callExpression,
215                @NotNull ResolutionContext context, @NotNull CheckValueArgumentsMode checkArguments,
216                @NotNull boolean[] result
217        ) {
218            CallResolver callResolver = expressionTypingServices.getCallResolver();
219            OverloadResolutionResultsImpl<FunctionDescriptor> results = callResolver.resolveFunctionCall(
220                    BasicCallResolutionContext.create(context, call, checkArguments));
221            if (!results.isNothing()) {
222                checkSuper(call.getExplicitReceiver(), results, context.trace, callExpression);
223                result[0] = true;
224                return OverloadResolutionResultsUtil.getResultingCall(results, context.contextDependency);
225            }
226            result[0] = false;
227            return null;
228        }
229    
230        @Nullable
231        private JetType getVariableType(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
232                @Nullable ASTNode callOperationNode, @NotNull ResolutionContext context, @NotNull boolean[] result
233        ) {
234            TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
235                    context, "trace to resolve as local variable or property", nameExpression);
236            CallResolver callResolver = expressionTypingServices.getCallResolver();
237            Call call = CallMaker.makePropertyCall(receiver, callOperationNode, nameExpression);
238            BasicCallResolutionContext contextForVariable = BasicCallResolutionContext.create(
239                    context.replaceTraceAndCache(temporaryForVariable),
240                    call, CheckValueArgumentsMode.ENABLED);
241            OverloadResolutionResults<VariableDescriptor> resolutionResult = callResolver.resolveSimpleProperty(contextForVariable);
242            if (!resolutionResult.isNothing()) {
243                temporaryForVariable.commit();
244                checkSuper(receiver, resolutionResult, context.trace, nameExpression);
245                result[0] = true;
246                return resolutionResult.isSingleResult() ? resolutionResult.getResultingDescriptor().getReturnType() : null;
247            }
248    
249            ResolutionContext newContext = receiver.exists()
250                                                 ? context.replaceScope(receiver.getType().getMemberScope())
251                                                 : context;
252            TemporaryTraceAndCache temporaryForNamespaceOrClassObject = TemporaryTraceAndCache.create(
253                    context, "trace to resolve as namespace or class object", nameExpression);
254            JetType jetType = lookupNamespaceOrClassObject(nameExpression, newContext.replaceTraceAndCache(temporaryForNamespaceOrClassObject));
255            if (jetType != null) {
256                temporaryForNamespaceOrClassObject.commit();
257    
258                // Uncommitted changes in temp context
259                context.trace.record(RESOLUTION_SCOPE, nameExpression, context.scope);
260                if (context.dataFlowInfo.hasTypeInfoConstraints()) {
261                    context.trace.record(NON_DEFAULT_EXPRESSION_DATA_FLOW, nameExpression, context.dataFlowInfo);
262                }
263                result[0] = true;
264                return jetType;
265            }
266            temporaryForVariable.commit();
267            result[0] = false;
268            return null;
269        }
270    
271        @NotNull
272        public JetTypeInfo getSimpleNameExpressionTypeInfo(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
273                @Nullable ASTNode callOperationNode, @NotNull ResolutionContext context
274        ) {
275            boolean[] result = new boolean[1];
276    
277            TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
278                    context, "trace to resolve as variable", nameExpression);
279            JetType type = getVariableType(nameExpression, receiver, callOperationNode, context.replaceTraceAndCache(temporaryForVariable), result);
280            if (result[0]) {
281                temporaryForVariable.commit();
282                if (type instanceof NamespaceType && !isLHSOfDot(nameExpression)) {
283                    type = null;
284                }
285                return JetTypeInfo.create(type, context.dataFlowInfo);
286            }
287    
288            Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList());
289            TemporaryTraceAndCache temporaryForFunction = TemporaryTraceAndCache.create(
290                    context, "trace to resolve as function", nameExpression);
291            ResolutionContext newContext = context.replaceTraceAndCache(temporaryForFunction);
292            ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
293                    call, nameExpression, newContext, CheckValueArgumentsMode.ENABLED, result);
294            if (result[0]) {
295                FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
296                temporaryForFunction.commit();
297                boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
298                context.trace.report(FUNCTION_CALL_EXPECTED.on(nameExpression, nameExpression, hasValueParameters));
299                type = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
300                return JetTypeInfo.create(type, context.dataFlowInfo);
301            }
302    
303            temporaryForVariable.commit();
304            return JetTypeInfo.create(null, context.dataFlowInfo);
305        }
306    
307        @NotNull
308        public JetTypeInfo getCallExpressionTypeInfo(
309                @NotNull JetCallExpression callExpression, @NotNull ReceiverValue receiver,
310                @Nullable ASTNode callOperationNode, @NotNull ResolutionContext context
311        ) {
312            JetTypeInfo typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck(
313                    callExpression, receiver, callOperationNode, context);
314            if (context.contextDependency == INDEPENDENT) {
315                DataFlowUtils.checkType(typeInfo, callExpression, context);
316            }
317            return typeInfo;
318        }
319    
320        @NotNull
321        public JetTypeInfo getCallExpressionTypeInfoWithoutFinalTypeCheck(
322                @NotNull JetCallExpression callExpression, @NotNull ReceiverValue receiver,
323                @Nullable ASTNode callOperationNode, @NotNull ResolutionContext context
324        ) {
325            boolean[] result = new boolean[1];
326            Call call = CallMaker.makeCall(receiver, callOperationNode, callExpression);
327    
328            TemporaryTraceAndCache temporaryForFunction = TemporaryTraceAndCache.create(
329                    context, "trace to resolve as function call", callExpression);
330            ResolvedCallWithTrace<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
331                    call, callExpression, context.replaceTraceAndCache(temporaryForFunction),
332                    CheckValueArgumentsMode.ENABLED, result);
333            if (result[0]) {
334                FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
335                temporaryForFunction.commit();
336                if (callExpression.getValueArgumentList() == null && callExpression.getFunctionLiteralArguments().isEmpty()) {
337                    // there are only type arguments
338                    boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
339                    context.trace.report(FUNCTION_CALL_EXPECTED.on(callExpression, callExpression, hasValueParameters));
340                }
341                if (functionDescriptor == null) {
342                    return JetTypeInfo.create(null, context.dataFlowInfo);
343                }
344                JetType type = functionDescriptor.getReturnType();
345    
346                return JetTypeInfo.create(type, resolvedCall.getDataFlowInfoForArguments().getResultInfo());
347            }
348    
349            JetExpression calleeExpression = callExpression.getCalleeExpression();
350            if (calleeExpression instanceof JetSimpleNameExpression && callExpression.getTypeArgumentList() == null) {
351                TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
352                        context, "trace to resolve as variable with 'invoke' call", callExpression);
353                JetType type = getVariableType((JetSimpleNameExpression) calleeExpression, receiver, callOperationNode,
354                                               context.replaceTraceAndCache(temporaryForVariable), result);
355                if (result[0]) {
356                    temporaryForVariable.commit();
357                    context.trace.report(FUNCTION_EXPECTED.on((JetReferenceExpression) calleeExpression, calleeExpression,
358                                                              type != null ? type : ErrorUtils.createErrorType("")));
359                    return JetTypeInfo.create(null, context.dataFlowInfo);
360                }
361            }
362            temporaryForFunction.commit();
363            return JetTypeInfo.create(null, context.dataFlowInfo);
364        }
365    
366        private void checkSuper(@NotNull ReceiverValue receiverValue, @NotNull OverloadResolutionResults<? extends CallableDescriptor> results,
367                @NotNull BindingTrace trace, @NotNull JetExpression expression) {
368            if (!results.isSingleResult()) return;
369            if (!(receiverValue instanceof ExpressionReceiver)) return;
370            JetExpression receiver = ((ExpressionReceiver) receiverValue).getExpression();
371            CallableDescriptor descriptor = results.getResultingDescriptor();
372            if (receiver instanceof JetSuperExpression && descriptor instanceof MemberDescriptor) {
373                if (((MemberDescriptor) descriptor).getModality() == Modality.ABSTRACT) {
374                    trace.report(ABSTRACT_SUPER_CALL.on(expression));
375                }
376            }
377        }
378    
379        @NotNull
380        private JetTypeInfo getSelectorReturnTypeInfo(
381                @NotNull ReceiverValue receiver,
382                @Nullable ASTNode callOperationNode,
383                @NotNull JetExpression selectorExpression,
384                @NotNull ResolutionContext context
385        ) {
386            if (selectorExpression instanceof JetCallExpression) {
387                return getCallExpressionTypeInfoWithoutFinalTypeCheck((JetCallExpression) selectorExpression, receiver,
388                                                                      callOperationNode, context);
389            }
390            else if (selectorExpression instanceof JetSimpleNameExpression) {
391                return getSimpleNameExpressionTypeInfo((JetSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
392            }
393            else {
394                context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.getText()));
395            }
396            return JetTypeInfo.create(null, context.dataFlowInfo);
397        }
398    
399        @NotNull
400        public JetTypeInfo getQualifiedExpressionTypeInfo(
401                @NotNull JetQualifiedExpression expression, @NotNull ResolutionContext context
402        ) {
403            // TODO : functions as values
404            JetExpression selectorExpression = expression.getSelectorExpression();
405            JetExpression receiverExpression = expression.getReceiverExpression();
406            ResolutionContext contextForReceiver = context.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
407            JetTypeInfo receiverTypeInfo = expressionTypingServices.getTypeInfo(receiverExpression, contextForReceiver);
408            JetType receiverType = receiverTypeInfo.getType();
409            if (selectorExpression == null) return JetTypeInfo.create(null, context.dataFlowInfo);
410            if (receiverType == null) receiverType = ErrorUtils.createErrorType("Type for " + expression.getText());
411    
412            context = context.replaceDataFlowInfo(receiverTypeInfo.getDataFlowInfo());
413    
414            if (selectorExpression instanceof JetSimpleNameExpression) {
415                ConstantUtils.propagateConstantValues(expression, context.trace, (JetSimpleNameExpression) selectorExpression);
416            }
417    
418            JetTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(
419                    new ExpressionReceiver(receiverExpression, receiverType),
420                    expression.getOperationTokenNode(), selectorExpression, context);
421            JetType selectorReturnType = selectorReturnTypeInfo.getType();
422    
423            //TODO move further
424            if (!(receiverType instanceof NamespaceType) && expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
425                if (selectorReturnType != null && !selectorReturnType.isNullable() && !KotlinBuiltIns.getInstance().isUnit(selectorReturnType)) {
426                    if (receiverType.isNullable()) {
427                        selectorReturnType = TypeUtils.makeNullable(selectorReturnType);
428                    }
429                }
430            }
431    
432            // TODO : this is suspicious: remove this code?
433            if (selectorReturnType != null) {
434                context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, selectorReturnType);
435            }
436            JetTypeInfo typeInfo = JetTypeInfo.create(selectorReturnType, selectorReturnTypeInfo.getDataFlowInfo());
437            if (context.contextDependency == INDEPENDENT) {
438                DataFlowUtils.checkType(typeInfo, expression, context);
439            }
440            return typeInfo;
441        }
442    }