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 com.intellij.psi.util.PsiTreeUtil;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.descriptors.*;
024 import org.jetbrains.jet.lang.diagnostics.Errors;
025 import org.jetbrains.jet.lang.psi.*;
026 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027 import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
028 import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
029 import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument;
030 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
031 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
032 import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
033 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
034 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver;
035 import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
036 import org.jetbrains.jet.lang.types.JetType;
037 import org.jetbrains.jet.lang.types.lang.InlineUtil;
038 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
039 import org.jetbrains.jet.lexer.JetToken;
040 import org.jetbrains.jet.lexer.JetTokens;
041
042 import java.util.HashSet;
043 import java.util.Map;
044 import java.util.Set;
045
046 import static org.jetbrains.jet.lang.resolve.InlineDescriptorUtils.allowsNonLocalReturns;
047 import static org.jetbrains.jet.lang.resolve.InlineDescriptorUtils.checkNonLocalReturnUsage;
048
049 public class InlineCallResolverExtension implements CallResolverExtension {
050
051 private final SimpleFunctionDescriptor descriptor;
052
053 private final Set<CallableDescriptor> inlinableParameters = new HashSet<CallableDescriptor>();
054
055 private final boolean isEffectivelyPublicApiFunction;
056
057 public InlineCallResolverExtension(@NotNull SimpleFunctionDescriptor descriptor) {
058 assert descriptor.getInlineStrategy().isInline() : "This extension should be created only for inline functions but not " + descriptor;
059 this.descriptor = descriptor;
060 this.isEffectivelyPublicApiFunction = isEffectivelyPublicApi(descriptor);
061
062 for (ValueParameterDescriptor param : descriptor.getValueParameters()) {
063 if (isInlinableParameter(param)) {
064 inlinableParameters.add(param);
065 }
066 }
067
068 //add extension receiver as inlinable
069 ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
070 if (receiverParameter != null) {
071 if (isInlinableParameter(receiverParameter)) {
072 inlinableParameters.add(receiverParameter);
073 }
074 }
075 }
076
077 @Override
078 public <F extends CallableDescriptor> void run(@NotNull ResolvedCall<F> resolvedCall, @NotNull BasicCallResolutionContext context) {
079 JetExpression expression = context.call.getCalleeExpression();
080 if (expression == null) {
081 return;
082 }
083
084 //checking that only invoke or inlinable extension called on function parameter
085 CallableDescriptor targetDescriptor = resolvedCall.getResultingDescriptor();
086 checkCallWithReceiver(context, targetDescriptor, resolvedCall.getThisObject(), expression);
087 checkCallWithReceiver(context, targetDescriptor, resolvedCall.getReceiverArgument(), expression);
088
089 if (inlinableParameters.contains(targetDescriptor)) {
090 if (!isInsideCall(expression)) {
091 context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor));
092 }
093 }
094
095 for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
096 ResolvedValueArgument value = entry.getValue();
097 ValueParameterDescriptor valueDescriptor = entry.getKey();
098 if (!(value instanceof DefaultValueArgument)) {
099 for (ValueArgument argument : value.getArguments()) {
100 checkValueParameter(context, targetDescriptor, argument, valueDescriptor);
101 }
102 }
103 }
104
105 checkVisibility(targetDescriptor, expression, context);
106 checkRecursion(context, targetDescriptor, expression);
107 }
108
109 private static boolean isInsideCall(JetExpression expression) {
110 JetElement parent = JetPsiUtil.getParentCallIfPresent(expression);
111 if (parent instanceof JetBinaryExpression) {
112 JetToken token = JetPsiUtil.getOperationToken((JetOperationExpression) parent);
113 if (token == JetTokens.EQ || token == JetTokens.ANDAND || token == JetTokens.OROR) {
114 //assignment
115 return false;
116 }
117 }
118
119 return parent != null;
120 }
121
122
123
124 private void checkValueParameter(
125 @NotNull BasicCallResolutionContext context,
126 @NotNull CallableDescriptor targetDescriptor,
127 @NotNull ValueArgument targetArgument,
128 @NotNull ValueParameterDescriptor targetParameterDescriptor
129 ) {
130 JetExpression argumentExpression = targetArgument.getArgumentExpression();
131 if (argumentExpression == null) {
132 return;
133 }
134 CallableDescriptor argumentCallee = getCalleeDescriptor(context, argumentExpression, false);
135
136 if (argumentCallee != null && inlinableParameters.contains(argumentCallee)) {
137 boolean isTargetInlineFunction = targetDescriptor instanceof SimpleFunctionDescriptor &&
138 ((SimpleFunctionDescriptor) targetDescriptor).getInlineStrategy().isInline();
139
140 if (!isTargetInlineFunction || !isInlinableParameter(targetParameterDescriptor)) {
141 context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(argumentExpression, argumentExpression, descriptor));
142 } else {
143 if (allowsNonLocalReturns(argumentCallee) && !allowsNonLocalReturns(targetParameterDescriptor)) {
144 context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(argumentExpression, argumentExpression, argumentCallee, descriptor));
145 } else {
146 checkNonLocalReturn(context, argumentCallee, argumentExpression);
147 }
148 }
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 = getCalleeDescriptor(context, receiverExpression, true);
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 checkLambdaInvokeOrExtensionCall(context, varDescriptor, targetDescriptor, receiverExpression);
179 }
180 }
181
182 @Nullable
183 private static CallableDescriptor getCalleeDescriptor(
184 @NotNull BasicCallResolutionContext context,
185 @NotNull JetExpression expression,
186 boolean unwrapVariableAsFunction
187 ) {
188 if (!(expression instanceof JetSimpleNameExpression || expression instanceof JetThisExpression)) return null;
189
190 ResolvedCall<?> thisCall = CallUtilPackage.getResolvedCall(expression, context.trace.getBindingContext());
191 if (unwrapVariableAsFunction && thisCall instanceof VariableAsFunctionResolvedCall) {
192 return ((VariableAsFunctionResolvedCall) thisCall).getVariableCall().getResultingDescriptor();
193 }
194 return thisCall != null ? thisCall.getResultingDescriptor() : null;
195 }
196
197 private void checkLambdaInvokeOrExtensionCall(
198 @NotNull BasicCallResolutionContext context,
199 @NotNull CallableDescriptor lambdaDescriptor,
200 @NotNull CallableDescriptor callDescriptor,
201 @NotNull JetExpression receiverExpresssion
202 ) {
203 boolean inlinableCall = isInvokeOrInlineExtension(callDescriptor);
204 if (!inlinableCall) {
205 context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(receiverExpresssion, receiverExpresssion, descriptor));
206 } else {
207 checkNonLocalReturn(context, lambdaDescriptor, receiverExpresssion);
208 }
209 }
210
211 public void checkRecursion(
212 @NotNull BasicCallResolutionContext context,
213 @NotNull CallableDescriptor targetDescriptor,
214 @NotNull JetElement expression
215 ) {
216 if (targetDescriptor.getOriginal() == descriptor) {
217 context.trace.report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor));
218 }
219 }
220
221 private static boolean isInlinableParameter(@NotNull CallableDescriptor descriptor) {
222 JetType type = descriptor.getReturnType();
223 return type != null &&
224 KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(type) &&
225 !type.isNullable() &&
226 !InlineUtil.hasNoinlineAnnotation(descriptor);
227 }
228
229 private static boolean isInvokeOrInlineExtension(@NotNull CallableDescriptor descriptor) {
230 if (!(descriptor instanceof SimpleFunctionDescriptor)) {
231 return false;
232 }
233
234 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
235 boolean isInvoke = descriptor.getName().asString().equals("invoke") &&
236 containingDeclaration instanceof ClassDescriptor &&
237 KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(((ClassDescriptor) containingDeclaration).getDefaultType());
238
239 return isInvoke ||
240 //or inline extension
241 ((SimpleFunctionDescriptor) descriptor).getInlineStrategy().isInline();
242 }
243
244 private void checkVisibility(@NotNull CallableDescriptor declarationDescriptor, @NotNull JetElement expression, @NotNull BasicCallResolutionContext context){
245 if (isEffectivelyPublicApiFunction && !isEffectivelyPublicApi(declarationDescriptor) && declarationDescriptor.getVisibility() != Visibilities.LOCAL) {
246 context.trace.report(Errors.INVISIBLE_MEMBER_FROM_INLINE.on(expression, declarationDescriptor, descriptor));
247 }
248 }
249
250 private static boolean isEffectivelyPublicApi(DeclarationDescriptorWithVisibility descriptor) {
251 DeclarationDescriptorWithVisibility parent = descriptor;
252 while (parent != null) {
253 if (!parent.getVisibility().isPublicAPI()) {
254 return false;
255 }
256 parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class);
257 }
258 return true;
259 }
260
261 private void checkNonLocalReturn(
262 @NotNull BasicCallResolutionContext context,
263 @NotNull CallableDescriptor inlinableParameterDescriptor,
264 @NotNull JetExpression parameterUsage
265 ) {
266 if (!allowsNonLocalReturns(inlinableParameterDescriptor)) return;
267
268 if (!checkNonLocalReturnUsage(descriptor, parameterUsage, context.trace)) {
269 context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage, inlinableParameterDescriptor, descriptor));
270 }
271 }
272
273 @Nullable
274 public static PsiElement getDeclaration(JetExpression expression) {
275 do {
276 expression = PsiTreeUtil.getParentOfType(expression, JetDeclaration.class);
277 } while (expression instanceof JetMultiDeclaration || expression instanceof JetProperty);
278 return expression;
279 }
280
281 }