001    /*
002     * Copyright 2010-2015 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.kotlin.resolve;
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.kotlin.builtins.InlineUtil;
024    import org.jetbrains.kotlin.descriptors.*;
025    import org.jetbrains.kotlin.psi.*;
026    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
027    import org.jetbrains.kotlin.resolve.calls.model.ArgumentMapping;
028    import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch;
029    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
030    
031    public class InlineDescriptorUtils {
032    
033        public static boolean checkNonLocalReturnUsage(@NotNull DeclarationDescriptor fromFunction, @NotNull JetExpression startExpression, @NotNull BindingTrace trace) {
034            PsiElement containingFunction = PsiTreeUtil.getParentOfType(startExpression, JetClassOrObject.class, JetDeclarationWithBody.class);
035            if (containingFunction == null) {
036                return false;
037            }
038    
039            DeclarationDescriptor containingFunctionDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction);
040            if (containingFunctionDescriptor == null) {
041                return false;
042            }
043    
044            BindingContext bindingContext = trace.getBindingContext();
045    
046            while (containingFunction instanceof JetFunctionLiteral && fromFunction != containingFunctionDescriptor) {
047                //JetFunctionLiteralExpression
048                containingFunction = containingFunction.getParent();
049                if (!isInlineLambda((JetFunctionLiteralExpression) containingFunction, bindingContext, true)) {
050                    return false;
051                }
052    
053                containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true);
054    
055                containingFunction = containingFunctionDescriptor != null
056                                     ? DescriptorToSourceUtils.descriptorToDeclaration(containingFunctionDescriptor)
057                                     : null;
058            }
059    
060            return fromFunction == containingFunctionDescriptor;
061        }
062    
063        public static boolean isInlineLambda(
064                @NotNull JetFunctionLiteralExpression lambdaExpression,
065                @NotNull BindingContext bindingContext,
066                boolean checkNonLocalReturn
067        ) {
068            JetExpression call = JetPsiUtil.getParentCallIfPresent(lambdaExpression);
069            if (call != null) {
070                ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(call, bindingContext);
071                CallableDescriptor resultingDescriptor = resolvedCall == null ? null : resolvedCall.getResultingDescriptor();
072                if (resultingDescriptor instanceof SimpleFunctionDescriptor &&
073                    ((SimpleFunctionDescriptor) resultingDescriptor).getInlineStrategy().isInline()) {
074                    ValueArgument argument = CallUtilPackage.getValueArgumentForExpression(resolvedCall.getCall(), lambdaExpression);
075                    if (argument != null) {
076                        ArgumentMapping mapping = resolvedCall.getArgumentMapping(argument);
077                        if (mapping instanceof ArgumentMatch) {
078                            ValueParameterDescriptor parameter = ((ArgumentMatch) mapping).getValueParameter();
079                            if (!InlineUtil.hasNoinlineAnnotation(parameter)) {
080                                return !checkNonLocalReturn || allowsNonLocalReturns(parameter);
081                            }
082                        }
083                    }
084                }
085            }
086            return false;
087        }
088    
089        @Nullable
090        public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
091            DeclarationDescriptor currentDescriptor = strict ? descriptor.getContainingDeclaration() : descriptor;
092            while (currentDescriptor != null) {
093                if (currentDescriptor instanceof FunctionDescriptor || currentDescriptor instanceof ClassDescriptor) {
094                    return currentDescriptor;
095                }
096                currentDescriptor = currentDescriptor.getContainingDeclaration();
097            }
098    
099            return null;
100        }
101    
102        public static boolean allowsNonLocalReturns(@NotNull CallableDescriptor lambdaDescriptor) {
103            if (lambdaDescriptor instanceof ValueParameterDescriptor) {
104                if (InlineUtil.hasOnlyLocalReturn((ValueParameterDescriptor) lambdaDescriptor)) {
105                    //annotated
106                    return false;
107                }
108            }
109            return true;
110        }
111    }