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); 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