DFA: count null comparison as identity comparison #KT-16538 Fixed

+ minor parameter refactoring
This commit is contained in:
mglukhikh
2017-03-24 13:05:12 +03:00
committed by Mikhail Glukhikh
parent b5aa529901
commit 4700936f66
7 changed files with 60 additions and 9 deletions
@@ -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
@@ -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 {
@@ -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");