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