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.descriptors.annotations.AnnotationDescriptor;
026    import org.jetbrains.kotlin.psi.*;
027    import org.jetbrains.kotlin.resolve.BindingContext;
028    import org.jetbrains.kotlin.resolve.BindingTrace;
029    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
030    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
031    import org.jetbrains.kotlin.resolve.calls.model.ArgumentMapping;
032    import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch;
033    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
034    import org.jetbrains.kotlin.resolve.constants.ArrayValue;
035    import org.jetbrains.kotlin.resolve.constants.ConstantValue;
036    import org.jetbrains.kotlin.resolve.constants.EnumValue;
037    
038    import static kotlin.KotlinPackage.firstOrNull;
039    
040    public class InlineUtil {
041        public static boolean isInlineLambdaParameter(@NotNull ParameterDescriptor valueParameterOrReceiver) {
042            return !KotlinBuiltIns.isNoinline(valueParameterOrReceiver) &&
043                   KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(valueParameterOrReceiver.getOriginal().getType());
044        }
045    
046        public static boolean isInline(@Nullable DeclarationDescriptor descriptor) {
047            return descriptor instanceof SimpleFunctionDescriptor && getInlineStrategy(descriptor).isInline();
048        }
049    
050        @NotNull
051        public static InlineStrategy getInlineStrategy(@NotNull DeclarationDescriptor descriptor) {
052            AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(KotlinBuiltIns.FQ_NAMES.inline);
053            if (annotation == null) {
054                return InlineStrategy.NOT_INLINE;
055            }
056            ConstantValue<?> argument = firstOrNull(annotation.getAllValueArguments().values());
057            if (argument == null) {
058                return InlineStrategy.AS_FUNCTION;
059            }
060            assert argument instanceof EnumValue : "Inline annotation parameter should be enum entry but was: " + argument;
061            return InlineStrategy.valueOf(((EnumValue) argument).getValue().getName().asString());
062        }
063    
064        public static boolean hasOnlyLocalReturn(@NotNull ValueParameterDescriptor descriptor) {
065            return descriptor.getAnnotations().findAnnotation(KotlinBuiltIns.FQ_NAMES.crossinline) != null;
066        }
067    
068        public static boolean checkNonLocalReturnUsage(
069                @NotNull DeclarationDescriptor fromFunction,
070                @NotNull JetExpression startExpression,
071                @NotNull BindingTrace trace
072        ) {
073            PsiElement containingFunction = PsiTreeUtil.getParentOfType(startExpression, JetClassOrObject.class, JetDeclarationWithBody.class);
074            if (containingFunction == null) {
075                return false;
076            }
077    
078            DeclarationDescriptor containingFunctionDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction);
079            if (containingFunctionDescriptor == null) {
080                return false;
081            }
082    
083            BindingContext bindingContext = trace.getBindingContext();
084    
085            while (canBeInlineArgument(containingFunction) && fromFunction != containingFunctionDescriptor) {
086                if (!isInlinedArgument((JetFunction) containingFunction, bindingContext, true)) {
087                    return false;
088                }
089    
090                containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true);
091    
092                containingFunction = containingFunctionDescriptor != null
093                                     ? DescriptorToSourceUtils.descriptorToDeclaration(containingFunctionDescriptor)
094                                     : null;
095            }
096    
097            return fromFunction == containingFunctionDescriptor;
098        }
099    
100        public static boolean isInlinedArgument(
101                @NotNull JetFunction argument,
102                @NotNull BindingContext bindingContext,
103                boolean checkNonLocalReturn
104        ) {
105            if (!canBeInlineArgument(argument)) return false;
106    
107            JetExpression call = JetPsiUtil.getParentCallIfPresent(argument);
108            if (call != null) {
109                ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(call, bindingContext);
110                if (resolvedCall != null && isInline(resolvedCall.getResultingDescriptor())) {
111                    ValueArgument valueArgument = CallUtilPackage.getValueArgumentForExpression(resolvedCall.getCall(), argument);
112                    if (valueArgument != null) {
113                        ArgumentMapping mapping = resolvedCall.getArgumentMapping(valueArgument);
114                        if (mapping instanceof ArgumentMatch) {
115                            ValueParameterDescriptor parameter = ((ArgumentMatch) mapping).getValueParameter();
116                            if (isInlineLambdaParameter(parameter)) {
117                                return !checkNonLocalReturn || allowsNonLocalReturns(parameter);
118                            }
119                        }
120                    }
121                }
122            }
123            return false;
124        }
125    
126        public static boolean canBeInlineArgument(@Nullable PsiElement functionalExpression) {
127            return functionalExpression instanceof JetFunctionLiteral || functionalExpression instanceof JetNamedFunction;
128        }
129    
130        @Nullable
131        public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
132            DeclarationDescriptor current = strict ? descriptor.getContainingDeclaration() : descriptor;
133            while (current != null) {
134                if (current instanceof FunctionDescriptor || current instanceof ClassDescriptor) {
135                    return current;
136                }
137                current = current.getContainingDeclaration();
138            }
139    
140            return null;
141        }
142    
143        public static boolean allowsNonLocalReturns(@NotNull CallableDescriptor lambda) {
144            if (lambda instanceof ValueParameterDescriptor) {
145                if (hasOnlyLocalReturn((ValueParameterDescriptor) lambda)) {
146                    //annotated
147                    return false;
148                }
149            }
150            return true;
151        }
152    }