refactoring
extracted checkReceiverTypeError method to avoid 'checkOnlyReceiverTypeError' boolean flag
This commit is contained in:
@@ -152,29 +152,15 @@ public class CallResolverUtil {
|
|||||||
return CallKey.create(context.call.getCallType(), (JetExpression) callElement);
|
return CallKey.create(context.call.getCallType(), (JetExpression) callElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean checkArgumentCannotBeReceiver(
|
@NotNull
|
||||||
@NotNull JetType receiverArgumentType,
|
public static JetType getErasedReceiverType(
|
||||||
|
@NotNull ReceiverParameterDescriptor receiverParameterDescriptor,
|
||||||
@NotNull CallableDescriptor descriptor
|
@NotNull CallableDescriptor descriptor
|
||||||
) {
|
) {
|
||||||
JetType effectiveReceiverArgumentType = TypeUtils.makeNotNullable(receiverArgumentType);
|
JetType receiverType = receiverParameterDescriptor.getType();
|
||||||
|
|
||||||
JetType erasedReceiverType = getErasedReceiverType(descriptor);
|
|
||||||
if (erasedReceiverType == null) return true;
|
|
||||||
|
|
||||||
return !JetTypeChecker.INSTANCE.isSubtypeOf(effectiveReceiverArgumentType, erasedReceiverType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static JetType getErasedReceiverType(@NotNull CallableDescriptor descriptor) {
|
|
||||||
ReceiverParameterDescriptor receiverDescriptor = descriptor.getReceiverParameter();
|
|
||||||
ReceiverParameterDescriptor expectedThisObjectDescriptor = descriptor.getExpectedThisObject();
|
|
||||||
JetType receiverType = receiverDescriptor != null ? receiverDescriptor.getType() :
|
|
||||||
expectedThisObjectDescriptor != null ? expectedThisObjectDescriptor.getType() : null;
|
|
||||||
if (receiverType == null) return null;
|
|
||||||
|
|
||||||
for (TypeParameterDescriptor typeParameter : descriptor.getTypeParameters()) {
|
for (TypeParameterDescriptor typeParameter : descriptor.getTypeParameters()) {
|
||||||
if (typeParameter.getTypeConstructor().equals(receiverType.getConstructor())) {
|
if (typeParameter.getTypeConstructor().equals(receiverType.getConstructor())) {
|
||||||
return typeParameter.getUpperBoundsAsType();
|
receiverType = typeParameter.getUpperBoundsAsType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<TypeProjection> fakeTypeArguments = Lists.newArrayList();
|
List<TypeProjection> fakeTypeArguments = Lists.newArrayList();
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import org.jetbrains.jet.lang.resolve.calls.util.ExpressionAsFunctionDescriptor;
|
|||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
|
||||||
import org.jetbrains.jet.lang.types.*;
|
import org.jetbrains.jet.lang.types.*;
|
||||||
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||||
import org.jetbrains.jet.lang.types.expressions.DataFlowUtils;
|
import org.jetbrains.jet.lang.types.expressions.DataFlowUtils;
|
||||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
|
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
@@ -83,7 +84,7 @@ public class CandidateResolver {
|
|||||||
ResolvedCallImpl<D> candidateCall = context.candidateCall;
|
ResolvedCallImpl<D> candidateCall = context.candidateCall;
|
||||||
D candidate = candidateCall.getCandidateDescriptor();
|
D candidate = candidateCall.getCandidateDescriptor();
|
||||||
|
|
||||||
candidateCall.addStatus(checkReceiver(context, context.trace, /*checkOnlyReceiverTypeError=*/true));
|
candidateCall.addStatus(checkReceiverTypeError(context.candidateCall));
|
||||||
|
|
||||||
if (ErrorUtils.isError(candidate)) {
|
if (ErrorUtils.isError(candidate)) {
|
||||||
candidateCall.addStatus(SUCCESS);
|
candidateCall.addStatus(SUCCESS);
|
||||||
@@ -578,6 +579,8 @@ public class CandidateResolver {
|
|||||||
ResolutionStatus resultStatus = SUCCESS;
|
ResolutionStatus resultStatus = SUCCESS;
|
||||||
ResolvedCall<D> candidateCall = context.candidateCall;
|
ResolvedCall<D> candidateCall = context.candidateCall;
|
||||||
|
|
||||||
|
resultStatus = resultStatus.combine(checkReceiverTypeError(candidateCall));
|
||||||
|
|
||||||
// Comment about a very special case.
|
// Comment about a very special case.
|
||||||
// Call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' should be checked two times for safe call (in 'checkReceiver'), because
|
// Call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' should be checked two times for safe call (in 'checkReceiver'), because
|
||||||
// both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'.
|
// both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'.
|
||||||
@@ -586,14 +589,14 @@ public class CandidateResolver {
|
|||||||
resultStatus = resultStatus.combine(checkReceiver(
|
resultStatus = resultStatus.combine(checkReceiver(
|
||||||
context, candidateCall, trace,
|
context, candidateCall, trace,
|
||||||
candidateCall.getResultingDescriptor().getReceiverParameter(),
|
candidateCall.getResultingDescriptor().getReceiverParameter(),
|
||||||
candidateCall.getReceiverArgument(), candidateCall.getExplicitReceiverKind().isReceiver(), false, checkOnlyReceiverTypeError));
|
candidateCall.getReceiverArgument(), candidateCall.getExplicitReceiverKind().isReceiver(), false));
|
||||||
|
|
||||||
resultStatus = resultStatus.combine(checkReceiver(
|
resultStatus = resultStatus.combine(checkReceiver(
|
||||||
context, candidateCall, trace,
|
context, candidateCall, trace,
|
||||||
candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(),
|
candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(),
|
||||||
candidateCall.getExplicitReceiverKind().isThisObject(),
|
candidateCall.getExplicitReceiverKind().isThisObject(),
|
||||||
// for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error
|
// for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error
|
||||||
context.call instanceof CallForImplicitInvoke, checkOnlyReceiverTypeError));
|
context.call instanceof CallForImplicitInvoke));
|
||||||
return resultStatus;
|
return resultStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +676,37 @@ public class CandidateResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <D extends CallableDescriptor> ResolutionStatus checkReceiverTypeError(
|
||||||
|
@NotNull ResolvedCall<D> candidateCall
|
||||||
|
) {
|
||||||
|
D candidateDescriptor = candidateCall.getCandidateDescriptor();
|
||||||
|
if (candidateDescriptor instanceof ExpressionAsFunctionDescriptor) return SUCCESS;
|
||||||
|
|
||||||
|
ReceiverParameterDescriptor receiverDescriptor = candidateDescriptor.getReceiverParameter();
|
||||||
|
ReceiverParameterDescriptor expectedThisObjectDescriptor = candidateDescriptor.getExpectedThisObject();
|
||||||
|
ReceiverParameterDescriptor receiverParameterDescriptor;
|
||||||
|
JetType receiverArgumentType;
|
||||||
|
if (receiverDescriptor != null && candidateCall.getReceiverArgument().exists()) {
|
||||||
|
receiverParameterDescriptor = receiverDescriptor;
|
||||||
|
receiverArgumentType = candidateCall.getReceiverArgument().getType();
|
||||||
|
}
|
||||||
|
else if (expectedThisObjectDescriptor != null && candidateCall.getThisObject().exists()) {
|
||||||
|
receiverParameterDescriptor = expectedThisObjectDescriptor;
|
||||||
|
receiverArgumentType = candidateCall.getThisObject().getType();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
JetType effectiveReceiverArgumentType = TypeUtils.makeNotNullable(receiverArgumentType);
|
||||||
|
JetType erasedReceiverType = CallResolverUtil.getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor);
|
||||||
|
|
||||||
|
if (!JetTypeChecker.INSTANCE.isSubtypeOf(effectiveReceiverArgumentType, erasedReceiverType)) {
|
||||||
|
return RECEIVER_TYPE_ERROR;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
private static <D extends CallableDescriptor> ResolutionStatus checkReceiver(
|
private static <D extends CallableDescriptor> ResolutionStatus checkReceiver(
|
||||||
@NotNull CallCandidateResolutionContext<D> context,
|
@NotNull CallCandidateResolutionContext<D> context,
|
||||||
@NotNull ResolvedCall<D> candidateCall,
|
@NotNull ResolvedCall<D> candidateCall,
|
||||||
@@ -680,28 +714,18 @@ public class CandidateResolver {
|
|||||||
@Nullable ReceiverParameterDescriptor receiverParameter,
|
@Nullable ReceiverParameterDescriptor receiverParameter,
|
||||||
@NotNull ReceiverValue receiverArgument,
|
@NotNull ReceiverValue receiverArgument,
|
||||||
boolean isExplicitReceiver,
|
boolean isExplicitReceiver,
|
||||||
boolean implicitInvokeCheck,
|
boolean implicitInvokeCheck
|
||||||
boolean checkOnlyReceiverTypeError
|
|
||||||
) {
|
) {
|
||||||
if (receiverParameter == null || !receiverArgument.exists()) return SUCCESS;
|
if (receiverParameter == null || !receiverArgument.exists()) return SUCCESS;
|
||||||
|
|
||||||
JetType receiverArgumentType = receiverArgument.getType();
|
JetType receiverArgumentType = receiverArgument.getType();
|
||||||
JetType effectiveReceiverArgumentType = TypeUtils.makeNotNullable(receiverArgumentType);
|
JetType effectiveReceiverArgumentType = TypeUtils.makeNotNullable(receiverArgumentType);
|
||||||
D candidateDescriptor = candidateCall.getCandidateDescriptor();
|
D candidateDescriptor = candidateCall.getCandidateDescriptor();
|
||||||
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveReceiverArgumentType, receiverParameter.getType())) {
|
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveReceiverArgumentType, receiverParameter.getType())
|
||||||
|
&& !TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) {
|
||||||
if (CallResolverUtil.checkArgumentCannotBeReceiver(effectiveReceiverArgumentType, candidateDescriptor)
|
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument);
|
||||||
&& !(candidateDescriptor instanceof ExpressionAsFunctionDescriptor)) {
|
return OTHER_ERROR;
|
||||||
return RECEIVER_TYPE_ERROR;
|
|
||||||
}
|
|
||||||
//todo
|
|
||||||
if (!TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())
|
|
||||||
&& !checkOnlyReceiverTypeError) {
|
|
||||||
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument);
|
|
||||||
return OTHER_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (checkOnlyReceiverTypeError) return SUCCESS;
|
|
||||||
|
|
||||||
BindingContext bindingContext = trace.getBindingContext();
|
BindingContext bindingContext = trace.getBindingContext();
|
||||||
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
|
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
|
||||||
@@ -727,7 +751,6 @@ public class CandidateResolver {
|
|||||||
this.status = status;
|
this.status = status;
|
||||||
this.argumentTypes = argumentTypes;
|
this.argumentTypes = argumentTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
Reference in New Issue
Block a user