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.
* 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
@@ -169,7 +169,7 @@ internal class DelegatingDataFlowInfo private constructor(
}
override fun equate(
a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean, languageVersionSettings: LanguageVersionSettings
a: DataFlowValue, b: DataFlowValue, identityEquals: Boolean, languageVersionSettings: LanguageVersionSettings
): DataFlowInfo {
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
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
val newTypeInfo = newTypeInfo()
if (sameTypes) {
if (identityEquals || !nullabilityOfA.canBeNonNull() || !nullabilityOfB.canBeNonNull()) {
newTypeInfo.putAll(a, getStableTypes(b, false))
newTypeInfo.putAll(b, getStableTypes(a, false))
if (a.type != b.type) {
@@ -178,10 +178,11 @@ public class DataFlowAnalyzer {
}
if (equals != null) {
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
boolean byIdentity = operationToken == KtTokens.EQEQEQ || operationToken == KtTokens.EXCLEQEQEQ ||
typeHasEqualsFromAny(lhsType, condition);
boolean identityEquals = operationToken == KtTokens.EQEQEQ ||
operationToken == KtTokens.EXCLEQEQEQ ||
typeHasEqualsFromAny(lhsType, condition);
result.set(context.dataFlowInfo
.equate(leftValue, rightValue, byIdentity, languageVersionSettings)
.equate(leftValue, rightValue, identityEquals, languageVersionSettings)
.and(expressionFlowInfo));
}
else {
@@ -407,8 +407,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
val result = noChange(newContext)
return ConditionalDataFlowInfo(
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue,
DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression),
components.languageVersionSettings),
identityEquals = DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression),
languageVersionSettings = components.languageVersionSettings),
result.elseInfo.disequate(subjectDataFlowValue,
expressionDataFlowValue,
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);
}
@TestMetadata("equals.kt")
public void testEquals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/equals.kt");
doTest(fileName);
}
@TestMetadata("exclUnderAnd.kt")
public void testExclUnderAnd() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/exclUnderAnd.kt");