do not record smart cast to not nullable type separately

This commit is contained in:
Svetlana Isakova
2014-01-31 16:33:19 +04:00
parent 9063b3803e
commit 4c1c9f8407
4 changed files with 60 additions and 35 deletions
@@ -864,7 +864,7 @@ public class CandidateResolver {
JetType erasedReceiverType = CallResolverUtil.getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor);
boolean isSubtypeByAutoCast = AutoCastUtils.isSubTypeByAutoCast(receiverArgument, erasedReceiverType, context);
boolean isSubtypeByAutoCast = AutoCastUtils.isSubTypeByAutoCastIgnoringNullability(receiverArgument, erasedReceiverType, context);
if (!isSubtypeByAutoCast) {
return RECEIVER_TYPE_ERROR;
}
@@ -883,30 +883,26 @@ public class CandidateResolver {
) {
if (receiverParameter == null || !receiverArgument.exists()) return SUCCESS;
D candidateDescriptor = candidateCall.getCandidateDescriptor();
if (TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) return SUCCESS;
boolean smartCastRecorded = false;
if (!TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) {
boolean isSubtypeByAutoCast = AutoCastUtils.isSubTypeByAutoCast(receiverArgument, receiverParameter.getType(), context);
if (!isSubtypeByAutoCast) {
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument);
return OTHER_ERROR;
}
smartCastRecorded = AutoCastUtils.recordAutoCastIfNecessary(receiverArgument, receiverParameter.getType(), context);
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
boolean isSubtypeByAutoCast = AutoCastUtils.isSubTypeByAutoCastIgnoringNullability(
receiverArgument, receiverParameter.getType(), context);
if (!isSubtypeByAutoCast) {
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument);
return OTHER_ERROR;
}
AutoCastUtils.recordAutoCastIfNecessary(receiverArgument, receiverParameter.getType(), context, safeAccess);
JetType receiverArgumentType = receiverArgument.getType();
BindingContext bindingContext = trace.getBindingContext();
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
if (!safeAccess && !receiverParameter.getType().isNullable() && receiverArgument.getType().isNullable()) {
if (!safeAccess && !receiverParameter.getType().isNullable() && receiverArgumentType.isNullable()) {
if (!AutoCastUtils.isNotNull(receiverArgument, bindingContext, context.dataFlowInfo)) {
context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck);
return UNSAFE_CALL_ERROR;
}
if (!smartCastRecorded && isExplicitReceiver) {
AutoCastUtils.recordAutoCastToNotNullableType(receiverArgument, context.trace);
}
}
DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, bindingContext);
if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) {
@@ -94,13 +94,13 @@ public class AutoCastUtils {
return Lists.newArrayList(dataFlowInfo.getPossibleTypes(dataFlowValue));
}
public static boolean isSubTypeByAutoCast(
public static boolean isSubTypeByAutoCastIgnoringNullability(
@NotNull ReceiverValue receiverArgument,
@NotNull JetType receiverParameterType,
@NotNull ResolutionContext context
) {
List<JetType> autoCastTypes = getAutoCastVariants(receiverArgument, context);
return getAutoCastSubType(receiverParameterType, autoCastTypes) != null;
return getAutoCastSubType(TypeUtils.makeNullable(receiverParameterType), autoCastTypes) != null;
}
@Nullable
@@ -110,8 +110,7 @@ public class AutoCastUtils {
) {
Set<JetType> subTypes = Sets.newHashSet();
for (JetType autoCastType : autoCastTypes) {
JetType effectiveAutoCastType = TypeUtils.makeNotNullable(autoCastType);
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveAutoCastType, receiverParameterType)) {
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(autoCastType, receiverParameterType)) {
subTypes.add(autoCastType);
}
}
@@ -125,19 +124,21 @@ public class AutoCastUtils {
}
public static boolean recordAutoCastIfNecessary(
ReceiverValue receiver,
@NotNull JetType receiverType,
@NotNull ResolutionContext context
@NotNull ReceiverValue receiver,
@NotNull JetType receiverParameterType,
@NotNull ResolutionContext context,
boolean safeAccess
) {
if (!(receiver instanceof ExpressionReceiver)) return false;
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(receiver.getType(), receiverType)) {
receiverParameterType = safeAccess ? TypeUtils.makeNullable(receiverParameterType) : receiverParameterType;
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(receiver.getType(), receiverParameterType)) {
return false;
}
List<JetType> autoCastTypesExcludingReceiver = getAutoCastVariantsExcludingReceiver(
context.trace.getBindingContext(), context.dataFlowInfo, receiver);
JetType autoCastSubType = getAutoCastSubType(receiverType, autoCastTypesExcludingReceiver);
JetType autoCastSubType = getAutoCastSubType(receiverParameterType, autoCastTypesExcludingReceiver);
if (autoCastSubType == null) return false;
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
@@ -147,18 +148,6 @@ public class AutoCastUtils {
return true;
}
public static void recordAutoCastToNotNullableType(@NotNull ReceiverValue receiver, @NotNull BindingTrace trace) {
if (!(receiver instanceof ExpressionReceiver)) return;
JetType receiverType = receiver.getType();
if (!receiverType.isNullable()) return;
JetType notNullableType = TypeUtils.makeNotNullable(receiverType);
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, trace.getBindingContext());
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
recordCastOrError(expression, notNullableType, trace, dataFlowValue.isStableIdentifier(), true);
}
public static void recordCastOrError(
@NotNull JetExpression expression,
@NotNull JetType type,
@@ -0,0 +1,35 @@
trait A {
fun foo()
}
trait B : A {
fun bar()
}
trait C
trait D : A
fun test(a: A?) {
if (a != null && a is B?) {
<info descr="Automatically cast to B">a</info>.bar()
}
if (a is B && a is C) {
<info descr="Automatically cast to B">a</info>.foo()
}
if (a is B? && a is C?) {
<info descr="Automatically cast to B?">a</info><info>?.</info>bar()
}
a<info>?.</info>foo()
if (a is B? && a is C?) {
a<info>?.</info>foo()
}
if (a is B && a is D) {
//when it's resolved, the message should be 'Automatically cast to A'
a.<error>foo</error>
}
}
@@ -442,6 +442,11 @@ public class JetPsiCheckerTestGenerated extends AbstractJetPsiCheckerTest {
doTestWithInfos("idea/testData/checker/infos/PropertiesWithBackingFields.kt");
}
@TestMetadata("SmartCastsWithSafeAccess.kt")
public void testSmartCastsWithSafeAccess() throws Exception {
doTestWithInfos("idea/testData/checker/infos/SmartCastsWithSafeAccess.kt");
}
@TestMetadata("Typos.kt")
public void testTypos() throws Exception {
doTestWithInfos("idea/testData/checker/infos/Typos.kt");