All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.resolve.calls.CallTransformer Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.resolve.calls;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.ChainedTemporaryBindingTrace;
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace;
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext;
import org.jetbrains.kotlin.resolve.calls.context.CandidateResolveMode;
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallImpl;
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCallImpl;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl;
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind;
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate;
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionTask;
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategyForInvoke;
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;

/**
 * CallTransformer treats specially 'variable as function' call case, other cases keeps unchanged (base realization).
 *
 * For the call 'b.foo(1)' where foo is a variable that has method 'invoke' (for example of function type)
 * CallTransformer creates two contexts, two calls in each, and performs second ('invoke') call resolution:
 *
 *   context#1. calls: 'b.foo' 'invoke(1)'
 *   context#2. calls: 'foo'   'b.invoke(1)'
 *
 * If success VariableAsFunctionResolvedCall is created.
 */
public class CallTransformer {
    private CallTransformer() {}

    /**
     * Returns two contexts for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER), one context otherwise
     */
    @NotNull
    public Collection> createCallContexts(@NotNull ResolutionCandidate candidate,
            @NotNull ResolutionTask task,
            @NotNull TemporaryBindingTrace candidateTrace,
            @NotNull CandidateResolveMode candidateResolveMode
    ) {
        ResolvedCallImpl candidateCall = ResolvedCallImpl.create(candidate, candidateTrace, task.tracing, task.dataFlowInfoForArguments);
        return Collections.singleton(CallCandidateResolutionContext.create(candidateCall, task, candidateTrace, task.tracing, task.call,
                                                                           ReceiverValue.NO_RECEIVER, candidateResolveMode));
    }

    /**
     * Returns collection of resolved calls for 'invoke' for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER),
     * the resolved call from callCandidateResolutionContext otherwise
     */
    @NotNull
    public Collection> transformCall(@NotNull CallCandidateResolutionContext callCandidateResolutionContext,
            @NotNull CallResolver callResolver,
            @NotNull ResolutionTask task
    ) {
        return Collections.singleton((MutableResolvedCall) callCandidateResolutionContext.candidateCall);
    }


    public static CallTransformer MEMBER_CALL_TRANSFORMER = new CallTransformer();

    public static CallTransformer VARIABLE_CALL_TRANSFORMER = new CallTransformer();

