DFA: count null comparison as identity comparison #KT-16538 Fixed
+ minor parameter refactoring
This commit is contained in:
committed by
Mikhail Glukhikh
parent
b5aa529901
commit
4700936f66
@@ -73,9 +73,8 @@ interface DataFlowInfo {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function when it's known than a == b.
|
* Call this function when it's known than a == b.
|
||||||
* sameTypes should be true iff we have guarantee that a and b have the same type
|
|
||||||
*/
|
*/
|
||||||
fun equate(a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean, languageVersionSettings: LanguageVersionSettings): DataFlowInfo
|
fun equate(a: DataFlowValue, b: DataFlowValue, identityEquals: Boolean, languageVersionSettings: LanguageVersionSettings): DataFlowInfo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function when it's known than a != b
|
* Call this function when it's known than a != b
|
||||||
|
|||||||
+2
-2
@@ -169,7 +169,7 @@ internal class DelegatingDataFlowInfo private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun equate(
|
override fun equate(
|
||||||
a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean, languageVersionSettings: LanguageVersionSettings
|
a: DataFlowValue, b: DataFlowValue, identityEquals: Boolean, languageVersionSettings: LanguageVersionSettings
|
||||||
): DataFlowInfo {
|
): DataFlowInfo {
|
||||||
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
|
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
|
||||||
val nullabilityOfA = getStableNullability(a)
|
val nullabilityOfA = getStableNullability(a)
|
||||||
@@ -180,7 +180,7 @@ internal class DelegatingDataFlowInfo private constructor(
|
|||||||
|
|
||||||
// NB: == has no guarantees of type equality, see KT-11280 for the example
|
// NB: == has no guarantees of type equality, see KT-11280 for the example
|
||||||
val newTypeInfo = newTypeInfo()
|
val newTypeInfo = newTypeInfo()
|
||||||
if (sameTypes) {
|
if (identityEquals || !nullabilityOfA.canBeNonNull() || !nullabilityOfB.canBeNonNull()) {
|
||||||
newTypeInfo.putAll(a, getStableTypes(b, false))
|
newTypeInfo.putAll(a, getStableTypes(b, false))
|
||||||
newTypeInfo.putAll(b, getStableTypes(a, false))
|
newTypeInfo.putAll(b, getStableTypes(a, false))
|
||||||
if (a.type != b.type) {
|
if (a.type != b.type) {
|
||||||
|
|||||||
@@ -178,10 +178,11 @@ public class DataFlowAnalyzer {
|
|||||||
}
|
}
|
||||||
if (equals != null) {
|
if (equals != null) {
|
||||||
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
|
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
|
||||||
boolean byIdentity = operationToken == KtTokens.EQEQEQ || operationToken == KtTokens.EXCLEQEQEQ ||
|
boolean identityEquals = operationToken == KtTokens.EQEQEQ ||
|
||||||
typeHasEqualsFromAny(lhsType, condition);
|
operationToken == KtTokens.EXCLEQEQEQ ||
|
||||||
|
typeHasEqualsFromAny(lhsType, condition);
|
||||||
result.set(context.dataFlowInfo
|
result.set(context.dataFlowInfo
|
||||||
.equate(leftValue, rightValue, byIdentity, languageVersionSettings)
|
.equate(leftValue, rightValue, identityEquals, languageVersionSettings)
|
||||||
.and(expressionFlowInfo));
|
.and(expressionFlowInfo));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+2
-2
@@ -407,8 +407,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
val result = noChange(newContext)
|
val result = noChange(newContext)
|
||||||
return ConditionalDataFlowInfo(
|
return ConditionalDataFlowInfo(
|
||||||
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue,
|
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue,
|
||||||
DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression),
|
identityEquals = DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression),
|
||||||
components.languageVersionSettings),
|
languageVersionSettings = components.languageVersionSettings),
|
||||||
result.elseInfo.disequate(subjectDataFlowValue,
|
result.elseInfo.disequate(subjectDataFlowValue,
|
||||||
expressionDataFlowValue,
|
expressionDataFlowValue,
|
||||||
components.languageVersionSettings)
|
components.languageVersionSettings)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
fun foo(x: String?) = x
|
||||||
|
|
||||||
|
class Test
|
||||||
|
|
||||||
|
class TestWithEquals {
|
||||||
|
override fun equals(other: Any?) = super.equals(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(i: Test?) {
|
||||||
|
if (i == null) foo(<!DEBUG_INFO_CONSTANT!>i<!>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(i: TestWithEquals?) {
|
||||||
|
if (i == null) foo(<!DEBUG_INFO_CONSTANT!>i<!>)
|
||||||
|
if (null == i) foo(<!DEBUG_INFO_CONSTANT!>i<!>)
|
||||||
|
when (i) {
|
||||||
|
null -> foo(<!DEBUG_INFO_CONSTANT!>i<!>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gav(i: TestWithEquals?, j: TestWithEquals?) {
|
||||||
|
if (j == null) {
|
||||||
|
if (i == <!DEBUG_INFO_CONSTANT!>j<!>) foo(<!DEBUG_INFO_CONSTANT!>i<!>)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun bar(/*0*/ i: Test?): kotlin.Unit
|
||||||
|
public fun bar(/*0*/ i: TestWithEquals?): kotlin.Unit
|
||||||
|
public fun foo(/*0*/ x: kotlin.String?): kotlin.String?
|
||||||
|
public fun gav(/*0*/ i: TestWithEquals?, /*1*/ j: TestWithEquals?): kotlin.Unit
|
||||||
|
|
||||||
|
public final class Test {
|
||||||
|
public constructor Test()
|
||||||
|
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 TestWithEquals {
|
||||||
|
public constructor TestWithEquals()
|
||||||
|
public open override /*1*/ 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
|
||||||
|
}
|
||||||
@@ -19132,6 +19132,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("equals.kt")
|
||||||
|
public void testEquals() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/equals.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("exclUnderAnd.kt")
|
@TestMetadata("exclUnderAnd.kt")
|
||||||
public void testExclUnderAnd() throws Exception {
|
public void testExclUnderAnd() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/exclUnderAnd.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/exclUnderAnd.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user