record smartcast to the most specific type
This commit is contained in:
+23
-11
@@ -17,7 +17,9 @@
|
|||||||
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||||
@@ -28,9 +30,11 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
|
|||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||||
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.diagnostics.Errors.AUTOCAST_IMPOSSIBLE;
|
import static org.jetbrains.jet.lang.diagnostics.Errors.AUTOCAST_IMPOSSIBLE;
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
|
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
|
||||||
@@ -96,20 +100,28 @@ public class AutoCastUtils {
|
|||||||
@NotNull ResolutionContext context
|
@NotNull ResolutionContext context
|
||||||
) {
|
) {
|
||||||
List<JetType> autoCastTypes = getAutoCastVariants(receiverArgument, context);
|
List<JetType> autoCastTypes = getAutoCastVariants(receiverArgument, context);
|
||||||
return isSubTypeByAutoCast(receiverParameterType, autoCastTypes);
|
return getAutoCastSubType(receiverParameterType, autoCastTypes) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSubTypeByAutoCast(
|
@Nullable
|
||||||
|
private static JetType getAutoCastSubType(
|
||||||
@NotNull JetType receiverParameterType,
|
@NotNull JetType receiverParameterType,
|
||||||
@NotNull List<JetType> autoCastTypes
|
@NotNull List<JetType> autoCastTypes
|
||||||
) {
|
) {
|
||||||
|
Set<JetType> subTypes = Sets.newHashSet();
|
||||||
for (JetType autoCastType : autoCastTypes) {
|
for (JetType autoCastType : autoCastTypes) {
|
||||||
JetType effectiveAutoCastType = TypeUtils.makeNotNullable(autoCastType);
|
JetType effectiveAutoCastType = TypeUtils.makeNotNullable(autoCastType);
|
||||||
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveAutoCastType, receiverParameterType)) {
|
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveAutoCastType, receiverParameterType)) {
|
||||||
return true;
|
subTypes.add(autoCastType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
if (subTypes.isEmpty()) return null;
|
||||||
|
|
||||||
|
JetType intersection = TypeUtils.intersect(JetTypeChecker.INSTANCE, subTypes);
|
||||||
|
if (intersection == null || !intersection.getConstructor().isDenotable()) {
|
||||||
|
return receiverParameterType;
|
||||||
|
}
|
||||||
|
return intersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean recordAutoCastIfNecessary(
|
public static boolean recordAutoCastIfNecessary(
|
||||||
@@ -125,14 +137,14 @@ public class AutoCastUtils {
|
|||||||
|
|
||||||
List<JetType> autoCastTypesExcludingReceiver = getAutoCastVariantsExcludingReceiver(
|
List<JetType> autoCastTypesExcludingReceiver = getAutoCastVariantsExcludingReceiver(
|
||||||
context.trace.getBindingContext(), context.dataFlowInfo, receiver);
|
context.trace.getBindingContext(), context.dataFlowInfo, receiver);
|
||||||
boolean autoCast = isSubTypeByAutoCast(receiverType, autoCastTypesExcludingReceiver);
|
JetType autoCastSubType = getAutoCastSubType(receiverType, autoCastTypesExcludingReceiver);
|
||||||
if (autoCast) {
|
if (autoCastSubType == null) return false;
|
||||||
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
|
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext());
|
|
||||||
|
|
||||||
recordCastOrError(expression, receiverType, context.trace, dataFlowValue.isStableIdentifier(), true);
|
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
|
||||||
}
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext());
|
||||||
return autoCast;
|
|
||||||
|
recordCastOrError(expression, autoCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void recordAutoCastToNotNullableType(@NotNull ReceiverValue receiver, @NotNull BindingTrace trace) {
|
public static void recordAutoCastToNotNullableType(@NotNull ReceiverValue receiver, @NotNull BindingTrace trace) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ fun f9(a : A?) {
|
|||||||
a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>()
|
a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>()
|
||||||
if (a is B) {
|
if (a is B) {
|
||||||
<info descr="Automatically cast to B">a</info>.bar()
|
<info descr="Automatically cast to B">a</info>.bar()
|
||||||
<info descr="Automatically cast to A">a</info>.foo()
|
<info descr="Automatically cast to B">a</info>.foo()
|
||||||
}
|
}
|
||||||
a<info>?.</info>foo()
|
a<info>?.</info>foo()
|
||||||
a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>()
|
a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>()
|
||||||
@@ -26,7 +26,7 @@ fun f9(a : A?) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
<info descr="Automatically cast to B">a</info>.bar()
|
<info descr="Automatically cast to B">a</info>.bar()
|
||||||
<info descr="Automatically cast to A">a</info>.foo()
|
<info descr="Automatically cast to B">a</info>.foo()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun f10(a : A?) {
|
fun f10(a : A?) {
|
||||||
@@ -80,7 +80,7 @@ fun f12(a : A?) {
|
|||||||
|
|
||||||
fun f13(a : A?) {
|
fun f13(a : A?) {
|
||||||
if (a is B) {
|
if (a is B) {
|
||||||
<info descr="Automatically cast to A">a</info>.foo()
|
<info descr="Automatically cast to B">a</info>.foo()
|
||||||
<info descr="Automatically cast to B">a</info>.bar()
|
<info descr="Automatically cast to B">a</info>.bar()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -93,12 +93,12 @@ fun f13(a : A?) {
|
|||||||
a<info>?.</info>foo()
|
a<info>?.</info>foo()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
<info descr="Automatically cast to A">a</info>.foo()
|
<info descr="Automatically cast to B">a</info>.foo()
|
||||||
}
|
}
|
||||||
|
|
||||||
a<info>?.</info>foo()
|
a<info>?.</info>foo()
|
||||||
if (a is B && <info descr="Automatically cast to A">a</info>.foo() == Unit.VALUE) {
|
if (a is B && <info descr="Automatically cast to B">a</info>.foo() == Unit.VALUE) {
|
||||||
<info descr="Automatically cast to A">a</info>.foo()
|
<info descr="Automatically cast to B">a</info>.foo()
|
||||||
<info descr="Automatically cast to B">a</info>.bar()
|
<info descr="Automatically cast to B">a</info>.bar()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
Reference in New Issue
Block a user