From fd789cb68e3625d9336ab8160317425c77da78af Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 21 Apr 2015 14:42:11 +0300 Subject: [PATCH] Correct behaviour for break / continue inside function call arguments, including multiple ones. Additional tests. --- .../resolve/calls/CallExpressionResolver.java | 17 +++++++- .../loops/callBreakBetweenInsideDoWhile.kt | 16 +++++++ .../loops/callBreakBetweenInsideDoWhile.txt | 5 +++ .../loops/callBreakFirstInsideDoWhile.kt | 13 ++++++ .../loops/callBreakFirstInsideDoWhile.txt | 5 +++ .../loops/callBreakInsideDoWhile.kt | 14 +++++++ .../loops/callBreakInsideDoWhile.txt | 5 +++ .../loops/callBreakSecondInsideDoWhile.kt | 13 ++++++ .../loops/callBreakSecondInsideDoWhile.txt | 5 +++ .../loops/callBreakThirdInsideDoWhile.kt | 16 +++++++ .../loops/callBreakThirdInsideDoWhile.txt | 5 +++ .../loops/safeCallBreakInsideDoWhile.kt | 14 +++++++ .../loops/safeCallBreakInsideDoWhile.txt | 4 ++ .../smartCasts/loops/safeCallInsideDoWhile.kt | 12 ++++++ .../loops/safeCallInsideDoWhile.txt | 4 ++ .../checkers/JetDiagnosticsTestGenerated.java | 42 +++++++++++++++++++ 16 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java index ed7fa246638..3a3b2b85565 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java @@ -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 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(); diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.kt new file mode 100644 index 00000000000..aedf50d9a19 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.kt @@ -0,0 +1,16 @@ +fun bar(): Boolean { return true } + +fun gav(w: String, arg: Any, z: String): String +{ return if (arg is String) 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 x, z!!) + } while (bar()) + // w is not null because of w!! + w.length() + // z is nullable despite of z!! + z.length() + // x is null because of the break + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.txt new file mode 100644 index 00000000000..f17aff362e1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakBetweenInsideDoWhile.txt @@ -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 diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.kt new file mode 100644 index 00000000000..117c46815b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.kt @@ -0,0 +1,13 @@ +fun bar(): Boolean { return true } + +fun gav(arg: Any, z: String): String { return if (arg is String) arg else z } + +public fun foo(x: String?, z: String?): Int { + do { + gav(if (x == null) break else x, z!!) + } while (bar()) + // z is nullable despite of z!! + z.length() + // x is null because of the break + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.txt new file mode 100644 index 00000000000..56cb774adcf --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakFirstInsideDoWhile.txt @@ -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 diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.kt new file mode 100644 index 00000000000..7299be8a01a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.kt @@ -0,0 +1,14 @@ +fun bar(): Boolean { return true } + +fun gav(arg: Any): String { return if (arg is String) arg else "" } + +public fun foo(x: String?): Int { + var y: Any + do { + y = "" + y = gav(if (x == null) break else x) + } while (bar()) + y.hashCode() + // x is null because of the break + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.txt new file mode 100644 index 00000000000..5f3d6e6eae3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakInsideDoWhile.txt @@ -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 diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.kt new file mode 100644 index 00000000000..6f96d3e035e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.kt @@ -0,0 +1,13 @@ +fun bar(): Boolean { return true } + +fun gav(z: String, arg: Any): String { return if (arg is String) arg else z } + +public fun foo(x: String?, z: String?): Int { + do { + gav(z!!, if (x == null) break else x) + } while (bar()) + // z is not null because of z!! + z.length() + // x is null because of the break + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.txt new file mode 100644 index 00000000000..666fd7df335 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakSecondInsideDoWhile.txt @@ -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 diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.kt new file mode 100644 index 00000000000..4801b8e1c6c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.kt @@ -0,0 +1,16 @@ +fun bar(): Boolean { return true } + +fun gav(z: String, w: String, arg: Any): String +{ return if (arg is String) 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 x) + } while (bar()) + // w is not null because of w!! + w.length() + // z is not null because of z!! + z.length() + // x is null because of the break + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.txt new file mode 100644 index 00000000000..045f7e9353d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/callBreakThirdInsideDoWhile.txt @@ -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 diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.kt new file mode 100644 index 00000000000..506c9f9cc42 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.kt @@ -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 (x.length() > 0) 5 else break) + y!!.length() + // x is not null in condition but we do not see it yet + } while (x.length() > 0) + // y is nullable because of break + y.length() + // x is not null, at least in theory + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.txt new file mode 100644 index 00000000000..325ee25cac4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallBreakInsideDoWhile.txt @@ -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? diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.kt new file mode 100644 index 00000000000..3ef931c764d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.kt @@ -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, x.length()) + // Smart cast because of x!! in receiver + if (x.length() == 0) break + } while (true) + // Here x is also not null + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.txt new file mode 100644 index 00000000000..17752c6af2e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/safeCallInsideDoWhile.txt @@ -0,0 +1,4 @@ +package + +internal fun calc(/*0*/ x: kotlin.String?): kotlin.Int +internal fun foo(/*0*/ x: kotlin.String): kotlin.String? diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index f09cca0be9e..c535aa81ae0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -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");