001 /*
002 * Copyright 2010-2015 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.kotlin.resolve;
018
019 import com.google.common.collect.Lists;
020 import com.intellij.psi.PsiElement;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
024 import org.jetbrains.kotlin.descriptors.*;
025 import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
026 import org.jetbrains.kotlin.name.Name;
027 import org.jetbrains.kotlin.psi.*;
028 import org.jetbrains.kotlin.resolve.calls.CallResolver;
029 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
030 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
031 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
032 import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
033 import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
034 import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
035 import org.jetbrains.kotlin.resolve.scopes.JetScope;
036 import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
037 import org.jetbrains.kotlin.types.DeferredType;
038 import org.jetbrains.kotlin.types.JetType;
039 import org.jetbrains.kotlin.types.TypeUtils;
040 import org.jetbrains.kotlin.types.checker.JetTypeChecker;
041 import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
042 import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
043 import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
044
045 import javax.inject.Inject;
046 import java.util.List;
047
048 import static org.jetbrains.kotlin.diagnostics.Errors.*;
049 import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
050 import static org.jetbrains.kotlin.resolve.BindingContext.*;
051 import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getCalleeExpressionIfAny;
052 import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER;
053 import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
054 import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
055 import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
056
057 public class DelegatedPropertyResolver {
058
059 public static final Name PROPERTY_DELEGATED_FUNCTION_NAME = Name.identifier("propertyDelegated");
060
061 private ExpressionTypingServices expressionTypingServices;
062 private CallResolver callResolver;
063 private KotlinBuiltIns builtIns;
064 private AdditionalCheckerProvider additionalCheckerProvider;
065
066 @Inject
067 public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
068 this.expressionTypingServices = expressionTypingServices;
069 }
070
071 @Inject
072 public void setCallResolver(@NotNull CallResolver callResolver) {
073 this.callResolver = callResolver;
074 }
075
076 @Inject
077 public void setBuiltIns(@NotNull KotlinBuiltIns builtIns) {
078 this.builtIns = builtIns;
079 }
080
081 @Inject
082 public void setAdditionalCheckerProvider(AdditionalCheckerProvider additionalCheckerProvider) {
083 this.additionalCheckerProvider = additionalCheckerProvider;
084 }
085
086 @Nullable
087 public JetType getDelegatedPropertyGetMethodReturnType(
088 @NotNull PropertyDescriptor propertyDescriptor,
089 @NotNull JetExpression delegateExpression,
090 @NotNull JetType delegateType,
091 @NotNull BindingTrace trace,
092 @NotNull JetScope scope
093 ) {
094 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, true);
095 ResolvedCall<FunctionDescriptor> resolvedCall =
096 trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
097 return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
098 }
099
100 public void resolveDelegatedPropertyGetMethod(
101 @NotNull PropertyDescriptor propertyDescriptor,
102 @NotNull JetExpression delegateExpression,
103 @NotNull JetType delegateType,
104 @NotNull BindingTrace trace,
105 @NotNull JetScope scope
106 ) {
107 JetType returnType = getDelegatedPropertyGetMethodReturnType(
108 propertyDescriptor, delegateExpression, delegateType, trace, scope);
109 JetType propertyType = propertyDescriptor.getType();
110
111 /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
112 if (!(propertyType instanceof DeferredType) && returnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
113 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
114 assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
115 trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
116 .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
117 }
118 }
119
120 public void resolveDelegatedPropertySetMethod(
121 @NotNull PropertyDescriptor propertyDescriptor,
122 @NotNull JetExpression delegateExpression,
123 @NotNull JetType delegateType,
124 @NotNull BindingTrace trace,
125 @NotNull JetScope scope
126 ) {
127 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, false);
128 }
129
130 @NotNull
131 private JetExpression createExpressionForPropertyMetadata(
132 @NotNull JetPsiFactory psiFactory,
133 @NotNull PropertyDescriptor propertyDescriptor
134 ) {
135 return psiFactory.createExpression(builtIns.getPropertyMetadataImpl().getName().asString() +
136 "(\"" +
137 propertyDescriptor.getName().asString() +
138 "\"): " +
139 builtIns.getPropertyMetadata().getName().asString());
140 }
141
142 public void resolveDelegatedPropertyPDMethod(
143 @NotNull PropertyDescriptor propertyDescriptor,
144 @NotNull JetExpression delegateExpression,
145 @NotNull JetType delegateType,
146 @NotNull BindingTrace trace,
147 @NotNull JetScope scope
148 ) {
149 TemporaryBindingTrace traceToResolvePDMethod = TemporaryBindingTrace.create(trace, "Trace to resolve propertyDelegated method in delegated property");
150 ExpressionTypingContext context = ExpressionTypingContext.newContext(
151 additionalCheckerProvider, traceToResolvePDMethod, scope,
152 DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
153
154 List<JetExpression> arguments = Lists.newArrayList();
155 JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
156 arguments.add(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
157 JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(PROPERTY_DELEGATED_FUNCTION_NAME.asString());
158 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
159 Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
160
161 OverloadResolutionResults<FunctionDescriptor> functionResults =
162 callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, PROPERTY_DELEGATED_FUNCTION_NAME);
163
164 if (!functionResults.isSuccess()) {
165 String expectedFunction = renderCall(call, traceToResolvePDMethod.getBindingContext());
166 if (functionResults.isIncomplete() || functionResults.isSingleResult() ||
167 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
168 trace.report(DELEGATE_PD_METHOD_NONE_APPLICABLE.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
169 } else if (functionResults.isAmbiguity()) {
170 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
171 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
172 }
173 return;
174 }
175
176 trace.record(DELEGATED_PROPERTY_PD_RESOLVED_CALL, propertyDescriptor, functionResults.getResultingCall());
177 }
178
179 /* Resolve get() or set() methods from delegate */
180 private void resolveDelegatedPropertyConventionMethod(
181 @NotNull PropertyDescriptor propertyDescriptor,
182 @NotNull JetExpression delegateExpression,
183 @NotNull JetType delegateType,
184 @NotNull BindingTrace trace,
185 @NotNull JetScope scope,
186 boolean isGet
187 ) {
188 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
189 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
190
191 if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
192
193 OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
194 propertyDescriptor, delegateExpression, delegateType, trace, scope, isGet, true);
195 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
196 assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
197
198 if (!functionResults.isSuccess()) {
199 String expectedFunction = renderCall(call, trace.getBindingContext());
200 if (functionResults.isIncomplete()) {
201 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
202 }
203 else if (functionResults.isSingleResult() ||
204 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
205 trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
206 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
207 }
208 else if (functionResults.isAmbiguity()) {
209 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
210 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
211 }
212 else {
213 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
214 }
215 return;
216 }
217
218 ResolvedCall<FunctionDescriptor> resultingCall = functionResults.getResultingCall();
219 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
220 if (declaration instanceof JetProperty) {
221 JetProperty property = (JetProperty) declaration;
222 JetPropertyDelegate delegate = property.getDelegate();
223 if (delegate != null) {
224 PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
225 additionalCheckerProvider.getSymbolUsageValidator().validateCall(resultingCall.getResultingDescriptor(), trace, byKeyword);
226 }
227 }
228 trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall);
229 }
230
231 /* Resolve get() or set() methods from delegate */
232 public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
233 @NotNull PropertyDescriptor propertyDescriptor,
234 @NotNull JetExpression delegateExpression,
235 @NotNull JetType delegateType,
236 @NotNull BindingTrace trace,
237 @NotNull JetScope scope,
238 boolean isGet,
239 boolean isComplete
240 ) {
241 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
242 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
243
244 JetType expectedType = isComplete && isGet && !(propertyDescriptor.getType() instanceof DeferredType)
245 ? propertyDescriptor.getType() : TypeUtils.NO_EXPECTED_TYPE;
246
247 ExpressionTypingContext context = ExpressionTypingContext.newContext(
248 additionalCheckerProvider, trace, scope,
249 DataFlowInfo.EMPTY, expectedType);
250
251 boolean hasThis = propertyDescriptor.getExtensionReceiverParameter() != null || propertyDescriptor.getDispatchReceiverParameter() != null;
252
253 List<JetExpression> arguments = Lists.newArrayList();
254 JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
255 arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
256
257 arguments.add(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
258
259 if (!isGet) {
260 JetReferenceExpression fakeArgument = (JetReferenceExpression) createFakeExpressionOfType(delegateExpression.getProject(), trace,
261 "fakeArgument" + arguments.size(),
262 propertyDescriptor.getType());
263 arguments.add(fakeArgument);
264 List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
265 trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
266 }
267
268 Name functionName = Name.identifier(isGet ? "get" : "set");
269 JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(functionName.asString());
270
271 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
272 Call call = CallMaker.makeCallWithExpressions(delegateExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
273 trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, call);
274
275 return callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, functionName);
276 }
277
278 private String renderCall(@NotNull Call call, @NotNull BindingContext context) {
279 JetExpression calleeExpression = call.getCalleeExpression();
280 assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
281 StringBuilder builder = new StringBuilder(calleeExpression.getText());
282 builder.append("(");
283 List<JetType> argumentTypes = Lists.newArrayList();
284 for (ValueArgument argument : call.getValueArguments()) {
285 argumentTypes.add(context.getType(argument.getArgumentExpression()));
286
287 }
288 builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
289 builder.append(")");
290 return builder.toString();
291 }
292
293 @Nullable
294 public JetType resolveDelegateExpression(
295 @NotNull JetExpression delegateExpression,
296 @NotNull JetProperty jetProperty,
297 @NotNull PropertyDescriptor propertyDescriptor,
298 @NotNull JetScope propertyDeclarationInnerScope,
299 @NotNull JetScope accessorScope,
300 @NotNull BindingTrace trace,
301 @NotNull DataFlowInfo dataFlowInfo
302 ) {
303 TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
304 JetExpression calleeExpression = getCalleeExpressionIfAny(delegateExpression);
305 ConstraintSystemCompleter completer = createConstraintSystemCompleter(
306 jetProperty, propertyDescriptor, delegateExpression, accessorScope, trace);
307 if (calleeExpression != null) {
308 traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
309 }
310 JetType delegateType = expressionTypingServices.safeGetType(propertyDeclarationInnerScope, delegateExpression, NO_EXPECTED_TYPE,
311 dataFlowInfo, traceToResolveDelegatedProperty);
312 traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
313 @Override
314 public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
315 return slice != CONSTRAINT_SYSTEM_COMPLETER;
316 }
317 }, true);
318 return delegateType;
319 }
320
321 @NotNull
322 private ConstraintSystemCompleter createConstraintSystemCompleter(
323 @NotNull JetProperty property,
324 @NotNull final PropertyDescriptor propertyDescriptor,
325 @NotNull final JetExpression delegateExpression,
326 @NotNull final JetScope accessorScope,
327 @NotNull final BindingTrace trace
328 ) {
329 final JetType expectedType = property.getTypeReference() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
330 return new ConstraintSystemCompleter() {
331 @Override
332 public void completeConstraintSystem(
333 @NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
334 ) {
335 JetType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
336 if (returnType == null) return;
337
338 TemporaryBindingTrace traceToResolveConventionMethods =
339 TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
340 OverloadResolutionResults<FunctionDescriptor>
341 getMethodResults = getDelegatedPropertyConventionMethod(
342 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope,
343 true, false
344 );
345
346 if (conventionMethodFound(getMethodResults)) {
347 FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
348 JetType returnTypeOfGetMethod = descriptor.getReturnType();
349 if (returnTypeOfGetMethod != null) {
350 constraintSystem.addSupertypeConstraint(expectedType, returnTypeOfGetMethod, FROM_COMPLETER.position());
351 }
352 addConstraintForThisValue(constraintSystem, descriptor);
353 }
354 if (!propertyDescriptor.isVar()) return;
355
356 // For the case: 'val v by d' (no declared type).
357 // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
358 // But if the type isn't known yet, the constraint shouldn't be added (we try to infer the type of 'v' here as well).
359 if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
360
361 OverloadResolutionResults<FunctionDescriptor>
362 setMethodResults = getDelegatedPropertyConventionMethod(
363 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope,
364 false, false
365 );
366
367 if (conventionMethodFound(setMethodResults)) {
368 FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
369 List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
370 if (valueParameters.size() == 3) {
371 ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
372
373 if (!noExpectedType(expectedType)) {
374 constraintSystem.addSubtypeConstraint(
375 expectedType, valueParameterForThis.getType(), FROM_COMPLETER.position());
376 }
377 addConstraintForThisValue(constraintSystem, descriptor);
378 }
379 }
380 }
381
382 private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
383 return results.isSuccess() ||
384 (results.isSingleResult() &&
385 results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
386 }
387
388 private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
389 ReceiverParameterDescriptor extensionReceiver = propertyDescriptor.getExtensionReceiverParameter();
390 ReceiverParameterDescriptor dispatchReceiver = propertyDescriptor.getDispatchReceiverParameter();
391 JetType typeOfThis =
392 extensionReceiver != null ? extensionReceiver.getType() :
393 dispatchReceiver != null ? dispatchReceiver.getType() :
394 builtIns.getNullableNothingType();
395
396 List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
397 if (valueParameters.isEmpty()) return;
398 ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
399
400 constraintSystem.addSubtypeConstraint(typeOfThis, valueParameterForThis.getType(), FROM_COMPLETER.position());
401 }
402 };
403 }
404 }