Correct behaviour for break / continue inside function call arguments, including multiple ones. Additional tests.
This commit is contained in:
+15
-2
@@ -50,6 +50,7 @@ import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPac
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
||||
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
|
||||
@@ -217,8 +218,20 @@ public class CallExpressionResolver {
|
||||
}
|
||||
|
||||
JetType type = functionDescriptor.getReturnType();
|
||||
|
||||
return TypeInfoFactoryPackage.createTypeInfo(type, resolvedCall.getDataFlowInfoForArguments().getResultInfo());
|
||||
// Extracting jump out possible and jump point flow info from arguments, if any
|
||||
List<? extends ValueArgument> arguments = callExpression.getValueArguments();
|
||||
DataFlowInfo resultFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
|
||||
DataFlowInfo jumpFlowInfo = resultFlowInfo;
|
||||
boolean jumpOutPossible = false;
|
||||
for (ValueArgument argument: arguments) {
|
||||
JetTypeInfo argTypeInfo = context.trace.get(BindingContext.EXPRESSION_TYPE_INFO, argument.getArgumentExpression());
|
||||
if (argTypeInfo != null && argTypeInfo.getJumpOutPossible()) {
|
||||
jumpOutPossible = true;
|
||||
jumpFlowInfo = argTypeInfo.getJumpFlowInfo();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TypeInfoFactoryPackage.createTypeInfo(type, resultFlowInfo, jumpOutPossible, jumpFlowInfo);
|
||||
}
|
||||
|
||||
JetExpression calleeExpression = callExpression.getCalleeExpression();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
fun bar(): Boolean { return true }
|
||||
|
||||
fun gav(w: String, arg: Any, z: String): String
|
||||
{ return if (arg is String) <!DEBUG_INFO_SMARTCAST!>arg<!> else if (z != "") z else w }
|
||||
|
||||
public fun foo(x: String?, z: String?, w: String?): Int {
|
||||
do {
|
||||
gav(w!!, if (x == null) break else <!DEBUG_INFO_SMARTCAST!>x<!>, z!!)
|
||||
} while (bar())
|
||||
// w is not null because of w!!
|
||||
<!DEBUG_INFO_SMARTCAST!>w<!>.length()
|
||||
// z is nullable despite of z!!
|
||||
z<!UNSAFE_CALL!>.<!>length()
|
||||
// x is null because of the break
|
||||
return x<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
public fun foo(/*0*/ x: kotlin.String?, /*1*/ z: kotlin.String?, /*2*/ w: kotlin.String?): kotlin.Int
|
||||
internal fun gav(/*0*/ w: kotlin.String, /*1*/ arg: kotlin.Any, /*2*/ z: kotlin.String): kotlin.String
|
||||
@@ -0,0 +1,13 @@
|
||||
fun bar(): Boolean { return true }
|
||||
|
||||
fun gav(arg: Any, z: String): String { return if (arg is String) <!DEBUG_INFO_SMARTCAST!>arg<!> else z }
|
||||
|
||||
public fun foo(x: String?, z: String?): Int {
|
||||
do {
|
||||
gav(if (x == null) break else <!DEBUG_INFO_SMARTCAST!>x<!>, z!!)
|
||||
} while (bar())
|
||||
// z is nullable despite of z!!
|
||||
z<!UNSAFE_CALL!>.<!>length()
|
||||
// x is null because of the break
|
||||
return x<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
public fun foo(/*0*/ x: kotlin.String?, /*1*/ z: kotlin.String?): kotlin.Int
|
||||
internal fun gav(/*0*/ arg: kotlin.Any, /*1*/ z: kotlin.String): kotlin.String
|
||||
@@ -0,0 +1,14 @@
|
||||
fun bar(): Boolean { return true }
|
||||
|
||||
fun gav(arg: Any): String { return if (arg is String) <!DEBUG_INFO_SMARTCAST!>arg<!> else "" }
|
||||
|
||||
public fun foo(x: String?): Int {
|
||||
var y: Any
|
||||
do {
|
||||
y = ""
|
||||
y = gav(if (x == null) break else <!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
} while (bar())
|
||||
y.hashCode()
|
||||
// x is null because of the break
|
||||
return x<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
public fun foo(/*0*/ x: kotlin.String?): kotlin.Int
|
||||
internal fun gav(/*0*/ arg: kotlin.Any): kotlin.String
|
||||
@@ -0,0 +1,13 @@
|
||||
fun bar(): Boolean { return true }
|
||||
|
||||
fun gav(z: String, arg: Any): String { return if (arg is String) <!DEBUG_INFO_SMARTCAST!>arg<!> else z }
|
||||
|
||||
public fun foo(x: String?, z: String?): Int {
|
||||
do {
|
||||
gav(z!!, if (x == null) break else <!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
} while (bar())
|
||||
// z is not null because of z!!
|
||||
<!DEBUG_INFO_SMARTCAST!>z<!>.length()
|
||||
// x is null because of the break
|
||||
return x<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
public fun foo(/*0*/ x: kotlin.String?, /*1*/ z: kotlin.String?): kotlin.Int
|
||||
internal fun gav(/*0*/ z: kotlin.String, /*1*/ arg: kotlin.Any): kotlin.String
|
||||
@@ -0,0 +1,16 @@
|
||||
fun bar(): Boolean { return true }
|
||||
|
||||
fun gav(z: String, w: String, arg: Any): String
|
||||
{ return if (arg is String) <!DEBUG_INFO_SMARTCAST!>arg<!> else if (z != "") z else w }
|
||||
|
||||
public fun foo(x: String?, z: String?, w: String?): Int {
|
||||
do {
|
||||
gav(z!!, w!!, if (x == null) break else <!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||
} while (bar())
|
||||
// w is not null because of w!!
|
||||
<!DEBUG_INFO_SMARTCAST!>w<!>.length()
|
||||
// z is not null because of z!!
|
||||
<!DEBUG_INFO_SMARTCAST!>z<!>.length()
|
||||
// x is null because of the break
|
||||
return x<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
public fun foo(/*0*/ x: kotlin.String?, /*1*/ z: kotlin.String?, /*2*/ w: kotlin.String?): kotlin.Int
|
||||
internal fun gav(/*0*/ z: kotlin.String, /*1*/ w: kotlin.String, /*2*/ arg: kotlin.Any): kotlin.String
|
||||
@@ -0,0 +1,14 @@
|
||||
fun foo(x: String): String? = x
|
||||
|
||||
fun calc(x: String?, y: String?): Int {
|
||||
do {
|
||||
// Smart cast because of x!! in receiver
|
||||
foo(x!!)?.subSequence(0, if (<!DEBUG_INFO_SMARTCAST!>x<!>.length() > 0) 5 else break)
|
||||
y!!.length()
|
||||
// x is not null in condition but we do not see it yet
|
||||
} while (x<!UNSAFE_CALL!>.<!>length() > 0)
|
||||
// y is nullable because of break
|
||||
y<!UNSAFE_CALL!>.<!>length()
|
||||
// x is not null, at least in theory
|
||||
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal fun calc(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
|
||||
internal fun foo(/*0*/ x: kotlin.String): kotlin.String?
|
||||
@@ -0,0 +1,12 @@
|
||||
fun foo(x: String): String? = x
|
||||
|
||||
fun calc(x: String?): Int {
|
||||
do {
|
||||
// Smart cast because of x!! in receiver
|
||||
foo(x!!)?.subSequence(0, <!DEBUG_INFO_SMARTCAST!>x<!>.length())
|
||||
// Smart cast because of x!! in receiver
|
||||
if (<!DEBUG_INFO_SMARTCAST!>x<!>.length() == 0) break
|
||||
} while (true)
|
||||
// Here x is also not null
|
||||
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal fun calc(/*0*/ x: kotlin.String?): kotlin.Int
|
||||
internal fun foo(/*0*/ x: kotlin.String): kotlin.String?
|
||||
@@ -11159,6 +11159,36 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callBreakBetweenInsideDoWhile.kt")
|
||||
public void testCallBreakBetweenInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callBreakFirstInsideDoWhile.kt")
|
||||
public void testCallBreakFirstInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callBreakInsideDoWhile.kt")
|
||||
public void testCallBreakInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callBreakSecondInsideDoWhile.kt")
|
||||
public void testCallBreakSecondInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callBreakThirdInsideDoWhile.kt")
|
||||
public void testCallBreakThirdInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("doWhile.kt")
|
||||
public void testDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhile.kt");
|
||||
@@ -11339,6 +11369,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallBreakInsideDoWhile.kt")
|
||||
public void testSafeCallBreakInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallInsideDoWhile.kt")
|
||||
public void testSafeCallInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("useInsideDoWhile.kt")
|
||||
public void testUseInsideDoWhile() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.kt");
|
||||
|
||||
Reference in New Issue
Block a user