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 ConstantExpressionEvaluator constantExpressionEvaluator;
|
||||||
private final KotlinBuiltIns builtIns;
|
private final KotlinBuiltIns builtIns;
|
||||||
private final SmartCastManager smartCastManager;
|
private final SmartCastManager smartCastManager;
|
||||||
|
private final ExpressionTypingFacade facade;
|
||||||
|
|
||||||
public DataFlowAnalyzer(
|
public DataFlowAnalyzer(
|
||||||
@NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
|
@NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
|
||||||
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
||||||
@NotNull KotlinBuiltIns builtIns,
|
@NotNull KotlinBuiltIns builtIns,
|
||||||
@NotNull SmartCastManager smartCastManager
|
@NotNull SmartCastManager smartCastManager,
|
||||||
|
@NotNull ExpressionTypingFacade facade
|
||||||
) {
|
) {
|
||||||
this.additionalTypeCheckers = additionalTypeCheckers;
|
this.additionalTypeCheckers = additionalTypeCheckers;
|
||||||
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
||||||
this.builtIns = builtIns;
|
this.builtIns = builtIns;
|
||||||
this.smartCastManager = smartCastManager;
|
this.smartCastManager = smartCastManager;
|
||||||
|
this.facade = facade;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -100,6 +103,7 @@ public class DataFlowAnalyzer {
|
|||||||
result.set(dataFlowInfo);
|
result.set(dataFlowInfo);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
DataFlowInfo expressionFlowInfo = facade.getTypeInfo(expression, context).getDataFlowInfo();
|
||||||
KtExpression left = expression.getLeft();
|
KtExpression left = expression.getLeft();
|
||||||
if (left == null) return;
|
if (left == null) return;
|
||||||
KtExpression right = expression.getRight();
|
KtExpression right = expression.getRight();
|
||||||
@@ -122,12 +126,14 @@ 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
|
||||||
result.set(context.dataFlowInfo.equate(leftValue, rightValue));
|
result.set(context.dataFlowInfo.equate(leftValue, rightValue).and(expressionFlowInfo));
|
||||||
}
|
}
|
||||||
else {
|
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));
|
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
|
@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);
|
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")
|
@TestMetadata("dataFlowInfoForArguments.kt")
|
||||||
public void testDataFlowInfoForArguments() throws Exception {
|
public void testDataFlowInfoForArguments() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt");
|
||||||
@@ -14346,6 +14352,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("extensionSafeCall.kt")
|
||||||
public void testExtensionSafeCall() throws Exception {
|
public void testExtensionSafeCall() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/extensionSafeCall.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/extensionSafeCall.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user