001    /*
002     * Copyright 2010-2014 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;
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.psi.*;
025    import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
026    import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMapping;
027    import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMatch;
028    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
029    import org.jetbrains.jet.lang.types.lang.InlineUtil;
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                boolean allowsNonLocalReturns = false;
050                JetExpression call = JetPsiUtil.getParentCallIfPresent((JetFunctionLiteralExpression) containingFunction);
051                if (call != null) {
052                    ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(call, bindingContext);
053                    CallableDescriptor resultingDescriptor = resolvedCall == null ? null : resolvedCall.getResultingDescriptor();
054                    if (resultingDescriptor instanceof SimpleFunctionDescriptor &&
055                        ((SimpleFunctionDescriptor) resultingDescriptor).getInlineStrategy().isInline()) {
056                        ValueArgument argument = CallUtilPackage.getValueArgumentForExpression(
057                                resolvedCall.getCall(), (JetFunctionLiteralExpression) containingFunction);
058                        if (argument != null) {
059                            ArgumentMapping mapping = resolvedCall.getArgumentMapping(argument);
060                            if (mapping instanceof ArgumentMatch) {
061                                allowsNonLocalReturns = allowsNonLocalReturns(((ArgumentMatch) mapping).getValueParameter());
062                            }
063                        }
064                    }
065                }
066                if (!allowsNonLocalReturns) {
067                    return false;
068                }
069    
070                containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true);
071    
072                containingFunction = containingFunctionDescriptor != null
073                                     ? DescriptorToSourceUtils.descriptorToDeclaration(containingFunctionDescriptor)
074                                     : null;
075            }
076    
077            return fromFunction == containingFunctionDescriptor;
078        }
079    
080        @Nullable
081        public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
082            DeclarationDescriptor currentDescriptor = strict ? descriptor.getContainingDeclaration() : descriptor;
083            while (currentDescriptor != null) {
084                if (currentDescriptor instanceof FunctionDescriptor || currentDescriptor instanceof ClassDescriptor) {
085                    return currentDescriptor;
086                }
087                currentDescriptor = currentDescriptor.getContainingDeclaration();
088            }
089    
090            return null;
091        }
092    
093        public static boolean allowsNonLocalReturns(@NotNull CallableDescriptor lambdaDescriptor) {
094            if (lambdaDescriptor instanceof ValueParameterDescriptor) {
095                if (InlineUtil.hasOnlyLocalReturn((ValueParameterDescriptor) lambdaDescriptor)) {
096                    //annotated
097                    return false;
098                }
099            }
100            return true;
101        }
102    }