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