do not record smart cast to not nullable type separately
This commit is contained in:
@@ -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()) {
|
||||
|
||||
+10
-21
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user