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