From 4c1c9f84071a50cf07eec65d939776a803cdbfbb Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Fri, 31 Jan 2014 16:33:19 +0400 Subject: [PATCH] do not record smart cast to not nullable type separately --- .../lang/resolve/calls/CandidateResolver.java | 24 ++++++------- .../calls/autocasts/AutoCastUtils.java | 31 ++++++---------- .../checker/infos/SmartCastsWithSafeAccess.kt | 35 +++++++++++++++++++ .../checkers/JetPsiCheckerTestGenerated.java | 5 +++ 4 files changed, 60 insertions(+), 35 deletions(-) create mode 100644 idea/testData/checker/infos/SmartCastsWithSafeAccess.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index 86ea5cfe42e..ce9d2ec4b14 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -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()) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java index ca0a0f646df..2e6bdc26d93 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java @@ -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 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 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 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, diff --git a/idea/testData/checker/infos/SmartCastsWithSafeAccess.kt b/idea/testData/checker/infos/SmartCastsWithSafeAccess.kt new file mode 100644 index 00000000000..eeecc0f3552 --- /dev/null +++ b/idea/testData/checker/infos/SmartCastsWithSafeAccess.kt @@ -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?) { + a.bar() + } + + if (a is B && a is C) { + a.foo() + } + + if (a is B? && a is C?) { + a?.bar() + } + + a?.foo() + if (a is B? && a is C?) { + a?.foo() + } + + if (a is B && a is D) { + //when it's resolved, the message should be 'Automatically cast to A' + a.foo + } +} diff --git a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java index 090af0aa443..d68efdf1b91 100644 --- a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java @@ -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");