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.inline;
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.KotlinBuiltIns;
024    import org.jetbrains.kotlin.descriptors.*;
025    import org.jetbrains.kotlin.psi.*;
026    import org.jetbrains.kotlin.resolve.BindingContext;
027    import org.jetbrains.kotlin.resolve.BindingTrace;
028    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
029    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
030    import org.jetbrains.kotlin.resolve.calls.model.ArgumentMapping;
031    import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch;
032    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
033    
034    public class InlineUtil {
035        public static boolean isInlineLambdaParameter(@NotNull ParameterDescriptor valueParameterOrReceiver) {
036            return !(valueParameterOrReceiver instanceof ValueParameterDescriptor
037                     && ((ValueParameterDescriptor) valueParameterOrReceiver).isNoinline()) &&
038                   KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(valueParameterOrReceiver.getOriginal().getType());
039        }
040    
041        public static boolean isInline(@Nullable DeclarationDescriptor descriptor) {
042            return descriptor instanceof SimpleFunctionDescriptor && getInlineStrategy(descriptor).isInline();
043        }
044    
045        @NotNull
046        public static InlineStrategy getInlineStrategy(@NotNull DeclarationDescriptor descriptor) {
047            if (descriptor instanceof FunctionDescriptor &&
048                ((FunctionDescriptor) descriptor).isInline()) {
049                return InlineStrategy.AS_FUNCTION;
050            }
051    
052            return InlineStrategy.NOT_INLINE;
053        }
054    
055        public static boolean checkNonLocalReturnUsage(
056                @NotNull DeclarationDescriptor fromFunction,
057                @NotNull KtExpression startExpression,
058                @NotNull BindingTrace trace
059        ) {
060            PsiElement containingFunction = PsiTreeUtil.getParentOfType(startExpression, KtClassOrObject.class, KtDeclarationWithBody.class);
061            if (containingFunction == null) {
062                return false;
063            }
064    
065            DeclarationDescriptor containingFunctionDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction);
066            if (containingFunctionDescriptor == null) {
067                return false;
068            }
069    
070            BindingContext bindingContext = trace.getBindingContext();
071    
072            while (canBeInlineArgument(containingFunction) && fromFunction != containingFunctionDescriptor) {
073                if (!isInlinedArgument((KtFunction) containingFunction, bindingContext, true)) {
074                    return false;
075                }
076    
077                containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true);
078    
079                containingFunction = containingFunctionDescriptor != null
080                                     ? DescriptorToSourceUtils.descriptorToDeclaration(containingFunctionDescriptor)
081                                     : null;
082            }
083    
084            return fromFunction == containingFunctionDescriptor;
085        }
086    
087        public static boolean isInlinedArgument(
088                @NotNull KtFunction argument,
089                @NotNull BindingContext bindingContext,
090                boolean checkNonLocalReturn
091        ) {
092            if (!canBeInlineArgument(argument)) return false;
093    
094            KtExpression call = KtPsiUtil.getParentCallIfPresent(argument);
095            if (call != null) {
096                ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(call, bindingContext);
097                if (resolvedCall != null && isInline(resolvedCall.getResultingDescriptor())) {
098                    ValueArgument valueArgument = CallUtilKt.getValueArgumentForExpression(resolvedCall.getCall(), argument);
099                    if (valueArgument != null) {
100                        ArgumentMapping mapping = resolvedCall.getArgumentMapping(valueArgument);
101                        if (mapping instanceof ArgumentMatch) {
102                            ValueParameterDescriptor parameter = ((ArgumentMatch) mapping).getValueParameter();
103                            if (isInlineLambdaParameter(parameter)) {
104                                return !checkNonLocalReturn || allowsNonLocalReturns(parameter);
105                            }
106                        }
107                    }
108                }
109            }
110            return false;
111        }
112    
113        public static boolean canBeInlineArgument(@Nullable PsiElement functionalExpression) {
114            return functionalExpression instanceof KtFunctionLiteral || functionalExpression instanceof KtNamedFunction;
115        }
116    
117        @Nullable
118        public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
119            DeclarationDescriptor current = strict ? descriptor.getContainingDeclaration() : descriptor;
120            while (current != null) {
121                if (current instanceof FunctionDescriptor || current instanceof ClassDescriptor) {
122                    return current;
123                }
124                current = current.getContainingDeclaration();
125            }
126    
127            return null;
128        }
129    
130        public static boolean allowsNonLocalReturns(@NotNull CallableDescriptor lambda) {
131            if (lambda instanceof ValueParameterDescriptor) {
132                if (((ValueParameterDescriptor) lambda).isCrossinline()) {
133                    //annotated
134                    return false;
135                }
136            }
137            return true;
138        }
139    
140        public static boolean containsReifiedTypeParameters(@NotNull CallableDescriptor descriptor) {
141            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
142                if (typeParameterDescriptor.isReified()) return true;
143            }
144    
145            return false;
146        }
147    }