    public static CallTransformer FUNCTION_CALL_TRANSFORMER = new CallTransformer() {
        @NotNull
        @Override
        public Collection> createCallContexts(@NotNull ResolutionCandidate candidate,
                @NotNull ResolutionTask task, @NotNull TemporaryBindingTrace candidateTrace,
                @NotNull CandidateResolveMode candidateResolveMode
        ) {

            if (candidate.getDescriptor() instanceof FunctionDescriptor) {
                return super.createCallContexts(candidate, task, candidateTrace, candidateResolveMode);
            }

            assert candidate.getDescriptor() instanceof VariableDescriptor;

            boolean hasReceiver = candidate.getExtensionReceiver().exists();
            Call variableCall = stripCallArguments(task.call);
            ResolutionCandidate variableCandidate = ResolutionCandidate.create(
                    variableCall,
                    candidate.getDescriptor(),
                    candidate.getDispatchReceiver(),
                    candidate.getExtensionReceiver(),
                    candidate.getExplicitReceiverKind(),
                    null);
            if (!hasReceiver) {
                CallCandidateResolutionContext context = CallCandidateResolutionContext.create(
                        ResolvedCallImpl.create(variableCandidate, candidateTrace, task.tracing, task.dataFlowInfoForArguments),
                        task, candidateTrace, task.tracing, variableCall, ReceiverValue.NO_RECEIVER, candidateResolveMode);
                return Collections.singleton(context);
            }
            CallCandidateResolutionContext contextWithReceiver = createContextWithChainedTrace(
                    variableCandidate, variableCall, candidateTrace, task, ReceiverValue.NO_RECEIVER, candidateResolveMode);

            Call variableCallWithoutReceiver = stripReceiver(variableCall);
            ResolutionCandidate candidateWithoutReceiver = ResolutionCandidate.create(
                    variableCallWithoutReceiver,
                    candidate.getDescriptor(),
                    candidate.getDispatchReceiver(),
                    ReceiverValue.NO_RECEIVER,
                    ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, null);

            CallCandidateResolutionContext contextWithoutReceiver = createContextWithChainedTrace(
                    candidateWithoutReceiver, variableCallWithoutReceiver, candidateTrace, task, variableCall.getExplicitReceiver(),
                    candidateResolveMode);

            return Lists.newArrayList(contextWithReceiver, contextWithoutReceiver);
        }

        private CallCandidateResolutionContext createContextWithChainedTrace(
                @NotNull ResolutionCandidate candidate, @NotNull Call call, @NotNull TemporaryBindingTrace temporaryTrace,
                @NotNull ResolutionTask task, @NotNull ReceiverValue receiverValue,
                @NotNull CandidateResolveMode candidateResolveMode
        ) {
            ChainedTemporaryBindingTrace chainedTrace = ChainedTemporaryBindingTrace.create(temporaryTrace, "chained trace to resolve candidate", candidate);
            ResolvedCallImpl resolvedCall = ResolvedCallImpl.create(candidate, chainedTrace, task.tracing, task.dataFlowInfoForArguments);
            return CallCandidateResolutionContext.create(resolvedCall, task, chainedTrace, task.tracing, call, receiverValue,
                                                         candidateResolveMode);
        }

        private Call stripCallArguments(@NotNull Call call) {
            return new DelegatingCall(call) {
                @Override
                public JetValueArgumentList getValueArgumentList() {
                    return null;
                }

                @NotNull
                @Override
                public List getValueArguments() {
                    return Collections.emptyList();
                }

                @NotNull
                @Override
                public List getFunctionLiteralArguments() {
                    return Collections.emptyList();
                }

                @NotNull
                @Override
                public List getTypeArguments() {
                    return Collections.emptyList();
                }

                @Override
                public JetTypeArgumentList getTypeArgumentList() {
                    return null;
                }

                @NotNull
                @Override
                public JetElement getCallElement() {
                    JetExpression calleeExpression = getCalleeExpression();
                    assert calleeExpression != null : "No callee expression: " + getCallElement().getText();

                    return calleeExpression;
                }
            };
        }

        private Call stripReceiver(@NotNull Call variableCall) {
            return new DelegatingCall(variableCall) {
                @Nullable
                @Override
                public ASTNode getCallOperationNode() {
                    return null;
                }

                @NotNull
                @Override
                public ReceiverValue getExplicitReceiver() {
                    return ReceiverValue.NO_RECEIVER;
                }
            };
        }

        @NotNull
        @Override
        public Collection> transformCall(
                @NotNull CallCandidateResolutionContext context,
                @NotNull CallResolver callResolver,
                @NotNull ResolutionTask task
        ) {
            CallableDescriptor descriptor = context.candidateCall.getCandidateDescriptor();
            if (descriptor instanceof FunctionDescriptor) {
                return super.transformCall(context, callResolver, task);
            }

            assert descriptor instanceof VariableDescriptor;
            JetType returnType = descriptor.getReturnType();
            if (returnType == null) {
                return Collections.emptyList();
            }

            final MutableResolvedCall variableResolvedCall = (MutableResolvedCall)context.candidateCall;

            JetExpression calleeExpression = task.call.getCalleeExpression();
            if (calleeExpression == null) return Collections.emptyList();

            ExpressionReceiver variableReceiver = new ExpressionReceiver(calleeExpression, variableResolvedCall.getResultingDescriptor().getType());
            Call functionCall = new CallForImplicitInvoke(context.explicitExtensionReceiverForInvoke, variableReceiver, task.call);

            DelegatingBindingTrace variableCallTrace = context.candidateCall.getTrace();
            BasicCallResolutionContext basicCallResolutionContext = BasicCallResolutionContext.create(
                    context.replaceBindingTrace(variableCallTrace).replaceContextDependency(ContextDependency.DEPENDENT),
                    functionCall, context.checkArguments, context.dataFlowInfoForArguments);

            // 'invoke' call resolve
            TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(
                    calleeExpression, functionCall, variableReceiver.getType());
            OverloadResolutionResults results = callResolver.resolveCallForInvoke(
                    basicCallResolutionContext, tracingForInvoke);
            Collection> calls = ((OverloadResolutionResultsImpl)results).getResultingCalls();

            return Collections2.transform(calls, new Function, MutableResolvedCall>() {
                @Override
                public MutableResolvedCall apply(MutableResolvedCall functionResolvedCall) {
                    return new VariableAsFunctionResolvedCallImpl(functionResolvedCall, variableResolvedCall);
                }
            });
        }
    };

    public static class CallForImplicitInvoke extends DelegatingCall {
        private final Call outerCall;
        private final ReceiverValue explicitExtensionReceiver;
        private final ExpressionReceiver calleeExpressionAsDispatchReceiver;
        private final JetSimpleNameExpression fakeInvokeExpression;

        public CallForImplicitInvoke(
                @NotNull ReceiverValue explicitExtensionReceiver,
                @NotNull ExpressionReceiver calleeExpressionAsDispatchReceiver,
                @NotNull Call call
        ) {
            super(call);
            this.outerCall = call;
            this.explicitExtensionReceiver = explicitExtensionReceiver;
            this.calleeExpressionAsDispatchReceiver = calleeExpressionAsDispatchReceiver;
            this.fakeInvokeExpression =
                    (JetSimpleNameExpression) JetPsiFactory(call.getCallElement()).createExpression(OperatorConventions.INVOKE.asString());
        }

        @Nullable
        @Override
        public ASTNode getCallOperationNode() {
            // if an explicit receiver corresponds to the implicit invoke, there is a corresponding call operation node:
            // a.b() or a?.b() (where b has an extension function type);
            // otherwise it's implicit
            return explicitExtensionReceiver.exists() ? super.getCallOperationNode() : null;
        }

        @NotNull
        @Override
        public ReceiverValue getExplicitReceiver() {
            return explicitExtensionReceiver;
        }

        @NotNull
        @Override
        public ExpressionReceiver getDispatchReceiver() {
            return calleeExpressionAsDispatchReceiver;
        }

        @Override
        public JetExpression getCalleeExpression() {
            return fakeInvokeExpression;
        }

        @NotNull
        @Override
        public CallType getCallType() {
            return CallType.INVOKE;
        }

        @NotNull
        public Call getOuterCall() {
            return outerCall;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy