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