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 }