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.model.ResolvedCallImpl;
034    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
035    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
036    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
037    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
038    import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind;
039    import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate;
040    import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask;
041    import org.jetbrains.jet.lang.resolve.calls.util.DelegatingCall;
042    import org.jetbrains.jet.lang.resolve.name.Name;
043    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
044    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
045    import org.jetbrains.jet.lang.types.JetType;
046    
047    import java.util.Collection;
048    import java.util.Collections;
049    import 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     */
062    public 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    }