From d808f3e4fbf47079d076b3b304ca8b228e2e7ec8 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 10 Apr 2020 14:48:43 +0300 Subject: [PATCH] [FIR-TEST] Add tests for contracts resolve --- .../good/callsInPlace/atLeastOnce.dot | 88 ++++++++++++++++++ .../good/callsInPlace/atLeastOnce.kt | 33 +++++++ .../good/callsInPlace/atLeastOnce.txt | 35 +++++++ .../good/callsInPlace/atMostOnce.dot | 86 +++++++++++++++++ .../good/callsInPlace/atMostOnce.kt | 33 +++++++ .../good/callsInPlace/atMostOnce.txt | 35 +++++++ .../good/callsInPlace/exactlyOnce.dot | 92 +++++++++++++++++++ .../good/callsInPlace/exactlyOnce.kt | 33 +++++++ .../good/callsInPlace/exactlyOnce.txt | 35 +++++++ .../fromSource/good/callsInPlace/unknown.dot | 88 ++++++++++++++++++ .../fromSource/good/callsInPlace/unknown.kt | 33 +++++++ .../fromSource/good/callsInPlace/unknown.txt | 35 +++++++ .../good/returnsImplies/booleanOperators.kt | 69 ++++++++++++++ .../good/returnsImplies/booleanOperators.txt | 89 ++++++++++++++++++ .../fromSource/good/returnsImplies/eqNotEq.kt | 31 +++++++ .../good/returnsImplies/eqNotEq.txt | 40 ++++++++ .../good/returnsImplies/receivers.kt | 43 +++++++++ .../good/returnsImplies/receivers.txt | 48 ++++++++++ .../good/returnsImplies/typePredicate.kt | 17 ++++ .../good/returnsImplies/typePredicate.txt | 22 +++++ ...FirDiagnosticsWithStdlibTestGenerated.java | 92 +++++++++++++++++++ 21 files changed, 1077 insertions(+) create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.dot create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.txt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.dot create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.txt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.dot create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.txt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.dot create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.txt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.txt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.txt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.txt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.txt diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.dot new file mode 100644 index 00000000000..258d057ef48 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.dot @@ -0,0 +1,88 @@ +digraph atLeastOnce_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function inlineRun" style="filled" fillcolor=red]; + 2 [label="Function call: R|/block|.R|FakeOverride|()"]; + 1 [label="Exit function inlineRun" style="filled" fillcolor=red]; + } + 0 -> {2}; + 2 -> {1}; + + subgraph cluster_1 { + color=red + 3 [label="Enter function myRun" style="filled" fillcolor=red]; + 5 [label="Function call: R|/block|.R|FakeOverride|()"]; + 4 [label="Exit function myRun" style="filled" fillcolor=red]; + } + 3 -> {5}; + 5 -> {4}; + + subgraph cluster_2 { + color=red + 6 [label="Enter function test_1" style="filled" fillcolor=red]; + 8 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 9 [label="Postponed enter to lambda"]; + subgraph cluster_3 { + color=blue + 14 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 16 [label="Const: Int(1)"]; + 17 [label="Assignment: R|/x|"]; + 15 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 10 [label="Postponed exit from lambda"]; + 11 [label="Function call: R|/inlineRun|(...)"]; + 12 [label="Access variable R|/x|"]; + 13 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 7 [label="Exit function test_1" style="filled" fillcolor=red]; + } + 6 -> {8}; + 8 -> {9}; + 9 -> {14}; + 9 -> {10} [color=red]; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {7}; + 14 -> {16}; + 15 -> {14}; + 15 -> {10} [color=green]; + 16 -> {17}; + 17 -> {15}; + + subgraph cluster_4 { + color=red + 18 [label="Enter function test_2" style="filled" fillcolor=red]; + 20 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 21 [label="Postponed enter to lambda"]; + subgraph cluster_5 { + color=blue + 26 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 28 [label="Const: Int(1)"]; + 29 [label="Assignment: R|/x|"]; + 27 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 22 [label="Postponed exit from lambda"]; + 23 [label="Function call: R|/myRun|(...)"]; + 24 [label="Access variable R|/x|"]; + 25 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 19 [label="Exit function test_2" style="filled" fillcolor=red]; + } + 18 -> {20}; + 20 -> {21}; + 21 -> {26}; + 21 -> {22} [color=red]; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {19}; + 26 -> {28}; + 27 -> {26}; + 27 -> {22} [color=green]; + 28 -> {29}; + 29 -> {27}; + +} diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt new file mode 100644 index 00000000000..c50cf9a2b6f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt @@ -0,0 +1,33 @@ +// !DUMP_CFG + +import kotlin.contracts.* + +inline fun inlineRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.AT_LEAST_ONCE) + } + block() +} + +fun myRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.AT_LEAST_ONCE) + } + block() +} + +fun test_1() { + val x: Int + inlineRun { + x = 1 + } + x.inc() +} + +fun test_2() { + val x: Int + myRun { + x = 1 + } + x.inc() +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.txt new file mode 100644 index 00000000000..a081a88afda --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.txt @@ -0,0 +1,35 @@ +FILE: atLeastOnce.kt + public final inline fun inlineRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, AT_LEAST_ONCE) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun myRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, AT_LEAST_ONCE) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun test_1(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/inlineRun|( = inlineRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } + public final fun test_2(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/myRun|( = myRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.dot new file mode 100644 index 00000000000..c46ddf15601 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.dot @@ -0,0 +1,86 @@ +digraph atMostOnce_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function inlineRun" style="filled" fillcolor=red]; + 2 [label="Function call: R|/block|.R|FakeOverride|()"]; + 1 [label="Exit function inlineRun" style="filled" fillcolor=red]; + } + 0 -> {2}; + 2 -> {1}; + + subgraph cluster_1 { + color=red + 3 [label="Enter function myRun" style="filled" fillcolor=red]; + 5 [label="Function call: R|/block|.R|FakeOverride|()"]; + 4 [label="Exit function myRun" style="filled" fillcolor=red]; + } + 3 -> {5}; + 5 -> {4}; + + subgraph cluster_2 { + color=red + 6 [label="Enter function test_1" style="filled" fillcolor=red]; + 8 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 9 [label="Postponed enter to lambda"]; + subgraph cluster_3 { + color=blue + 14 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 16 [label="Const: Int(1)"]; + 17 [label="Assignment: R|/x|"]; + 15 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 10 [label="Postponed exit from lambda"]; + 11 [label="Function call: R|/inlineRun|(...)"]; + 12 [label="Access variable R|/x|"]; + 13 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 7 [label="Exit function test_1" style="filled" fillcolor=red]; + } + 6 -> {8}; + 8 -> {9}; + 9 -> {14}; + 9 -> {10} [color=red]; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {7}; + 14 -> {15 16}; + 15 -> {10} [color=green]; + 16 -> {17}; + 17 -> {15}; + + subgraph cluster_4 { + color=red + 18 [label="Enter function test_2" style="filled" fillcolor=red]; + 20 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 21 [label="Postponed enter to lambda"]; + subgraph cluster_5 { + color=blue + 26 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 28 [label="Const: Int(1)"]; + 29 [label="Assignment: R|/x|"]; + 27 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 22 [label="Postponed exit from lambda"]; + 23 [label="Function call: R|/myRun|(...)"]; + 24 [label="Access variable R|/x|"]; + 25 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 19 [label="Exit function test_2" style="filled" fillcolor=red]; + } + 18 -> {20}; + 20 -> {21}; + 21 -> {26}; + 21 -> {22} [color=red]; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {19}; + 26 -> {27 28}; + 27 -> {22} [color=green]; + 28 -> {29}; + 29 -> {27}; + +} diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.kt new file mode 100644 index 00000000000..e2a316b3117 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.kt @@ -0,0 +1,33 @@ +// !DUMP_CFG + +import kotlin.contracts.* + +inline fun inlineRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.AT_MOST_ONCE) + } + block() +} + +fun myRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.AT_MOST_ONCE) + } + block() +} + +fun test_1() { + val x: Int + inlineRun { + x = 1 + } + x.inc() +} + +fun test_2() { + val x: Int + myRun { + x = 1 + } + x.inc() +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.txt new file mode 100644 index 00000000000..fe7c93827c6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.txt @@ -0,0 +1,35 @@ +FILE: atMostOnce.kt + public final inline fun inlineRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, AT_MOST_ONCE) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun myRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, AT_MOST_ONCE) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun test_1(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/inlineRun|( = inlineRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } + public final fun test_2(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/myRun|( = myRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.dot new file mode 100644 index 00000000000..4456a0e4ad6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.dot @@ -0,0 +1,92 @@ +digraph exactlyOnce_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function inlineRun" style="filled" fillcolor=red]; + 2 [label="Function call: R|/block|.R|FakeOverride|()"]; + 1 [label="Exit function inlineRun" style="filled" fillcolor=red]; + } + 0 -> {2}; + 2 -> {1}; + + subgraph cluster_1 { + color=red + 3 [label="Enter function myRun" style="filled" fillcolor=red]; + 5 [label="Function call: R|/block|.R|FakeOverride|()"]; + 4 [label="Exit function myRun" style="filled" fillcolor=red]; + } + 3 -> {5}; + 5 -> {4}; + + subgraph cluster_2 { + color=red + 6 [label="Enter function test_1" style="filled" fillcolor=red]; + 8 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 9 [label="Postponed enter to lambda"]; + subgraph cluster_3 { + color=blue + 15 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 17 [label="Const: Int(1)"]; + 18 [label="Assignment: R|/x|"]; + 16 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 12 [label="Call arguments union" style="filled" fillcolor=yellow]; + 10 [label="Postponed exit from lambda"]; + 11 [label="Function call: R|/inlineRun|(...)"]; + 13 [label="Access variable R|/x|"]; + 14 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 7 [label="Exit function test_1" style="filled" fillcolor=red]; + } + 6 -> {8}; + 8 -> {9}; + 9 -> {15}; + 9 -> {10} [color=red]; + 10 -> {11} [color=green]; + 11 -> {13}; + 12 -> {11} [color=red]; + 13 -> {14}; + 14 -> {7}; + 15 -> {17}; + 16 -> {10} [color=green]; + 16 -> {12} [color=red]; + 17 -> {18}; + 18 -> {16}; + + subgraph cluster_4 { + color=red + 19 [label="Enter function test_2" style="filled" fillcolor=red]; + 21 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 22 [label="Postponed enter to lambda"]; + subgraph cluster_5 { + color=blue + 28 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 30 [label="Const: Int(1)"]; + 31 [label="Assignment: R|/x|"]; + 29 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 25 [label="Call arguments union" style="filled" fillcolor=yellow]; + 23 [label="Postponed exit from lambda"]; + 24 [label="Function call: R|/myRun|(...)"]; + 26 [label="Access variable R|/x|"]; + 27 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 20 [label="Exit function test_2" style="filled" fillcolor=red]; + } + 19 -> {21}; + 21 -> {22}; + 22 -> {28}; + 22 -> {23} [color=red]; + 23 -> {24} [color=green]; + 24 -> {26}; + 25 -> {24} [color=red]; + 26 -> {27}; + 27 -> {20}; + 28 -> {30}; + 29 -> {23} [color=green]; + 29 -> {25} [color=red]; + 30 -> {31}; + 31 -> {29}; + +} diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.kt new file mode 100644 index 00000000000..bb304c78ecc --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.kt @@ -0,0 +1,33 @@ +// !DUMP_CFG + +import kotlin.contracts.* + +inline fun inlineRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +fun myRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +fun test_1() { + val x: Int + inlineRun { + x = 1 + } + x.inc() +} + +fun test_2() { + val x: Int + myRun { + x = 1 + } + x.inc() +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.txt new file mode 100644 index 00000000000..62d096cabec --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.txt @@ -0,0 +1,35 @@ +FILE: exactlyOnce.kt + public final inline fun inlineRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, EXACTLY_ONCE) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun myRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, EXACTLY_ONCE) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun test_1(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/inlineRun|( = inlineRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } + public final fun test_2(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/myRun|( = myRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.dot new file mode 100644 index 00000000000..f4f3027f6f4 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.dot @@ -0,0 +1,88 @@ +digraph unknown_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function inlineRun" style="filled" fillcolor=red]; + 2 [label="Function call: R|/block|.R|FakeOverride|()"]; + 1 [label="Exit function inlineRun" style="filled" fillcolor=red]; + } + 0 -> {2}; + 2 -> {1}; + + subgraph cluster_1 { + color=red + 3 [label="Enter function myRun" style="filled" fillcolor=red]; + 5 [label="Function call: R|/block|.R|FakeOverride|()"]; + 4 [label="Exit function myRun" style="filled" fillcolor=red]; + } + 3 -> {5}; + 5 -> {4}; + + subgraph cluster_2 { + color=red + 6 [label="Enter function test_1" style="filled" fillcolor=red]; + 8 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 9 [label="Postponed enter to lambda"]; + subgraph cluster_3 { + color=blue + 14 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 16 [label="Const: Int(1)"]; + 17 [label="Assignment: R|/x|"]; + 15 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 10 [label="Postponed exit from lambda"]; + 11 [label="Function call: R|/inlineRun|(...)"]; + 12 [label="Access variable R|/x|"]; + 13 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 7 [label="Exit function test_1" style="filled" fillcolor=red]; + } + 6 -> {8}; + 8 -> {9}; + 9 -> {14}; + 9 -> {10} [color=red]; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {7}; + 14 -> {15 16}; + 15 -> {14}; + 15 -> {10} [color=green]; + 16 -> {17}; + 17 -> {15}; + + subgraph cluster_4 { + color=red + 18 [label="Enter function test_2" style="filled" fillcolor=red]; + 20 [label="Variable declaration: lval x: R|kotlin/Int|"]; + 21 [label="Postponed enter to lambda"]; + subgraph cluster_5 { + color=blue + 26 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 28 [label="Const: Int(1)"]; + 29 [label="Assignment: R|/x|"]; + 27 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 22 [label="Postponed exit from lambda"]; + 23 [label="Function call: R|/myRun|(...)"]; + 24 [label="Access variable R|/x|"]; + 25 [label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 19 [label="Exit function test_2" style="filled" fillcolor=red]; + } + 18 -> {20}; + 20 -> {21}; + 21 -> {26}; + 21 -> {22} [color=red]; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {19}; + 26 -> {27 28}; + 27 -> {26}; + 27 -> {22} [color=green]; + 28 -> {29}; + 29 -> {27}; + +} diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt new file mode 100644 index 00000000000..517efc78886 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt @@ -0,0 +1,33 @@ +// !DUMP_CFG + +import kotlin.contracts.* + +inline fun inlineRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.UNKNOWN) + } + block() +} + +fun myRun(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.UNKNOWN) + } + block() +} + +fun test_1() { + val x: Int + inlineRun { + x = 1 + } + x.inc() +} + +fun test_2() { + val x: Int + myRun { + x = 1 + } + x.inc() +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.txt new file mode 100644 index 00000000000..26392791b5b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.txt @@ -0,0 +1,35 @@ +FILE: unknown.kt + public final inline fun inlineRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, UNKNOWN) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun myRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(block, UNKNOWN) + > + { + [StubStatement] + R|/block|.R|FakeOverride|() + } + public final fun test_1(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/inlineRun|( = inlineRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } + public final fun test_2(): R|kotlin/Unit| { + lval x: R|kotlin/Int| + R|/myRun|( = myRun@fun (): R|kotlin/Unit| { + R|/x| = Int(1) + } + ) + R|/x|.R|kotlin/Int.inc|() + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.kt new file mode 100644 index 00000000000..e310196c7fe --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.kt @@ -0,0 +1,69 @@ +import kotlin.contracts.* + +fun myRequire(b: Boolean) { + contract { + returns() implies (b) + } + if (!b) throw IllegalStateException() +} + +fun myRequireAnd(b1: Boolean, b2: Boolean) { + contract { + returns() implies (b1 && b2) + } + if (!(b1 && b2)) throw IllegalStateException() +} + +fun myRequireOr(b1: Boolean, b2: Boolean) { + contract { + returns() implies (b1 || b2) + } + if (!(b1 || b2)) throw IllegalStateException() +} + +fun myRequireNot(b: Boolean) { + contract { + returns() implies (!b) + } + if (b) throw IllegalStateException() +} + +// ---------------------------------------------------- + +interface A { + fun foo() +} + +interface B : A { + fun bar() +} + +interface C : A { + fun baz() +} + +// ---------------------------------------------------- + +fun test_1(x: Any) { + myRequire(x is A) + x.foo() +} + +fun test_2(x: Any) { + myRequireAnd(x is B, x is C) + x.foo() + x.bar() + x.baz() +} + +fun test_3(x: Any) { + myRequireOr(x is B, x is C) + x.foo() // OK + x.bar() // Error + x.baz() // Error +} + +fun test_4(x: Any) { + myRequireNot(x !is A) + x.foo() +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.txt new file mode 100644 index 00000000000..c227f6e84df --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.txt @@ -0,0 +1,89 @@ +FILE: booleanOperators.kt + public final fun myRequire(b: R|kotlin/Boolean|): R|kotlin/Unit| + [R|Contract description] + < + Returns(WILDCARD) -> b + > + { + [StubStatement] + when () { + R|/b|.R|kotlin/Boolean.not|() -> { + throw R|java/lang/IllegalStateException.IllegalStateException|() + } + } + + } + public final fun myRequireAnd(b1: R|kotlin/Boolean|, b2: R|kotlin/Boolean|): R|kotlin/Unit| + [R|Contract description] + < + Returns(WILDCARD) -> (b1) && (b2) + > + { + [StubStatement] + when () { + R|/b1| && R|/b2|.R|kotlin/Boolean.not|() -> { + throw R|java/lang/IllegalStateException.IllegalStateException|() + } + } + + } + public final fun myRequireOr(b1: R|kotlin/Boolean|, b2: R|kotlin/Boolean|): R|kotlin/Unit| + [R|Contract description] + < + Returns(WILDCARD) -> (b1) || (b2) + > + { + [StubStatement] + when () { + R|/b1| || R|/b2|.R|kotlin/Boolean.not|() -> { + throw R|java/lang/IllegalStateException.IllegalStateException|() + } + } + + } + public final fun myRequireNot(b: R|kotlin/Boolean|): R|kotlin/Unit| + [R|Contract description] + < + Returns(WILDCARD) -> (!)b + > + { + [StubStatement] + when () { + R|/b| -> { + throw R|java/lang/IllegalStateException.IllegalStateException|() + } + } + + } + public abstract interface A : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public abstract interface B : R|A| { + public abstract fun bar(): R|kotlin/Unit| + + } + public abstract interface C : R|A| { + public abstract fun baz(): R|kotlin/Unit| + + } + public final fun test_1(x: R|kotlin/Any|): R|kotlin/Unit| { + R|/myRequire|((R|/x| is R|A|)) + R|/x|.R|/A.foo|() + } + public final fun test_2(x: R|kotlin/Any|): R|kotlin/Unit| { + R|/myRequireAnd|((R|/x| is R|B|), (R|/x| is R|C|)) + R|/x|.R|/A.foo|() + R|/x|.R|/B.bar|() + R|/x|.R|/C.baz|() + } + public final fun test_3(x: R|kotlin/Any|): R|kotlin/Unit| { + R|/myRequireOr|((R|/x| is R|B|), (R|/x| is R|C|)) + R|/x|.R|/A.foo|() + R|/x|.#() + R|/x|.#() + } + public final fun test_4(x: R|kotlin/Any|): R|kotlin/Unit| { + R|/myRequireNot|((R|/x| !is R|A|)) + R|/x|.R|/A.foo|() + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt new file mode 100644 index 00000000000..af95ad9668c --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt @@ -0,0 +1,31 @@ +import kotlin.contracts.* + +fun checkNotNull(x: Any?) { + contract { + returns(true) implies (x != null) + returns(false) implies (x == null) + } + return x != null +} + +fun trickyRequireNotNull(x: Any?) { + contract { + returns() implies (!(x == null)) + } + if (x == null) { + throw IllegalStateException() + } +} + +fun test_1(x: String?) { + if (checkNotNull(x)) { + x.length // OK + } else { + x.length // Error + } +} + +fun test_2(x: String?) { + trickyRequireNotNull(x) + x.length +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.txt new file mode 100644 index 00000000000..56a85c143e3 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.txt @@ -0,0 +1,40 @@ +FILE: eqNotEq.kt + public final fun checkNotNull(x: R|kotlin/Any?|): R|kotlin/Unit| + [R|Contract description] + < + Returns(TRUE) -> x != null + Returns(FALSE) -> x == null + > + { + [StubStatement] + ^checkNotNull !=(R|/x|, Null(null)) + } + public final fun trickyRequireNotNull(x: R|kotlin/Any?|): R|kotlin/Unit| + [R|Contract description] + < + Returns(WILDCARD) -> !x == null + > + { + [StubStatement] + when () { + ==(R|/x|, Null(null)) -> { + throw R|java/lang/IllegalStateException.IllegalStateException|() + } + } + + } + public final fun test_1(x: R|kotlin/String?|): R|kotlin/Unit| { + when () { + R|/checkNotNull|(R|/x|) -> { + R|/x|.R|kotlin/String.length| + } + else -> { + R|/x|.# + } + } + + } + public final fun test_2(x: R|kotlin/String?|): R|kotlin/Unit| { + R|/trickyRequireNotNull|(R|/x|) + R|/x|.R|kotlin/String.length| + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.kt new file mode 100644 index 00000000000..0e12db35591 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.kt @@ -0,0 +1,43 @@ +import kotlin.contracts.* + +interface A + +fun A.foo() {} + +fun Any?.myRequireNotNull() { + contract { + returns() implies (this@myRequireNotNull != null) + } + if (this == null) throw IllegalStateException() +} + +fun test_1(x: A?) { + x.myRequireNotNull() + x.foo() +} + +fun test_2(x: A?) { + x.myRequireNotNull() + with(x) { + foo() + } +} + +fun test_3(x: A?) { + with(x) { + myRequireNotNull() + } + x.foo() +} + +fun test_4(x: A?) { + with(x) { + myRequireNotNull() + foo() + } +} + +fun A?.test_5() { + myRequireNotNull() + foo() +} diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.txt new file mode 100644 index 00000000000..b1f87fc1770 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.txt @@ -0,0 +1,48 @@ +FILE: receivers.kt + public abstract interface A : R|kotlin/Any| { + } + public final fun R|A|.foo(): R|kotlin/Unit| { + } + public final fun R|kotlin/Any?|.myRequireNotNull(): R|kotlin/Unit| + [R|Contract description] + < + Returns(WILDCARD) -> this != null + > + { + [StubStatement] + when () { + ==(this@R|/myRequireNotNull|, Null(null)) -> { + throw R|java/lang/IllegalStateException.IllegalStateException|() + } + } + + } + public final fun test_1(x: R|A?|): R|kotlin/Unit| { + R|/x|.R|/myRequireNotNull|() + R|/x|.R|/foo|() + } + public final fun test_2(x: R|A?|): R|kotlin/Unit| { + R|/x|.R|/myRequireNotNull|() + R|kotlin/with|(R|/x|, = with@fun R|A|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/foo|() + } + ) + } + public final fun test_3(x: R|A?|): R|kotlin/Unit| { + R|kotlin/with|(R|/x|, = with@fun R|A?|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/myRequireNotNull|() + } + ) + R|/x|.#() + } + public final fun test_4(x: R|A?|): R|kotlin/Unit| { + R|kotlin/with|(R|/x|, = with@fun R|A?|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/myRequireNotNull|() + this@R|special/anonymous|.R|/foo|() + } + ) + } + public final fun R|A?|.test_5(): R|kotlin/Unit| { + this@R|/test_5|.R|/myRequireNotNull|() + this@R|/test_5|.R|/foo|() + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.kt new file mode 100644 index 00000000000..817489339b2 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.kt @@ -0,0 +1,17 @@ +import kotlin.contracts.* + +fun checkIsString(x: Any) { + contract { + returns(true) implies (x is String) + returns(false) implies (x !is String) + } + return x is String +} + +fun test(x: Any) { + if (checkIsString(x)) { + x.length // OK + } else { + x.length // Error + } +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.txt new file mode 100644 index 00000000000..02ead41e79a --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.txt @@ -0,0 +1,22 @@ +FILE: typePredicate.kt + public final fun checkIsString(x: R|kotlin/Any|): R|kotlin/Unit| + [R|Contract description] + < + Returns(TRUE) -> x is kotlin/String + Returns(FALSE) -> x !is kotlin/String + > + { + [StubStatement] + ^checkIsString (R|/x| is R|kotlin/String|) + } + public final fun test(x: R|kotlin/Any|): R|kotlin/Unit| { + when () { + R|/checkIsString|(R|/x|) -> { + R|/x|.R|kotlin/String.length| + } + else -> { + R|/x|.# + } + } + + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java index c8c8fcfa9ae..3fb562af0bf 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java @@ -513,6 +513,98 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromLibrary/notIsNullOrEmpty.kt"); } } + + @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FromSource extends AbstractFirDiagnosticsWithStdlibTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInFromSource() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Good extends AbstractFirDiagnosticsWithStdlibTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInGood() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CallsInPlace extends AbstractFirDiagnosticsWithStdlibTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCallsInPlace() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @TestMetadata("atLeastOnce.kt") + public void testAtLeastOnce() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt"); + } + + @TestMetadata("atMostOnce.kt") + public void testAtMostOnce() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atMostOnce.kt"); + } + + @TestMetadata("exactlyOnce.kt") + public void testExactlyOnce() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/exactlyOnce.kt"); + } + + @TestMetadata("unknown.kt") + public void testUnknown() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt"); + } + } + + @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ReturnsImplies extends AbstractFirDiagnosticsWithStdlibTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInReturnsImplies() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @TestMetadata("booleanOperators.kt") + public void testBooleanOperators() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.kt"); + } + + @TestMetadata("eqNotEq.kt") + public void testEqNotEq() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt"); + } + + @TestMetadata("receivers.kt") + public void testReceivers() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.kt"); + } + + @TestMetadata("typePredicate.kt") + public void testTypePredicate() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.kt"); + } + } + } + } } @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates")