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.calls;
018
019import com.google.common.base.Function;
020import com.google.common.collect.Collections2;
021import com.google.common.collect.Lists;
022import com.intellij.psi.PsiElement;
023import org.jetbrains.annotations.NotNull;
024import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
025import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
026import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
027import org.jetbrains.jet.lang.psi.*;
028import org.jetbrains.jet.lang.resolve.ChainedTemporaryBindingTrace;
029import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace;
030import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
031import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
032import org.jetbrains.jet.lang.resolve.calls.context.CallCandidateResolutionContext;
033import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl;
034import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
035import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
036import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
037import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
038import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind;
039import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate;
040import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask;
041import org.jetbrains.jet.lang.resolve.calls.util.DelegatingCall;
042import org.jetbrains.jet.lang.resolve.name.Name;
043import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
044import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
045import org.jetbrains.jet.lang.types.JetType;
046
047import java.util.Collection;
048import java.util.Collections;
049import java.util.List;
050
051/**
052 * CallTransformer treats specially 'variable as function' call case, other cases keeps unchanged (base realization).
053 *
054 * For the call 'b.foo(1)' where foo is a variable that has method 'invoke' (for example of function type)
055 * CallTransformer creates two contexts, two calls in each, and performs second ('invoke') call resolution:
056 *
057 *   context#1. calls: 'b.foo' 'invoke(1)'
058 *   context#2. calls: 'foo'   'b.invoke(1)'
059 *
060 * If success VariableAsFunctionResolvedCall is created.
061 */
062public class CallTransformer<D extends CallableDescriptor, F extends D> {
063    private CallTransformer() {}
064
065    /**
066     * Returns two contexts for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER), one context otherwise
067     */
068    @NotNull
069    public Collection<CallCandidateResolutionContext<D>> createCallContexts(@NotNull ResolutionCandidate<D> candidate,
070            @NotNull ResolutionTask<D, F> task,
071            @NotNull TemporaryBindingTrace candidateTrace) {
072
073        ResolvedCallImpl<D> candidateCall = ResolvedCallImpl.create(candidate, candidateTrace, task.tracing);
074        return Collections.singleton(CallCandidateResolutionContext.create(candidateCall, task, candidateTrace, task.tracing));
075    }
076
077    /**
078     * Returns collection of resolved calls for 'invoke' for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER),
079     * the resolved call from callCandidateResolutionContext otherwise
080     */
081    @NotNull
082    public Collection<ResolvedCallWithTrace<F>> transformCall(@NotNull CallCandidateResolutionContext<D> callCandidateResolutionContext,
083            @NotNull CallResolver callResolver,
084            @NotNull ResolutionTask<D, F> task) {
085
086        return Collections.singleton((ResolvedCallWithTrace<F>) callCandidateResolutionContext.candidateCall);
087    }
088
089
090    public static CallTransformer<VariableDescriptor, VariableDescriptor> PROPERTY_CALL_TRANSFORMER = new CallTransformer<VariableDescriptor, VariableDescriptor>();
091
092    public static CallTransformer<CallableDescriptor, FunctionDescriptor> FUNCTION_CALL_TRANSFORMER = new CallTransformer<CallableDescriptor, FunctionDescriptor>() {
093        @NotNull
094        @Override
095        public Collection<CallCandidateResolutionContext<CallableDescriptor>> createCallContexts(@NotNull ResolutionCandidate<CallableDescriptor> candidate,
096                @NotNull ResolutionTask<CallableDescriptor, FunctionDescriptor> task, @NotNull TemporaryBindingTrace candidateTrace) {
097
098            if (candidate.getDescriptor() instanceof FunctionDescriptor) {
099                return super.createCallContexts(candidate, task, candidateTrace);
100            }
101
102            assert candidate.getDescriptor() instanceof VariableDescriptor;
103
104            boolean hasReceiver = candidate.getReceiverArgument().exists();
105            Call variableCall = stripCallArguments(task);
106            if (!hasReceiver) {
107                CallCandidateResolutionContext<CallableDescriptor> context = CallCandidateResolutionContext.create(
108                        ResolvedCallImpl.create(candidate, candidateTrace, task.tracing), task, candidateTrace, task.tracing, variableCall);
109                return Collections.singleton(context);
110            }
111            Call variableCallWithoutReceiver = stripReceiver(variableCall);
112            CallCandidateResolutionContext<CallableDescriptor> contextWithReceiver = createContextWithChainedTrace(
113                    candidate, variableCall, candidateTrace, task);
114
115            ResolutionCandidate<CallableDescriptor> candidateWithoutReceiver = ResolutionCandidate.create(
116                    candidate.getDescriptor(), candidate.getThisObject(), ReceiverValue.NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, false);
117
118            CallCandidateResolutionContext<CallableDescriptor> contextWithoutReceiver = createContextWithChainedTrace(
119                    candidateWithoutReceiver, variableCallWithoutReceiver, candidateTrace, task);
120
121            contextWithoutReceiver.receiverForVariableAsFunctionSecondCall = variableCall.getExplicitReceiver();
122
123            return Lists.newArrayList(contextWithReceiver, contextWithoutReceiver);
124        }
125
126        private CallCandidateResolutionContext<CallableDescriptor> createContextWithChainedTrace(ResolutionCandidate<CallableDescriptor> candidate,
127                Call call, TemporaryBindingTrace temporaryTrace, ResolutionTask<CallableDescriptor, FunctionDescriptor> task) {
128
129            ChainedTemporaryBindingTrace chainedTrace = ChainedTemporaryBindingTrace.create(temporaryTrace, "chained trace to resolve candidate", candidate);
130            ResolvedCallImpl<CallableDescriptor> resolvedCall = ResolvedCallImpl.create(candidate, chainedTrace, task.tracing);
131            return CallCandidateResolutionContext.create(resolvedCall, task, chainedTrace, task.tracing, call);
132        }
133
134        private Call stripCallArguments(@NotNull ResolutionTask<CallableDescriptor, FunctionDescriptor> task) {
135            return new DelegatingCall(task.call) {
136                @Override
137                public JetValueArgumentList getValueArgumentList() {
138                    return null;
139                }
140
141                @NotNull
142                @Override
143                public List<? extends ValueArgument> getValueArguments() {
144                    return Collections.emptyList();
145                }
146
147                @NotNull
148                @Override
149                public List<JetExpression> getFunctionLiteralArguments() {
150                    return Collections.emptyList();
151                }
152
153                @NotNull
154                @Override
155                public List<JetTypeProjection> getTypeArguments() {
156                    return Collections.emptyList();
157                }
158
159                @Override
160                public JetTypeArgumentList getTypeArgumentList() {
161                    return null;
162                }
163            };
164        }
165
166        private Call stripReceiver(@NotNull Call variableCall) {
167            return new DelegatingCall(variableCall) {
168                @NotNull
169                @Override
170                public ReceiverValue getExplicitReceiver() {
171                    return ReceiverValue.NO_RECEIVER;
172                }
173            };
174        }
175
176        @NotNull
177        @Override
178        public Collection<ResolvedCallWithTrace<FunctionDescriptor>> transformCall(@NotNull CallCandidateResolutionContext<CallableDescriptor> context,
179                @NotNull CallResolver callResolver, @NotNull ResolutionTask<CallableDescriptor, FunctionDescriptor> task) {
180
181            CallableDescriptor descriptor = context.candidateCall.getCandidateDescriptor();
182            if (descriptor instanceof FunctionDescriptor) {
183                return super.transformCall(context, callResolver, task);
184            }
185
186            assert descriptor instanceof VariableDescriptor;
187            JetType returnType = descriptor.getReturnType();
188            if (returnType == null) {
189                return Collections.emptyList();
190            }
191
192            final ResolvedCallWithTrace<VariableDescriptor> variableResolvedCall = (ResolvedCallWithTrace)context.candidateCall;
193
194            Call functionCall = new CallForImplicitInvoke(context, task, returnType);
195
196            DelegatingBindingTrace variableCallTrace = context.candidateCall.getTrace();
197            BasicCallResolutionContext basicCallResolutionContext = BasicCallResolutionContext.create(
198                    variableCallTrace, context.scope, functionCall, context.expectedType, context.dataFlowInfo,
199                    context.resolveMode, context.checkArguments, context.expressionPosition, context.resolutionResultsCache);
200
201            // 'invoke' call resolve
202            OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallWithGivenName(basicCallResolutionContext, task.reference, Name.identifier("invoke"));
203            Collection<ResolvedCallWithTrace<FunctionDescriptor>> calls = ((OverloadResolutionResultsImpl<FunctionDescriptor>)results).getResultingCalls();
204
205            return Collections2.transform(calls, new Function<ResolvedCallWithTrace<FunctionDescriptor>, ResolvedCallWithTrace<FunctionDescriptor>>() {
206                @Override
207                public ResolvedCallWithTrace<FunctionDescriptor> apply(ResolvedCallWithTrace<FunctionDescriptor> functionResolvedCall) {
208                    return new VariableAsFunctionResolvedCall(functionResolvedCall, variableResolvedCall);
209                }
210            });
211        }
212    };
213
214    public static class CallForImplicitInvoke extends DelegatingCall {
215        final Call outerCall;
216        final CallCandidateResolutionContext<CallableDescriptor> context;
217        final ExpressionReceiver receiverFromVariable;
218        final JetSimpleNameExpression invokeExpression;
219
220        private CallForImplicitInvoke(CallCandidateResolutionContext<CallableDescriptor> context,
221                        ResolutionTask<CallableDescriptor, FunctionDescriptor> task, JetType returnType) {
222            super(task.call);
223            this.outerCall = task.call;
224            this.context = context;
225            this.receiverFromVariable = new ExpressionReceiver(task.reference, returnType);
226            this.invokeExpression = (JetSimpleNameExpression) JetPsiFactory.createExpression(task.call.getCallElement().getProject(), "invoke");
227        }
228        @NotNull
229        @Override
230        public ReceiverValue getExplicitReceiver() {
231            return context.receiverForVariableAsFunctionSecondCall;
232        }
233
234        @NotNull
235        @Override
236        public ReceiverValue getThisObject() {
237            return receiverFromVariable;
238        }
239
240        @Override
241        public JetExpression getCalleeExpression() {
242            return invokeExpression;
243        }
244
245        @NotNull
246        @Override
247        public PsiElement getCallElement() {
248            if (outerCall.getCallElement() instanceof JetCallElement) {
249                //to report errors properly
250                JetValueArgumentList list = ((JetCallElement)outerCall.getCallElement()).getValueArgumentList();
251                if (list != null) {
252                    return list;
253                }
254            }
255            return invokeExpression;
256        }
257
258        @NotNull
259        @Override
260        public CallType getCallType() {
261            return CallType.INVOKE;
262        }
263
264        @NotNull
265        public Call getOuterCall() {
266            return outerCall;
267        }
268    }
269}