Standard data flow analysis results are taken into account during condition analysis #KT-7933 Fixed
This commit is contained in:
@@ -49,17 +49,20 @@ public class DataFlowAnalyzer {
|
||||
private final ConstantExpressionEvaluator constantExpressionEvaluator;
|
||||
private final KotlinBuiltIns builtIns;
|
||||
private final SmartCastManager smartCastManager;
|
||||
private final ExpressionTypingFacade facade;
|
||||
|
||||
public DataFlowAnalyzer(
|
||||
@NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
|
||||
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
||||
@NotNull KotlinBuiltIns builtIns,
|
||||
@NotNull SmartCastManager smartCastManager
|
||||
@NotNull SmartCastManager smartCastManager,
|
||||
@NotNull ExpressionTypingFacade facade
|
||||
) {
|
||||
this.additionalTypeCheckers = additionalTypeCheckers;
|
||||
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
||||
this.builtIns = builtIns;
|
||||
this.smartCastManager = smartCastManager;
|
||||
this.facade = facade;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -100,6 +103,7 @@ public class DataFlowAnalyzer {
|
||||
result.set(dataFlowInfo);
|
||||
}
|
||||
else {
|
||||
DataFlowInfo expressionFlowInfo = facade.getTypeInfo(expression, context).getDataFlowInfo();
|
||||
KtExpression left = expression.getLeft();
|
||||
if (left == null) return;
|
||||
KtExpression right = expression.getRight();
|
||||
@@ -122,12 +126,14 @@ public class DataFlowAnalyzer {
|
||||
}
|
||||
if (equals != null) {
|
||||
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
|
||||
result.set(context.dataFlowInfo.equate(leftValue, rightValue));
|
||||
result.set(context.dataFlowInfo.equate(leftValue, rightValue).and(expressionFlowInfo));
|
||||
}
|
||||
else {
|
||||
result.set(context.dataFlowInfo.disequate(leftValue, rightValue));
|
||||
result.set(context.dataFlowInfo.disequate(leftValue, rightValue).and(expressionFlowInfo));
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
result.set(expressionFlowInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +147,15 @@ public class DataFlowAnalyzer {
|
||||
result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, context));
|
||||
}
|
||||
}
|
||||
else {
|
||||
visitExpression(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExpression(@NotNull KtExpression expression) {
|
||||
// In fact, everything is taken from trace here
|
||||
result.set(facade.getTypeInfo(expression, context).getDataFlowInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
fun foo(x: String?, y: String?, z: String?) {
|
||||
if ((x!!.hashCode() == 0 || y!!.hashCode() == 1) && z!!.hashCode() == 2) {
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
// condition is true => z!! after and is called
|
||||
<!DEBUG_INFO_SMARTCAST!>z<!>.length
|
||||
}
|
||||
else {
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
z<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
// First element is always analyzed
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||
var xx = y ?: z
|
||||
if ((xx!!.hashCode() == 0 && y!!.hashCode() == 1) || z!!.hashCode() == 2) {
|
||||
<!DEBUG_INFO_SMARTCAST!>xx<!>.length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
z<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
else {
|
||||
<!DEBUG_INFO_SMARTCAST!>xx<!>.length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
// condition is false => z!! after or is called
|
||||
<!DEBUG_INFO_SMARTCAST!>z<!>.length
|
||||
}
|
||||
// First element is always analyzed
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||
xx = y ?: z
|
||||
if (xx!!.hashCode() == 0 && y!!.hashCode() == 1 && z!!.hashCode() == 2) {
|
||||
// all three are called
|
||||
<!DEBUG_INFO_SMARTCAST!>xx<!>.length
|
||||
<!DEBUG_INFO_SMARTCAST!>y<!>.length
|
||||
<!DEBUG_INFO_SMARTCAST!>z<!>.length
|
||||
}
|
||||
else {
|
||||
<!DEBUG_INFO_SMARTCAST!>xx<!>.length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
z<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
// First element is always analyzed
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||
xx = y ?: z
|
||||
if (xx!!.hashCode() == 0 || y!!.hashCode() == 1 || z!!.hashCode() == 2) {
|
||||
<!DEBUG_INFO_SMARTCAST!>xx<!>.length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
z<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
else {
|
||||
// all three are called
|
||||
<!DEBUG_INFO_SMARTCAST!>xx<!>.length
|
||||
<!DEBUG_INFO_SMARTCAST!>y<!>.length
|
||||
<!DEBUG_INFO_SMARTCAST!>z<!>.length
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?, /*2*/ z: kotlin.String?): kotlin.Unit
|
||||
@@ -0,0 +1,13 @@
|
||||
class D(val a: String, val b: Boolean)
|
||||
|
||||
fun foo(p: Boolean, v: D?): String {
|
||||
if (p && v!!.b) <!DEBUG_INFO_SMARTCAST!>v<!>.a
|
||||
else v<!UNSAFE_CALL!>.<!>a
|
||||
if (p && v!! == D("?", false)) <!DEBUG_INFO_SMARTCAST!>v<!>.a
|
||||
else v<!UNSAFE_CALL!>.<!>a
|
||||
if (p || v!!.b) v<!UNSAFE_CALL!>.<!>a
|
||||
else <!DEBUG_INFO_SMARTCAST!>v<!>.a
|
||||
if (p || v!! == D("?", false)) v<!UNSAFE_CALL!>.<!>a
|
||||
else <!DEBUG_INFO_SMARTCAST!>v<!>.a
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.Boolean, /*1*/ v: D?): kotlin.String
|
||||
|
||||
public final class D {
|
||||
public constructor D(/*0*/ a: kotlin.String, /*1*/ b: kotlin.Boolean)
|
||||
public final val a: kotlin.String
|
||||
public final val b: kotlin.Boolean
|
||||
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
|
||||
}
|
||||
@@ -14310,6 +14310,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("complexConditionsWithExcl.kt")
|
||||
public void testComplexConditionsWithExcl() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/complexConditionsWithExcl.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dataFlowInfoForArguments.kt")
|
||||
public void testDataFlowInfoForArguments() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt");
|
||||
@@ -14346,6 +14352,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exclUnderAnd.kt")
|
||||
public void testExclUnderAnd() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/exclUnderAnd.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionSafeCall.kt")
|
||||
public void testExtensionSafeCall() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/extensionSafeCall.kt");
|
||||
|
||||
Reference in New Issue
Block a user