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 JetDotQualifiedExpression ||
109                    parent instanceof JetCallExpression ||
110                    parent instanceof JetArrayAccessExpression ||
111                    parent instanceof JetMultiDeclaration) {
112    
113                    if (parent instanceof JetPrefixExpression) {
114                        if (JetPsiUtil.isLabeledExpression((JetPrefixExpression) parent)) {
115                            parent = parent.getParent();
116                            continue;
117                        }
118                    }
119                    else if (parent instanceof JetBinaryExpression) {
120                        JetToken token = JetPsiUtil.getOperationToken((JetOperationExpression) parent);
121                        if (token == JetTokens.EQ || token == JetTokens.ANDAND || token == JetTokens.OROR) {
122                            //assignment
123                            return false;
124                        }
125                    }
126    
127                    //check that it's in inlineable call would be in resolve call of parent
128                    return true;
129                }
130                else if (parent instanceof JetParenthesizedExpression || parent instanceof JetBinaryExpressionWithTypeRHS) {
131                    parent = parent.getParent();
132                }
133                else {
134                    return false;
135                }
136            }
137            return false;
138        }
139    
140        private void checkValueParameter(BasicCallResolutionContext context, CallableDescriptor targetDescriptor, ValueArgument argument, boolean isVararg) {
141            JetExpression jetExpression = argument.getArgumentExpression();
142            if (jetExpression == null) {
143                return;
144            }
145            CallableDescriptor varDescriptor = getDescriptor(context, jetExpression);
146    
147            if (varDescriptor != null && inlinableParameters.contains(varDescriptor)) {
148                checkFunctionCall(context, targetDescriptor, jetExpression, isVararg);
149            }
150        }
151    
152        private void checkCallWithReceiver(
153                @NotNull BasicCallResolutionContext context,
154                @NotNull CallableDescriptor targetDescriptor,
155                @NotNull ReceiverValue receiver,
156                @Nullable JetExpression expression
157        ) {
158            if (!receiver.exists()) return;
159    
160            CallableDescriptor varDescriptor = null;
161            JetExpression receiverExpression = null;
162            if (receiver instanceof ExpressionReceiver) {
163                receiverExpression = ((ExpressionReceiver) receiver).getExpression();
164                varDescriptor = getDescriptor(context, receiverExpression);
165            }
166            else if (receiver instanceof ExtensionReceiver) {
167                ExtensionReceiver extensionReceiver = (ExtensionReceiver) receiver;
168                CallableDescriptor extension = extensionReceiver.getDeclarationDescriptor();
169    
170                varDescriptor = extension.getReceiverParameter();
171                assert varDescriptor != null : "Extension should have receiverParameterDescriptor: " + extension;
172    
173                receiverExpression = expression;
174            }
175    
176            if (inlinableParameters.contains(varDescriptor)) {
177                //check that it's invoke or inlinable extension
178                checkFunctionCall(context, targetDescriptor, receiverExpression, false);
179            }
180        }
181    
182        @Nullable
183        private static CallableDescriptor getDescriptor(
184                @NotNull BasicCallResolutionContext context,
185                @NotNull JetExpression expression
186        ) {
187            ResolvedCall<?> thisCall = context.trace.get(BindingContext.RESOLVED_CALL, expression);
188            return thisCall != null ? thisCall.getResultingDescriptor() : null;
189        }
190    
191        private void checkFunctionCall(
192                BasicCallResolutionContext context,
193                CallableDescriptor targetDescriptor,
194                JetExpression receiverExpresssion,
195                boolean isVararg
196        ) {
197            boolean inlinableCall = isInvokeOrInlineExtension(targetDescriptor);
198            if (!inlinableCall || isVararg) {
199                context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(receiverExpresssion, receiverExpresssion, descriptor));
200            }
201        }
202    
203        public void checkRecursion(
204                @NotNull CallableDescriptor targetDescriptor,
205                @NotNull JetElement expression,
206                @NotNull BasicCallResolutionContext context
207        ) {
208            if (targetDescriptor.getOriginal() == descriptor) {
209                context.trace.report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor));
210            }
211        }
212    
213        private static boolean isInlinableParameter(@NotNull CallableDescriptor descriptor) {
214            JetType type = descriptor.getReturnType();
215            return type != null &&
216                   KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(type) &&
217                   !type.isNullable() &&
218                   !InlineUtil.hasNoinlineAnnotation(descriptor);
219        }
220    
221        private static boolean isInvokeOrInlineExtension(@NotNull CallableDescriptor descriptor) {
222            if (!(descriptor instanceof SimpleFunctionDescriptor)) {
223                return false;
224            }
225    
226            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
227            boolean isInvoke = descriptor.getName().asString().equals("invoke") &&
228                               containingDeclaration instanceof ClassDescriptor &&
229                               KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(((ClassDescriptor) containingDeclaration).getDefaultType());
230    
231            return isInvoke ||
232                   //or inline extension
233                   ((SimpleFunctionDescriptor) descriptor).getInlineStrategy().isInline();
234        }
235    
236        private void checkVisibility(@NotNull CallableDescriptor declarationDescriptor, @NotNull JetElement expression, @NotNull BasicCallResolutionContext context){
237            if (isEffectivelyPublicApiFunction && !isEffectivelyPublicApi(declarationDescriptor) && declarationDescriptor.getVisibility() != Visibilities.LOCAL) {
238                context.trace.report(Errors.INVISIBLE_MEMBER_FROM_INLINE.on(expression, declarationDescriptor, descriptor));
239            }
240        }
241    
242        private static boolean isEffectivelyPublicApi(DeclarationDescriptorWithVisibility descriptor) {
243            DeclarationDescriptorWithVisibility parent = descriptor;
244            while (parent != null) {
245                if (!parent.getVisibility().isPublicAPI()) {
246                    return false;
247                }
248                parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class);
249            }
250            return true;
251        }
252    }