refactoring

extracted checkReceiverTypeError method to avoid 'checkOnlyReceiverTypeError' boolean flag
This commit is contained in:
Svetlana Isakova
2013-06-18 19:52:42 +04:00
parent eb85e9abce
commit 9bf5f16bb7
2 changed files with 47 additions and 38 deletions
@@ -152,29 +152,15 @@ public class CallResolverUtil {
return CallKey.create(context.call.getCallType(), (JetExpression) callElement);
}
public static boolean checkArgumentCannotBeReceiver(
@NotNull JetType receiverArgumentType,
@NotNull
public static JetType getErasedReceiverType(
@NotNull ReceiverParameterDescriptor receiverParameterDescriptor,
@NotNull CallableDescriptor descriptor
) {
JetType effectiveReceiverArgumentType = TypeUtils.makeNotNullable(receiverArgumentType);
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;
JetType receiverType = receiverParameterDescriptor.getType();
for (TypeParameterDescriptor typeParameter : descriptor.getTypeParameters()) {
if (typeParameter.getTypeConstructor().equals(receiverType.getConstructor())) {
return typeParameter.getUpperBoundsAsType();
receiverType = typeParameter.getUpperBoundsAsType();
}
}
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.ReceiverValue;
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.ExpressionTypingUtils;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
@@ -83,7 +84,7 @@ public class CandidateResolver {
ResolvedCallImpl<D> candidateCall = context.candidateCall;
D candidate = candidateCall.getCandidateDescriptor();
candidateCall.addStatus(checkReceiver(context, context.trace, /*checkOnlyReceiverTypeError=*/true));
candidateCall.addStatus(checkReceiverTypeError(context.candidateCall));
if (ErrorUtils.isError(candidate)) {
candidateCall.addStatus(SUCCESS);
@@ -578,6 +579,8 @@ public class CandidateResolver {
ResolutionStatus resultStatus = SUCCESS;
ResolvedCall<D> candidateCall = context.candidateCall;
resultStatus = resultStatus.combine(checkReceiverTypeError(candidateCall));
// 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
// 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(
context, candidateCall, trace,
candidateCall.getResultingDescriptor().getReceiverParameter(),
candidateCall.getReceiverArgument(), candidateCall.getExplicitReceiverKind().isReceiver(), false, checkOnlyReceiverTypeError));
candidateCall.getReceiverArgument(), candidateCall.getExplicitReceiverKind().isReceiver(), false));
resultStatus = resultStatus.combine(checkReceiver(
context, candidateCall, trace,
candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(),
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
context.call instanceof CallForImplicitInvoke, checkOnlyReceiverTypeError));
context.call instanceof CallForImplicitInvoke));
return resultStatus;
}
@@ -673,6 +676,37 @@ public class CandidateResolver {
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(
@NotNull CallCandidateResolutionContext<D> context,
@NotNull ResolvedCall<D> candidateCall,
@@ -680,28 +714,18 @@ public class CandidateResolver {
@Nullable ReceiverParameterDescriptor receiverParameter,
@NotNull ReceiverValue receiverArgument,
boolean isExplicitReceiver,
boolean implicitInvokeCheck,
boolean checkOnlyReceiverTypeError
boolean implicitInvokeCheck
) {
if (receiverParameter == null || !receiverArgument.exists()) return SUCCESS;
JetType receiverArgumentType = receiverArgument.getType();
JetType effectiveReceiverArgumentType = TypeUtils.makeNotNullable(receiverArgumentType);
D candidateDescriptor = candidateCall.getCandidateDescriptor();
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveReceiverArgumentType, receiverParameter.getType())) {
if (CallResolverUtil.checkArgumentCannotBeReceiver(effectiveReceiverArgumentType, candidateDescriptor)
&& !(candidateDescriptor instanceof ExpressionAsFunctionDescriptor)) {
return RECEIVER_TYPE_ERROR;
}
//todo
if (!TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())
&& !checkOnlyReceiverTypeError) {
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument);
return OTHER_ERROR;
}
if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveReceiverArgumentType, receiverParameter.getType())
&& !TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) {
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument);
return OTHER_ERROR;
}
if (checkOnlyReceiverTypeError) return SUCCESS;
BindingContext bindingContext = trace.getBindingContext();
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
@@ -727,7 +751,6 @@ public class CandidateResolver {
this.status = status;
this.argumentTypes = argumentTypes;
}
}
@NotNull