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.psi.PsiElement;
020    import com.intellij.psi.util.PsiTreeUtil;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.diagnostics.Errors;
025    import org.jetbrains.jet.lang.psi.*;
026    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027    import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
028    import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
029    import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument;
030    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
031    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
032    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
033    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
034    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver;
035    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
036    import org.jetbrains.jet.lang.types.JetType;
037    import org.jetbrains.jet.lang.types.lang.InlineUtil;
038    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
039    import org.jetbrains.jet.lexer.JetToken;
040    import org.jetbrains.jet.lexer.JetTokens;
041    
042    import java.util.HashSet;
043    import java.util.Map;
044    import java.util.Set;
045    
046    import static org.jetbrains.jet.lang.resolve.InlineDescriptorUtils.allowsNonLocalReturns;
047    import static org.jetbrains.jet.lang.resolve.InlineDescriptorUtils.checkNonLocalReturnUsage;
048    
049    public class InlineCallResolverExtension implements CallResolverExtension {
050    
051        private final SimpleFunctionDescriptor descriptor;
052    
053        private final Set<CallableDescriptor> inlinableParameters = new HashSet<CallableDescriptor>();
054    
055        private final boolean isEffectivelyPublicApiFunction;
056    
057        public InlineCallResolverExtension(@NotNull SimpleFunctionDescriptor descriptor) {
058            assert descriptor.getInlineStrategy().isInline() : "This extension should be created only for inline functions but not " + descriptor;
059            this.descriptor = descriptor;
060            this.isEffectivelyPublicApiFunction = isEffectivelyPublicApi(descriptor);
061    
062            for (ValueParameterDescriptor param : descriptor.getValueParameters()) {
063                if (isInlinableParameter(param)) {
064                    inlinableParameters.add(param);
065                }
066            }
067    
068            //add extension receiver as inlinable
069            ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
070            if (receiverParameter != null) {
071                if (isInlinableParameter(receiverParameter)) {
072                    inlinableParameters.add(receiverParameter);
073                }
074            }
075        }
076    
077        @Override
078        public <F extends CallableDescriptor> void run(@NotNull ResolvedCall<F> resolvedCall, @NotNull BasicCallResolutionContext context) {
079            JetExpression expression = context.call.getCalleeExpression();
080            if (expression == null) {
081                return;
082            }
083    
084            //checking that only invoke or inlinable extension called on function parameter
085            CallableDescriptor targetDescriptor = resolvedCall.getResultingDescriptor();
086            checkCallWithReceiver(context, targetDescriptor, resolvedCall.getDispatchReceiver(), expression);
087            checkCallWithReceiver(context, targetDescriptor, resolvedCall.getExtensionReceiver(), expression);
088    
089            if (inlinableParameters.contains(targetDescriptor)) {
090                if (!isInsideCall(expression)) {
091                    context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor));
092                }
093            }
094    
095            for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
096                ResolvedValueArgument value = entry.getValue();
097                ValueParameterDescriptor valueDescriptor = entry.getKey();
098                if (!(value instanceof DefaultValueArgument)) {
099                    for (ValueArgument argument : value.getArguments()) {
100                        checkValueParameter(context, targetDescriptor, argument, valueDescriptor);
101                    }
102                }
103            }
104    
105            checkVisibility(targetDescriptor, expression, context);
106            checkRecursion(context, targetDescriptor, expression);
107        }
108    
109        private static boolean isInsideCall(JetExpression expression) {
110            JetElement parent = JetPsiUtil.getParentCallIfPresent(expression);
111            if (parent instanceof JetBinaryExpression) {
112                JetToken token = JetPsiUtil.getOperationToken((JetOperationExpression) parent);
113                if (token == JetTokens.EQ || token == JetTokens.ANDAND || token == JetTokens.OROR) {
114                    //assignment
115                    return false;
116                }
117            }
118    
119            return parent != null;
120        }
121    
122    
123    
124        private void checkValueParameter(
125                @NotNull BasicCallResolutionContext context,
126                @NotNull CallableDescriptor targetDescriptor,
127                @NotNull ValueArgument targetArgument,
128                @NotNull ValueParameterDescriptor targetParameterDescriptor
129        ) {
130            JetExpression argumentExpression = targetArgument.getArgumentExpression();
131            if (argumentExpression == null) {
132                return;
133            }
134            CallableDescriptor argumentCallee = getCalleeDescriptor(context, argumentExpression, false);
135    
136            if (argumentCallee != null && inlinableParameters.contains(argumentCallee)) {
137                boolean isTargetInlineFunction = targetDescriptor instanceof SimpleFunctionDescriptor &&
138                                                 ((SimpleFunctionDescriptor) targetDescriptor).getInlineStrategy().isInline();
139    
140                if (!isTargetInlineFunction || !isInlinableParameter(targetParameterDescriptor)) {
141                    context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(argumentExpression, argumentExpression, descriptor));
142                } else {
143                    if (allowsNonLocalReturns(argumentCallee) && !allowsNonLocalReturns(targetParameterDescriptor)) {
144                        context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(argumentExpression, argumentExpression, argumentCallee, descriptor));
145                    } else {
146                        checkNonLocalReturn(context, argumentCallee, argumentExpression);
147                    }
148                }
149            }
150        }
151    
152        private void checkCallWithReceiver(
153                @NotNull BasicCallResolutionContext context,
154                @NotNull CallableDescriptor targetDescriptor,
155                @NotNull ReceiverValue receiver,
156                @Nullable JetExpression expression
157        ) {
158            if (!receiver.exists()) return;
159    
160            CallableDescriptor varDescriptor = null;
161            JetExpression receiverExpression = null;
162            if (receiver instanceof ExpressionReceiver) {
163                receiverExpression = ((ExpressionReceiver) receiver).getExpression();
164                varDescriptor = getCalleeDescriptor(context, receiverExpression, true);
165            }
166            else if (receiver instanceof ExtensionReceiver) {
167                ExtensionReceiver extensionReceiver = (ExtensionReceiver) receiver;
168                CallableDescriptor extension = extensionReceiver.getDeclarationDescriptor();
169    
170                varDescriptor = extension.getExtensionReceiverParameter();
171                assert varDescriptor != null : "Extension should have receiverParameterDescriptor: " + extension;
172    
173                receiverExpression = expression;
174            }
175    
176            if (inlinableParameters.contains(varDescriptor)) {
177                //check that it's invoke or inlinable extension
178                checkLambdaInvokeOrExtensionCall(context, varDescriptor, targetDescriptor, receiverExpression);
179            }
180        }
181    
182        @Nullable
183        private static CallableDescriptor getCalleeDescriptor(
184                @NotNull BasicCallResolutionContext context,
185                @NotNull JetExpression expression,
186                boolean unwrapVariableAsFunction
187        ) {
188            if (!(expression instanceof JetSimpleNameExpression || expression instanceof JetThisExpression)) return null;
189    
190            ResolvedCall<?> thisCall = CallUtilPackage.getResolvedCall(expression, context.trace.getBindingContext());
191            if (unwrapVariableAsFunction && thisCall instanceof VariableAsFunctionResolvedCall) {
192                return ((VariableAsFunctionResolvedCall) thisCall).getVariableCall().getResultingDescriptor();
193            }
194            return thisCall != null ? thisCall.getResultingDescriptor() : null;
195        }
196    
197        private void checkLambdaInvokeOrExtensionCall(
198                @NotNull BasicCallResolutionContext context,
199                @NotNull CallableDescriptor lambdaDescriptor,
200                @NotNull CallableDescriptor callDescriptor,
201                @NotNull JetExpression receiverExpresssion
202        ) {
203            boolean inlinableCall = isInvokeOrInlineExtension(callDescriptor);
204            if (!inlinableCall) {
205                context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(receiverExpresssion, receiverExpresssion, descriptor));
206            } else {
207                checkNonLocalReturn(context, lambdaDescriptor, receiverExpresssion);
208            }
209        }
210    
211        public void checkRecursion(
212                @NotNull BasicCallResolutionContext context,
213                @NotNull CallableDescriptor targetDescriptor,
214                @NotNull JetElement expression
215        ) {
216            if (targetDescriptor.getOriginal() == descriptor) {
217                context.trace.report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor));
218            }
219        }
220    
221        private static boolean isInlinableParameter(@NotNull CallableDescriptor descriptor) {
222            JetType type = descriptor.getReturnType();
223            return type != null &&
224                   KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(type) &&
225                   !type.isNullable() &&
226                   !InlineUtil.hasNoinlineAnnotation(descriptor);
227        }
228    
229        private static boolean isInvokeOrInlineExtension(@NotNull CallableDescriptor descriptor) {
230            if (!(descriptor instanceof SimpleFunctionDescriptor)) {
231                return false;
232            }
233    
234            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
235            boolean isInvoke = descriptor.getName().asString().equals("invoke") &&
236                               containingDeclaration instanceof ClassDescriptor &&
237                               KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(((ClassDescriptor) containingDeclaration).getDefaultType());
238    
239            return isInvoke ||
240                   //or inline extension
241                   ((SimpleFunctionDescriptor) descriptor).getInlineStrategy().isInline();
242        }
243    
244        private void checkVisibility(@NotNull CallableDescriptor declarationDescriptor, @NotNull JetElement expression, @NotNull BasicCallResolutionContext context){
245            if (isEffectivelyPublicApiFunction && !isEffectivelyPublicApi(declarationDescriptor) && declarationDescriptor.getVisibility() != Visibilities.LOCAL) {
246                context.trace.report(Errors.INVISIBLE_MEMBER_FROM_INLINE.on(expression, declarationDescriptor, descriptor));
247            }
248        }
249    
250        private static boolean isEffectivelyPublicApi(DeclarationDescriptorWithVisibility descriptor) {
251            DeclarationDescriptorWithVisibility parent = descriptor;
252            while (parent != null) {
253                if (!parent.getVisibility().isPublicAPI()) {
254                    return false;
255                }
256                parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class);
257            }
258            return true;
259        }
260    
261        private void checkNonLocalReturn(
262                @NotNull BasicCallResolutionContext context,
263                @NotNull CallableDescriptor inlinableParameterDescriptor,
264                @NotNull JetExpression parameterUsage
265        ) {
266            if (!allowsNonLocalReturns(inlinableParameterDescriptor)) return;
267    
268            if (!checkNonLocalReturnUsage(descriptor, parameterUsage, context.trace)) {
269                context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage, inlinableParameterDescriptor, descriptor));
270            }
271        }
272    
273        @Nullable
274        public static PsiElement getDeclaration(JetExpression expression) {
275            do {
276                expression = PsiTreeUtil.getParentOfType(expression, JetDeclaration.class);
277            } while (expression instanceof JetMultiDeclaration || expression instanceof JetProperty);
278            return expression;
279        }
280    
281    }