DFA bug fix: assignment of unstable to stable uses predictable type set (see test)
This commit is contained in:
@@ -51,12 +51,24 @@ public interface DataFlowInfo {
|
||||
Nullability getPredictableNullability(@NotNull DataFlowValue key);
|
||||
|
||||
/**
|
||||
* Returns possible types for the given value, NOT taking its predictability into account.
|
||||
*
|
||||
* IMPORTANT: by default, the original (native) type for this value
|
||||
* are NOT included. So it's quite possible to get an empty set here.
|
||||
*/
|
||||
@NotNull
|
||||
Set<KotlinType> getPossibleTypes(@NotNull DataFlowValue key);
|
||||
|
||||
/**
|
||||
* Returns possible types for the given value if it's predictable.
|
||||
* Otherwise, basic value type is returned.
|
||||
*
|
||||
* IMPORTANT: by default, the original (native) type for this value
|
||||
* are NOT included. So it's quite possible to get an empty set here.
|
||||
*/
|
||||
@NotNull
|
||||
Set<KotlinType> getPredictableTypes(@NotNull DataFlowValue key);
|
||||
|
||||
/**
|
||||
* Call this function to clear all data flow information about
|
||||
* the given data flow value. Useful when we are not sure how this value can be changed, e.g. in a loop.
|
||||
|
||||
+14
-8
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL;
|
||||
|
||||
@@ -145,7 +142,7 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
||||
public Set<KotlinType> getPossibleTypes(@NotNull DataFlowValue key) {
|
||||
KotlinType originalType = key.getType();
|
||||
Set<KotlinType> types = collectTypesFromMeAndParents(key);
|
||||
if (getPredictableNullability(key).canBeNull()) {
|
||||
if (getNullability(key).canBeNull()) {
|
||||
return types;
|
||||
}
|
||||
|
||||
@@ -160,6 +157,15 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
||||
return enrichedTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Set<KotlinType> getPredictableTypes(@NotNull DataFlowValue key) {
|
||||
if (!key.isPredictable()) {
|
||||
return new LinkedHashSet<KotlinType>();
|
||||
}
|
||||
return getPossibleTypes(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function to clear all data flow information about
|
||||
* the given data flow value.
|
||||
@@ -187,7 +193,7 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
||||
putNullability(nullability, a, nullabilityOfB);
|
||||
|
||||
SetMultimap<DataFlowValue, KotlinType> newTypeInfo = newTypeInfo();
|
||||
Set<KotlinType> typesForB = getPossibleTypes(b);
|
||||
Set<KotlinType> typesForB = getPredictableTypes(b);
|
||||
// Own type of B must be recorded separately, e.g. for a constant
|
||||
// But if its type is the same as A or it's null, there is no reason to do it
|
||||
// because usually null type or own type are not saved in this set
|
||||
@@ -216,8 +222,8 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
|
||||
changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA));
|
||||
|
||||
SetMultimap<DataFlowValue, KotlinType> newTypeInfo = newTypeInfo();
|
||||
newTypeInfo.putAll(a, collectTypesFromMeAndParents(b));
|
||||
newTypeInfo.putAll(b, collectTypesFromMeAndParents(a));
|
||||
newTypeInfo.putAll(a, getPredictableTypes(b));
|
||||
newTypeInfo.putAll(b, getPredictableTypes(a));
|
||||
if (!a.getType().equals(b.getType())) {
|
||||
// To avoid recording base types of own type
|
||||
if (!TypeUtilsKt.isSubtypeOf(a.getType(), b.getType())) {
|
||||
|
||||
@@ -311,9 +311,7 @@ public class DataFlowAnalyzer {
|
||||
) {
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
|
||||
Collection<KotlinType> possibleTypes = Sets.newHashSet(type);
|
||||
if (dataFlowValue.isPredictable()) {
|
||||
possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
|
||||
}
|
||||
possibleTypes.addAll(dataFlowInfo.getPredictableTypes(dataFlowValue));
|
||||
return possibleTypes;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
class Bar {
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
class Foo(var x: Any) {
|
||||
init {
|
||||
if (x is Bar) {
|
||||
val y = x
|
||||
// Error: x is not stable, Type(y) = Any
|
||||
<!SMARTCAST_IMPOSSIBLE!>x<!>.bar()
|
||||
y.<!UNRESOLVED_REFERENCE!>bar<!>()
|
||||
if (y == x) {
|
||||
// Still error
|
||||
y.<!UNRESOLVED_REFERENCE!>bar<!>()
|
||||
}
|
||||
if (x !is Bar && y != x) {
|
||||
// Still error
|
||||
y.<!UNRESOLVED_REFERENCE!>bar<!>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package
|
||||
|
||||
public final class Bar {
|
||||
public constructor Bar()
|
||||
public final fun bar(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Foo {
|
||||
public constructor Foo(/*0*/ x: kotlin.Any)
|
||||
public final var x: kotlin.Any
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -15066,6 +15066,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unstableToStableTypes.kt")
|
||||
public void testUnstableToStableTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("varChangedInInitializer.kt")
|
||||
public void testVarChangedInInitializer() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.kt");
|
||||
|
||||
Reference in New Issue
Block a user