Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.resolve.DelegatedPropertyResolver Maven / Gradle / Ivy
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
import org.jetbrains.kotlin.resolve.calls.inference.TypeVariableKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeImpl;
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.validation.OperatorValidator;
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
import org.jetbrains.kotlin.types.expressions.FakeCallResolver;
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.psi.KtPsiFactoryKt.KtPsiFactory;
import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER;
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
public class DelegatedPropertyResolver {
public static final Name PROPERTY_DELEGATED_FUNCTION_NAME = Name.identifier("propertyDelegated");
public static final Name GETTER_NAME = Name.identifier("getValue");
public static final Name SETTER_NAME = Name.identifier("setValue");
public static final Name OLD_GETTER_NAME = Name.identifier("get");
public static final Name OLD_SETTER_NAME = Name.identifier("set");
@NotNull private final ExpressionTypingServices expressionTypingServices;
@NotNull private final FakeCallResolver fakeCallResolver;
@NotNull private final KotlinBuiltIns builtIns;
@NotNull private final SymbolUsageValidator symbolUsageValidator;
public DelegatedPropertyResolver(
@NotNull SymbolUsageValidator symbolUsageValidator,
@NotNull KotlinBuiltIns builtIns,
@NotNull FakeCallResolver fakeCallResolver,
@NotNull ExpressionTypingServices expressionTypingServices
) {
this.symbolUsageValidator = symbolUsageValidator;
this.builtIns = builtIns;
this.fakeCallResolver = fakeCallResolver;
this.expressionTypingServices = expressionTypingServices;
}
@Nullable
public KotlinType getDelegatedPropertyGetMethodReturnType(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull KtExpression delegateExpression,
@NotNull KotlinType delegateType,
@NotNull BindingTrace trace,
@NotNull LexicalScope delegateFunctionsScope
) {
resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, true);
ResolvedCall resolvedCall =
trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
}
public void resolveDelegatedPropertyGetMethod(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull KtExpression delegateExpression,
@NotNull KotlinType delegateType,
@NotNull BindingTrace trace,
@NotNull LexicalScope delegateFunctionsScope
) {
KotlinType returnType = getDelegatedPropertyGetMethodReturnType(
propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope);
KotlinType propertyType = propertyDescriptor.getType();
/* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
if (!(propertyType instanceof DeferredType) && returnType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
.on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
}
}
public void resolveDelegatedPropertySetMethod(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull KtExpression delegateExpression,
@NotNull KotlinType delegateType,
@NotNull BindingTrace trace,
@NotNull LexicalScope delegateFunctionsScope
) {
resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, false);
}
@NotNull
private static KtExpression createExpressionForProperty(@NotNull KtPsiFactory psiFactory) {
return psiFactory.createExpression("null as " + KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString() + "<*>");
}
public void resolveDelegatedPropertyPDMethod(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull KtExpression delegateExpression,
@NotNull KotlinType delegateType,
@NotNull BindingTrace trace,
@NotNull LexicalScope delegateFunctionsScope
) {
TemporaryBindingTrace traceToResolvePDMethod = TemporaryBindingTrace.create(trace, "Trace to resolve propertyDelegated method in delegated property");
ExpressionTypingContext context = ExpressionTypingContext.newContext(
traceToResolvePDMethod, delegateFunctionsScope,
DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
KtPsiFactory psiFactory = KtPsiFactory(delegateExpression);
List arguments = Collections.singletonList(createExpressionForProperty(psiFactory));
ExpressionReceiver receiver = ExpressionReceiver.Companion.create(delegateExpression, delegateType, trace.getBindingContext());
Pair> resolutionResult =
fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, PROPERTY_DELEGATED_FUNCTION_NAME, delegateExpression);
Call call = resolutionResult.getFirst();
OverloadResolutionResults functionResults = resolutionResult.getSecond();
if (!functionResults.isSuccess()) {
String expectedFunction = renderCall(call, traceToResolvePDMethod.getBindingContext());
if (functionResults.isIncomplete() || functionResults.isSingleResult() ||
functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
trace.report(DELEGATE_PD_METHOD_NONE_APPLICABLE.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
} else if (functionResults.isAmbiguity()) {
trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
}
return;
}
trace.record(DELEGATED_PROPERTY_PD_RESOLVED_CALL, propertyDescriptor, functionResults.getResultingCall());
}
/* Resolve getValue() or setValue() methods from delegate */
private void resolveDelegatedPropertyConventionMethod(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull KtExpression delegateExpression,
@NotNull KotlinType delegateType,
@NotNull BindingTrace trace,
@NotNull LexicalScope delegateFunctionsScope,
boolean isGet
) {
PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
OverloadResolutionResults functionResults = getDelegatedPropertyConventionMethod(
propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, isGet, true);
Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
if (!functionResults.isSuccess()) {
String expectedFunction = renderCall(call, trace.getBindingContext());
if (functionResults.isSingleResult() || functionResults.isIncomplete() ||
functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
}
else if (functionResults.isAmbiguity()) {
trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
}
else {
trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
}
return;
}
FunctionDescriptor resultingDescriptor = functionResults.getResultingDescriptor();
ResolvedCall resultingCall = functionResults.getResultingCall();
PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
if (declaration instanceof KtProperty) {
KtProperty property = (KtProperty) declaration;
KtPropertyDelegate delegate = property.getDelegate();
if (delegate != null) {
PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
if (!resultingDescriptor.isOperator()) {
OperatorValidator.Companion.report(byKeyword, resultingDescriptor, trace);
}
symbolUsageValidator.validateCall(resultingCall, resultingCall.getResultingDescriptor(), trace, byKeyword);
}
}
trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall);
}
/* Resolve getValue() or setValue() methods from delegate */
public OverloadResolutionResults getDelegatedPropertyConventionMethod(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull KtExpression delegateExpression,
@NotNull KotlinType delegateType,
@NotNull BindingTrace trace,
@NotNull LexicalScope delegateFunctionsScope,
boolean isGet,
boolean isComplete
) {
PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
KotlinType expectedType = isComplete && isGet && !(propertyDescriptor.getType() instanceof DeferredType)
? propertyDescriptor.getType() : TypeUtils.NO_EXPECTED_TYPE;
ExpressionTypingContext context = ExpressionTypingContext.newContext(
trace, delegateFunctionsScope,
DataFlowInfo.EMPTY, expectedType);
boolean hasThis = propertyDescriptor.getExtensionReceiverParameter() != null || propertyDescriptor.getDispatchReceiverParameter() != null;
List arguments = Lists.newArrayList();
KtPsiFactory psiFactory = KtPsiFactory(delegateExpression);
arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
arguments.add(createExpressionForProperty(psiFactory));
if (!isGet) {
KtReferenceExpression fakeArgument = (KtReferenceExpression) createFakeExpressionOfType(delegateExpression.getProject(), trace,
"fakeArgument" + arguments.size(),
propertyDescriptor.getType());
arguments.add(fakeArgument);
List valueParameters = accessor.getValueParameters();
trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
}
Name functionName = isGet ? GETTER_NAME : SETTER_NAME;
ExpressionReceiver receiver = ExpressionReceiver.Companion.create(delegateExpression, delegateType, trace.getBindingContext());
Pair> resolutionResult =
fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, functionName, delegateExpression);
OverloadResolutionResults resolutionResults = resolutionResult.getSecond();
// Resolve get/set is getValue/setValue was not found. Temporary, for code migration
if (!resolutionResults.isSuccess() && !resolutionResults.isAmbiguity()) {
Name oldFunctionName = isGet ? OLD_GETTER_NAME : OLD_SETTER_NAME;
Pair> additionalResolutionResult =
fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, oldFunctionName, delegateExpression);
if (additionalResolutionResult.getSecond().isSuccess()) {
FunctionDescriptor resultingDescriptor = additionalResolutionResult.getSecond().getResultingDescriptor();
PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
if (declaration instanceof KtProperty) {
KtProperty property = (KtProperty) declaration;
KtPropertyDelegate delegate = property.getDelegate();
if (delegate != null) {
PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
trace.report(DELEGATE_RESOLVED_TO_DEPRECATED_CONVENTION.on(
byKeyword, resultingDescriptor, delegateType, functionName.asString()));
}
}
trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, additionalResolutionResult.getFirst());
return additionalResolutionResult.getSecond();
}
}
trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, resolutionResult.getFirst());
return resolutionResults;
}
private static String renderCall(@NotNull Call call, @NotNull BindingContext context) {
KtExpression calleeExpression = call.getCalleeExpression();
assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
StringBuilder builder = new StringBuilder(calleeExpression.getText());
builder.append("(");
List argumentTypes = Lists.newArrayList();
for (ValueArgument argument : call.getValueArguments()) {
argumentTypes.add(context.getType(argument.getArgumentExpression()));
}
builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
builder.append(")");
return builder.toString();
}
@NotNull
public KotlinType resolveDelegateExpression(
@NotNull KtExpression delegateExpression,
@NotNull KtProperty property,
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull LexicalScope scopeForDelegate,
@NotNull BindingTrace trace,
@NotNull DataFlowInfo dataFlowInfo
) {
TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
KtExpression calleeExpression = CallUtilKt.getCalleeExpressionIfAny(delegateExpression);
ConstraintSystemCompleter completer = createConstraintSystemCompleter(
property, propertyDescriptor, delegateExpression, scopeForDelegate, trace);
if (calleeExpression != null) {
traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
}
KotlinType delegateType = expressionTypingServices.safeGetType(scopeForDelegate, delegateExpression, NO_EXPECTED_TYPE,
dataFlowInfo, traceToResolveDelegatedProperty);
traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
@Override
public boolean accept(@Nullable WritableSlice, ?> slice, Object key) {
return slice != CONSTRAINT_SYSTEM_COMPLETER;
}
}, true);
return delegateType;
}
@NotNull
private ConstraintSystemCompleter createConstraintSystemCompleter(
@NotNull KtProperty property,
@NotNull final PropertyDescriptor propertyDescriptor,
@NotNull final KtExpression delegateExpression,
@NotNull LexicalScope scopeForDelegate,
@NotNull final BindingTrace trace
) {
final LexicalScope delegateFunctionsScope = JetScopeUtils.makeScopeForDelegateConventionFunctions(scopeForDelegate, propertyDescriptor);
final KotlinType expectedType = property.getTypeReference() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
return new ConstraintSystemCompleter() {
@Override
public void completeConstraintSystem(
@NotNull ConstraintSystem.Builder constraintSystem, @NotNull ResolvedCall> resolvedCall
) {
KotlinType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
if (returnType == null) return;
TypeSubstitutor typeVariableSubstitutor =
constraintSystem.getTypeVariableSubstitutors().get(TypeVariableKt.toHandle(resolvedCall.getCall()));
assert typeVariableSubstitutor != null : "No substitutor in the system for call: " + resolvedCall.getCall();
TemporaryBindingTrace traceToResolveConventionMethods =
TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
OverloadResolutionResults
getMethodResults = getDelegatedPropertyConventionMethod(
propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, delegateFunctionsScope,
true, false
);
if (conventionMethodFound(getMethodResults)) {
FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
KotlinType returnTypeOfGetMethod = descriptor.getReturnType();
if (returnTypeOfGetMethod != null && !TypeUtils.noExpectedType(expectedType)) {
KotlinType returnTypeInSystem = typeVariableSubstitutor.substitute(returnTypeOfGetMethod, Variance.INVARIANT);
if (returnTypeInSystem != null) {
constraintSystem.addSubtypeConstraint(returnTypeInSystem, expectedType, FROM_COMPLETER.position());
}
}
addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, descriptor);
}
if (!propertyDescriptor.isVar()) return;
// For the case: 'val v by d' (no declared type).
// When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
// 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).
if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
OverloadResolutionResults
setMethodResults = getDelegatedPropertyConventionMethod(
propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, delegateFunctionsScope,
false, false
);
if (conventionMethodFound(setMethodResults)) {
FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
List valueParameters = descriptor.getValueParameters();
if (valueParameters.size() == 3) {
ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
if (!noExpectedType(expectedType)) {
constraintSystem.addSubtypeConstraint(
expectedType,
typeVariableSubstitutor.substitute(valueParameterForThis.getType(), Variance.INVARIANT),
FROM_COMPLETER.position()
);
}
addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, descriptor);
}
}
}
private boolean conventionMethodFound(@NotNull OverloadResolutionResults results) {
return results.isSuccess() ||
(results.isSingleResult() &&
results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
}
private void addConstraintForThisValue(
ConstraintSystem.Builder constraintSystem,
TypeSubstitutor typeVariableSubstitutor,
FunctionDescriptor resultingDescriptor
) {
ReceiverParameterDescriptor extensionReceiver = propertyDescriptor.getExtensionReceiverParameter();
ReceiverParameterDescriptor dispatchReceiver = propertyDescriptor.getDispatchReceiverParameter();
KotlinType typeOfThis =
extensionReceiver != null ? extensionReceiver.getType() :
dispatchReceiver != null ? dispatchReceiver.getType() :
builtIns.getNullableNothingType();
List valueParameters = resultingDescriptor.getValueParameters();
if (valueParameters.isEmpty()) return;
ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
constraintSystem.addSubtypeConstraint(
typeOfThis,
typeVariableSubstitutor.substitute(valueParameterForThis.getType(), Variance.INVARIANT),
FROM_COMPLETER.position()
);
}
};
}
}