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