[FIR] Add ReturnsImplies effect analyzer

This commit is contained in:
Oleg Ivanov
2020-08-11 12:41:43 +03:00
parent 3454ae7ca4
commit 21b8679799
37 changed files with 1110 additions and 89 deletions
@@ -0,0 +1,35 @@
import kotlin.contracts.*
@OptIn(ExperimentalContracts::class)
fun foo(x: String?): Any? {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(null) implies (x != null)
}<!>
if (true) {
throw java.lang.IllegalArgumentException()
}
return x
}
@OptIn(ExperimentalContracts::class)
fun bar(x: String?): Any? {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x != null)
}<!>
if (x == null) {
return x
}
return x
}
@OptIn(ExperimentalContracts::class)
fun baz(x: String?): Any? {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(null) implies (x != null)
}<!>
return x
}
@@ -0,0 +1,40 @@
FILE: notNull.kt
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun foo(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NULL) -> x != null
>
{
[StubStatement]
when () {
Boolean(true) -> {
throw Q|java/lang|.R|java/lang/IllegalArgumentException.IllegalArgumentException|()
}
}
^foo R|<local>/x|
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun bar(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(WILDCARD) -> x != null
>
{
[StubStatement]
when () {
==(R|<local>/x|, Null(null)) -> {
^bar R|<local>/x|
}
}
^bar R|<local>/x|
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun baz(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NULL) -> x != null
>
{
[StubStatement]
^baz R|<local>/x|
}
@@ -0,0 +1,18 @@
import kotlin.contracts.*
@OptIn(ExperimentalContracts::class)
fun Any?.isNotNull(): Boolean {
contract {
returns(true) implies (this@isNotNull != null)
}
return this != null
}
@OptIn(ExperimentalContracts::class)
val Any?.isNotNull: Boolean
get() {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (this@isNotNull != null)
}<!>
return this@isNotNull != null
}
@@ -0,0 +1,20 @@
FILE: propertyGetter.kt
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun R|kotlin/Any?|.isNotNull(): R|kotlin/Boolean|
[R|Contract description]
<
Returns(TRUE) -> this != null
>
{
[StubStatement]
^isNotNull !=(this@R|/isNotNull|, Null(null))
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final val R|kotlin/Any?|.isNotNull: R|kotlin/Boolean|
public get(): R|kotlin/Boolean|
[R|Contract description]
<
Returns(TRUE) -> this != null
>
{
[StubStatement]
^ !=(this@R|/isNotNull|, Null(null))
}
@@ -0,0 +1,121 @@
import kotlin.contracts.*
@OptIn(ExperimentalContracts::class)
fun test1(x: String?): Any? {
contract {
returnsNotNull() implies (!(x == null))
}
return x
}
@OptIn(ExperimentalContracts::class)
fun test2(x: String?): Any? {
contract {
returnsNotNull() implies (x is String && x != null)
}
return x
}
@OptIn(ExperimentalContracts::class)
fun test3(x: String?): Any? {
contract {
returnsNotNull() implies (x is String? || x is Any?)
}
return x
}
@OptIn(ExperimentalContracts::class)
fun test4(x: String?, y: String?): Any? {
contract {
returns(true) implies (x != null && y != null)
}
return x != null && y != null
}
@OptIn(ExperimentalContracts::class)
fun test5(x: Any?): Any? {
contract {
returns(true) implies (x != null || x is Any)
}
return x != null
}
@OptIn(ExperimentalContracts::class)
fun test6(x: Any?): Any? {
contract {
returns(true) implies (x is String? && x != null)
}
return x is String
}
@OptIn(ExperimentalContracts::class)
fun test7(x: Any?): Any? {
contract {
returns(true) implies (x is String? && x != null || x is Int)
}
return x is String
}
@OptIn(ExperimentalContracts::class)
fun test8(x: Any?): Any? {
contract {
returns(true) implies (x is String || x is Int)
}
return x is String || x is Int
}
@OptIn(ExperimentalContracts::class)
fun test9(x: Any?): Any? {
contract {
returns(true) implies (x is String || x is Int)
}
if(x is String){
return true
}
return x is Int
}
@OptIn(ExperimentalContracts::class)
fun test10(x: Any?): Any? {
contract {
returns(true) implies (x is Comparable<*> || x is CharSequence)
}
return x is String
}
@OptIn(ExperimentalContracts::class)
fun test11(x: Any?): Any? {
contract {
returns(true) implies (x is Comparable<*> && x is CharSequence)
}
return x is String
}
@OptIn(ExperimentalContracts::class)
fun test12(x: Any?): Any? {
contract {
returns(true) implies (x is Comparable<*> && (x is CharSequence || x is Number))
}
return x is String || x is Int
}
@OptIn(ExperimentalContracts::class)
fun test13(x: Any?): Any? {
contract {
returns(true) implies (!(x !is Comparable<*> || (x !is CharSequence && !(x is Number))))
}
return x is String || x is Int
}
@@ -0,0 +1,118 @@
FILE: conditionLogic.kt
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test1(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NOT_NULL) -> !x == null
>
{
[StubStatement]
^test1 R|<local>/x|
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test2(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
>
{
[StubStatement]
^test2 R|<local>/x|
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test3(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NOT_NULL) -> x is kotlin/String? || x is kotlin/Any?
>
{
[StubStatement]
^test3 R|<local>/x|
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test4(x: R|kotlin/String?|, y: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x != null && y != null
>
{
[StubStatement]
^test4 !=(R|<local>/x|, Null(null)) && !=(R|<local>/y|, Null(null))
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test5(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x != null || x is kotlin/Any
>
{
[StubStatement]
^test5 !=(R|<local>/x|, Null(null))
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test6(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
>
{
[StubStatement]
^test6 (R|<local>/x| is R|kotlin/String|)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test7(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
>
{
[StubStatement]
^test7 (R|<local>/x| is R|kotlin/String|)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test8(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x is kotlin/String || x is kotlin/Int
>
{
[StubStatement]
^test8 (R|<local>/x| is R|kotlin/String|) || (R|<local>/x| is R|kotlin/Int|)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test9(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x is kotlin/String || x is kotlin/Int
>
{
[StubStatement]
when () {
(R|<local>/x| is R|kotlin/String|) -> {
^test9 Boolean(true)
}
}
^test9 (R|<local>/x| is R|kotlin/Int|)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test10(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x is kotlin/Comparable<*> || x is kotlin/CharSequence
>
{
[StubStatement]
^test10 (R|<local>/x| is R|kotlin/String|)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test11(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
>
{
[StubStatement]
^test11 (R|<local>/x| is R|kotlin/String|)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test12(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
>
{
[StubStatement]
^test12 (R|<local>/x| is R|kotlin/String|) || (R|<local>/x| is R|kotlin/Int|)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test13(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
>
{
[StubStatement]
^test13 (R|<local>/x| is R|kotlin/String|) || (R|<local>/x| is R|kotlin/Int|)
}
@@ -0,0 +1,57 @@
import kotlin.contracts.*
@OptIn(ExperimentalContracts::class)
fun wrongFalse(x: String?): Boolean {
contract {
returns(false) implies (x != null)
}
return true
}
@OptIn(ExperimentalContracts::class)
fun wrongTrue(x: String?): Boolean {
contract {
returns(true) implies (x != null)
}
return false
}
@OptIn(ExperimentalContracts::class)
fun nullableConstant(x: String?): Any? {
contract {
returns(null) implies (x != null)
}
return 1
}
fun string() : String = ""
@OptIn(ExperimentalContracts::class)
fun nullableReturn(x: String?): Any? {
contract {
returns(null) implies (x != null)
}
return string()
}
@OptIn(ExperimentalContracts::class)
fun returnsNull(x: String?): Any? {
contract {
returnsNotNull() implies (x != null)
}
return null
}
@OptIn(ExperimentalContracts::class)
fun wrongReturnType(x: String?): Any? {
contract {
returns(true) implies (x != null)
}
return "true"
}
@@ -0,0 +1,58 @@
FILE: inapplicable.kt
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun wrongFalse(x: R|kotlin/String?|): R|kotlin/Boolean|
[R|Contract description]
<
Returns(FALSE) -> x != null
>
{
[StubStatement]
^wrongFalse Boolean(true)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun wrongTrue(x: R|kotlin/String?|): R|kotlin/Boolean|
[R|Contract description]
<
Returns(TRUE) -> x != null
>
{
[StubStatement]
^wrongTrue Boolean(false)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun nullableConstant(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NULL) -> x != null
>
{
[StubStatement]
^nullableConstant Int(1)
}
public final fun string(): R|kotlin/String| {
^string String()
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun nullableReturn(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NULL) -> x != null
>
{
[StubStatement]
^nullableReturn R|/string|()
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun returnsNull(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NOT_NULL) -> x != null
>
{
[StubStatement]
^returnsNull Null(null)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun wrongReturnType(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x != null
>
{
[StubStatement]
^wrongReturnType String(true)
}
@@ -0,0 +1,56 @@
import kotlin.contracts.*
@OptIn(ExperimentalContracts::class)
fun test1(x: String?): Boolean {
contract {
returns(false) implies (x != null)
}
return x == null
}
@OptIn(ExperimentalContracts::class)
fun test2(x: String?): Boolean {
contract {
returns(true) implies (x != null)
}
return x != null
}
@OptIn(ExperimentalContracts::class)
fun test3(x: String?): Any? {
contract {
returns(true) implies (x != null)
}
if(x != null){
return true
}
return false
}
@OptIn(ExperimentalContracts::class)
fun test4(x: String?): Any? {
contract {
returns(true) implies (x != null)
}
val y = x
if(y != null){
return true
}
return false
}
@OptIn(ExperimentalContracts::class)
fun test5(x: String?): Any? {
contract {
returns(true) implies (x != null)
}
return test2(x)
}
@@ -0,0 +1,59 @@
FILE: notNull.kt
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test1(x: R|kotlin/String?|): R|kotlin/Boolean|
[R|Contract description]
<
Returns(FALSE) -> x != null
>
{
[StubStatement]
^test1 ==(R|<local>/x|, Null(null))
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test2(x: R|kotlin/String?|): R|kotlin/Boolean|
[R|Contract description]
<
Returns(TRUE) -> x != null
>
{
[StubStatement]
^test2 !=(R|<local>/x|, Null(null))
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test3(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x != null
>
{
[StubStatement]
when () {
!=(R|<local>/x|, Null(null)) -> {
^test3 Boolean(true)
}
}
^test3 Boolean(false)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test4(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x != null
>
{
[StubStatement]
lval y: R|kotlin/String?| = R|<local>/x|
when () {
!=(R|<local>/y|, Null(null)) -> {
^test4 Boolean(true)
}
}
^test4 Boolean(false)
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test5(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x != null
>
{
[StubStatement]
^test5 R|/test2|(R|<local>/x|)
}
@@ -6,16 +6,16 @@ interface A {
var Any?.isNotNull: Boolean
get() {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (this@isNotNull != null)
}
}<!>
return this != null
}
set(value) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (this@isNotNull != null)
require(this != null)
}
}<!>
}
fun test_1(a: A?) {
@@ -0,0 +1,19 @@
import kotlin.contracts.*
@OptIn(ExperimentalContracts::class)
fun test1(x: String?): Int? {
contract {
returnsNotNull() implies (x != null)
}
return x?.length
}
@OptIn(ExperimentalContracts::class)
fun test2(x: String?): Int? {
<!WRONG_IMPLIES_CONDITION!>contract {
returnsNotNull() implies (x is Boolean)
}<!>
return x?.length
}
@@ -0,0 +1,19 @@
FILE: safeCall.kt
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test1(x: R|kotlin/String?|): R|kotlin/Int?|
[R|Contract description]
<
Returns(NOT_NULL) -> x != null
>
{
[StubStatement]
^test1 R|<local>/x|?.{ $subj$.R|kotlin/String.length| }
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test2(x: R|kotlin/String?|): R|kotlin/Int?|
[R|Contract description]
<
Returns(NOT_NULL) -> x is kotlin/Boolean
>
{
[StubStatement]
^test2 R|<local>/x|?.{ $subj$.R|kotlin/String.length| }
}
@@ -0,0 +1,39 @@
import kotlin.contracts.*
@OptIn(ExperimentalContracts::class)
fun test1(x: String?): Any? {
contract {
returnsNotNull() implies (x != null)
}
return x
}
@OptIn(ExperimentalContracts::class)
fun test2(x: String?): Any? {
contract {
returns(true) implies (x != null)
}
return if(x != null) true else false
}
@OptIn(ExperimentalContracts::class)
fun test3(x: Any?): Any? {
contract {
returnsNotNull() implies (x != null)
}
return if(true) x else null
}
@OptIn(ExperimentalContracts::class)
fun test4(x: Any?): Any? {
contract {
returnsNotNull() implies (x != null)
}
return if(x != null) {
if (true) x else false
} else {
null
}
}
@@ -0,0 +1,69 @@
FILE: trickyCases.kt
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test1(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NOT_NULL) -> x != null
>
{
[StubStatement]
^test1 R|<local>/x|
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test2(x: R|kotlin/String?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(TRUE) -> x != null
>
{
[StubStatement]
^test2 when () {
!=(R|<local>/x|, Null(null)) -> {
Boolean(true)
}
else -> {
Boolean(false)
}
}
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test3(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NOT_NULL) -> x != null
>
{
[StubStatement]
^test3 when () {
Boolean(true) -> {
R|<local>/x|
}
else -> {
Null(null)
}
}
}
@R|kotlin/OptIn|(vararg(<getClass>(Q|kotlin/contracts/ExperimentalContracts|))) public final fun test4(x: R|kotlin/Any?|): R|kotlin/Any?|
[R|Contract description]
<
Returns(NOT_NULL) -> x != null
>
{
[StubStatement]
^test4 when () {
!=(R|<local>/x|, Null(null)) -> {
when () {
Boolean(true) -> {
R|<local>/x|
}
else -> {
Boolean(false)
}
}
}
else -> {
Null(null)
}
}
}
@@ -590,6 +590,29 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/callsInPlace/toLocalVariables.kt");
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/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/bad/returnsImplies"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("notNull.kt")
public void testNotNull() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/returnsImplies/notNull.kt");
}
@TestMetadata("propertyGetter.kt")
public void testPropertyGetter() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/returnsImplies/propertyGetter.kt");
}
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good")
@@ -674,16 +697,31 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/booleanOperators.kt");
}
@TestMetadata("conditionLogic.kt")
public void testConditionLogic() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/conditionLogic.kt");
}
@TestMetadata("eqNotEq.kt")
public void testEqNotEq() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt");
}
@TestMetadata("inapplicable.kt")
public void testInapplicable() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/inapplicable.kt");
}
@TestMetadata("namedArguments.kt")
public void testNamedArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/namedArguments.kt");
}
@TestMetadata("notNull.kt")
public void testNotNull() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/notNull.kt");
}
@TestMetadata("propertyAccessors.kt")
public void testPropertyAccessors() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt");
@@ -694,6 +732,16 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.kt");
}
@TestMetadata("safeCall.kt")
public void testSafeCall() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/safeCall.kt");
}
@TestMetadata("trickyCases.kt")
public void testTrickyCases() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/trickyCases.kt");
}
@TestMetadata("typePredicate.kt")
public void testTypePredicate() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/typePredicate.kt");
@@ -0,0 +1,212 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.cfa
import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.dfa.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.BlockExitNode
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.JumpNode
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
import org.jetbrains.kotlin.utils.addIfNotNull
object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) {
val function = graph.declaration as? FirFunction<*> ?: return
val graphRef = function.controlFlowGraphReference as FirControlFlowGraphReferenceImpl
val dataFlowInfo = graphRef.dataFlowInfo
if (function !is FirContractDescriptionOwner || dataFlowInfo == null) return
val effects = (function.contractDescription as? FirResolvedContractDescription)?.effects
?.filter { it is ConeConditionalEffectDeclaration && it.effect is ConeReturnsEffectDeclaration }
if (effects.isNullOrEmpty()) return
val logicSystem = object : PersistentLogicSystem(function.session.typeContext) {
override fun processUpdatedReceiverVariable(flow: PersistentFlow, variable: RealVariable) =
throw IllegalStateException("Receiver variable update is not possible for this logic system")
override fun updateAllReceivers(flow: PersistentFlow) =
throw IllegalStateException("Update of all receivers is not possible for this logic system")
}
effects.forEach { effect ->
val wrongCondition = graph.exitNode.previousCfgNodes.any {
isWrongConditionOnNode(it, effect as ConeConditionalEffectDeclaration, function, logicSystem, dataFlowInfo)
}
if (wrongCondition) {
function.contractDescription.source?.let {
reporter.report(FirErrors.WRONG_IMPLIES_CONDITION.on(it))
}
}
}
}
private fun isWrongConditionOnNode(
node: CFGNode<*>,
effectDeclaration: ConeConditionalEffectDeclaration,
function: FirFunction<*>,
logicSystem: LogicSystem<PersistentFlow>,
dataFlowInfo: DataFlowInfo
): Boolean {
val effect = effectDeclaration.effect as ConeReturnsEffectDeclaration
val builtinTypes = function.session.builtinTypes
val typeContext = function.session.typeContext
val flow = dataFlowInfo.flowOnNodes.getValue(node) as PersistentFlow
val isReturn = node is JumpNode && node.fir is FirReturnExpression
val resultExpression = if (isReturn) (node.fir as FirReturnExpression).result else node.fir
val expressionType = (resultExpression as? FirExpression)?.typeRef?.coneType
if (expressionType == builtinTypes.nothingType.type) return false
if (isReturn && resultExpression is FirWhenExpression) {
return node.collectBranchExits().any {
isWrongConditionOnNode(it, effectDeclaration, function, logicSystem, dataFlowInfo)
}
}
var typeStatements: TypeStatements = flow.approvedTypeStatements
if (effect.value != ConeConstantReference.WILDCARD) {
val operation = effect.value.toOperation()
if (expressionType != null && expressionType.isInapplicableWith(operation, function.session)) return false
if (resultExpression is FirConstExpression<*>) {
if (!resultExpression.isApplicableWith(operation)) return false
} else {
val resultVar = dataFlowInfo.variableStorage.getOrCreateVariable(flow, resultExpression)
typeStatements = logicSystem.approveOperationStatement(flow, OperationStatement(resultVar, operation), builtinTypes)
}
}
val conditionStatements =
effectDeclaration.condition.buildTypeStatements(function, logicSystem, dataFlowInfo.variableStorage, flow) ?: return false
for ((realVar, requiredTypeStatement) in conditionStatements) {
val fixedRealVar = typeStatements.keys.find { it.identifier == realVar.identifier } ?: realVar
val resultTypeStatement = typeStatements[fixedRealVar]
val resultType = mutableListOf<ConeKotlinType>().apply {
addIfNotNull(function.getParameterType(fixedRealVar.identifier.symbol))
if (resultTypeStatement != null) addAll(resultTypeStatement.exactType)
}.let { typeContext.intersectTypesOrNull(it) }
val requiredType = typeContext.intersectTypesOrNull(requiredTypeStatement.exactType.toList())
if (requiredType != null && !requiredType.isSupertypeOf(typeContext, resultType)) return true
}
return false
}
private fun LogicSystem<PersistentFlow>.approveOperationStatement(
flow: PersistentFlow,
statement: OperationStatement,
builtinTypes: BuiltinTypes
): MutableTypeStatements {
val newTypeStatements: MutableTypeStatements = mutableMapOf()
approveStatementsTo(newTypeStatements, flow, statement, flow.logicStatements.flatMap { it.value })
newTypeStatements.mergeTypeStatements(flow.approvedTypeStatements)
val variable = statement.variable
if (variable.isReal()) {
if (statement.operation == Operation.NotEqNull) {
newTypeStatements.addStatement(variable, simpleTypeStatement(variable, true, builtinTypes.anyType.type))
} else if (statement.operation == Operation.EqNull) {
newTypeStatements.addStatement(variable, simpleTypeStatement(variable, false, builtinTypes.anyType.type))
}
}
return newTypeStatements
}
private fun ConeBooleanExpression.buildTypeStatements(
function: FirFunction<*>,
logicSystem: LogicSystem<*>,
variableStorage: VariableStorage,
flow: Flow
): MutableTypeStatements? = when (this) {
is ConeBinaryLogicExpression -> {
val left = left.buildTypeStatements(function, logicSystem, variableStorage, flow)
val right = right.buildTypeStatements(function, logicSystem, variableStorage, flow)
if (left != null && right != null) {
if (kind == LogicOperationKind.AND) {
left.apply { mergeTypeStatements(right) }
} else logicSystem.orForTypeStatements(left, right)
} else (left ?: right)
}
is ConeIsInstancePredicate -> {
val fir = function.getParameterSymbol(arg.parameterIndex).fir
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
realVar?.to(simpleTypeStatement(realVar, !isNegated, type))?.let { mutableMapOf(it) }
}
is ConeIsNullPredicate -> {
val fir = function.getParameterSymbol(arg.parameterIndex).fir
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
realVar?.to(simpleTypeStatement(realVar, isNegated, function.session.builtinTypes.anyType.type))?.let { mutableMapOf(it) }
}
is ConeLogicalNot -> arg.buildTypeStatements(function, logicSystem, variableStorage, flow)
?.mapValuesTo(mutableMapOf()) { (_, value) -> value.invert() }
else -> null
}
private fun ConeKotlinType.isInapplicableWith(operation: Operation, session: FirSession): Boolean {
return (operation == Operation.EqFalse || operation == Operation.EqTrue)
&& !AbstractTypeChecker.isSubtypeOf(session.typeContext, session.builtinTypes.booleanType.type, this)
|| operation == Operation.EqNull && !isNullable
}
private fun FirConstExpression<*>.isApplicableWith(operation: Operation): Boolean = when {
kind == FirConstKind.Null -> operation == Operation.EqNull
kind == FirConstKind.Boolean && operation == Operation.EqTrue -> (value as Boolean)
kind == FirConstKind.Boolean && operation == Operation.EqFalse -> !(value as Boolean)
else -> true
}
fun KotlinTypeMarker.isSupertypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?) =
type != null && AbstractTypeChecker.isSubtypeOf(context, type, this)
private fun simpleTypeStatement(realVar: RealVariable, exactType: Boolean, type: ConeKotlinType): MutableTypeStatement {
return MutableTypeStatement(
realVar,
if (exactType) linkedSetOf(type) else linkedSetOf(),
if (!exactType) linkedSetOf(type) else linkedSetOf()
)
}
private fun CFGNode<*>.collectBranchExits(nodes: MutableList<CFGNode<*>> = mutableListOf()): List<CFGNode<*>> {
if (this is BlockExitNode) {
nodes += previousCfgNodes
} else previousCfgNodes.forEach { it.collectBranchExits(nodes) }
return nodes
}
private fun FirFunction<*>.getParameterType(symbol: AbstractFirBasedSymbol<*>): ConeKotlinType? {
return (if (this.symbol == symbol) receiverTypeRef else valueParameters.find { it.symbol == symbol }?.returnTypeRef)?.coneType
}
private fun FirFunction<*>.getParameterSymbol(index: Int): AbstractFirBasedSymbol<*> {
return if (index == -1) this.symbol else this.valueParameters[index].symbol
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.fir.analysis.cfa.FirCallsEffectAnalyzer
import org.jetbrains.kotlin.fir.analysis.cfa.FirPropertyInitializationAnalyzer
import org.jetbrains.kotlin.fir.analysis.cfa.FirReturnsImpliesAnalyzer
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
object CommonDeclarationCheckers : DeclarationCheckers() {
@@ -42,6 +43,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val controlFlowAnalyserCheckers: List<FirControlFlowChecker> = listOf(
FirPropertyInitializationAnalyzer,
FirCallsEffectAnalyzer
FirCallsEffectAnalyzer,
FirReturnsImpliesAnalyzer
)
}
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NULLABLE_STRING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.PROPERTY_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL
@@ -255,7 +254,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
TO_STRING
)
map.put(LEAKED_IN_PLACE_LAMBDA, "Leaked in-place lambda: {2}", SYMBOL)
map.put(FirErrors.WRONG_IMPLIES_CONDITION, "Wrong implies condition")
// Extended checkers group
// map.put(REDUNDANT_VISIBILITY_MODIFIER, ...) // &
@@ -131,6 +131,7 @@ object FirErrors {
val UNINITIALIZED_VARIABLE by error1<FirSourceElement, PsiElement, FirPropertySymbol>()
val WRONG_INVOCATION_KIND by warning3<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>, EventOccurrencesRange, EventOccurrencesRange>()
val LEAKED_IN_PLACE_LAMBDA by error1<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>>()
val WRONG_IMPLIES_CONDITION by error0<FirSourceElement, PsiElement>()
// Extended checkers group
val REDUNDANT_VISIBILITY_MODIFIER by warning0<FirSourceElement, PsiElement>()
@@ -177,7 +177,7 @@ class MutableTypeStatement(
override val isEmpty: Boolean
get() = exactType.isEmpty() && exactType.isEmpty()
override fun invert(): TypeStatement {
override fun invert(): MutableTypeStatement {
return MutableTypeStatement(
variable,
LinkedHashSet(exactNotType),
@@ -79,7 +79,7 @@ internal fun FirFunctionCall.isBooleanNot(): Boolean {
return symbol.callableId == FirDataFlowAnalyzer.KOTLIN_BOOLEAN_NOT
}
internal fun ConeConstantReference.toOperation(): Operation = when (this) {
fun ConeConstantReference.toOperation(): Operation = when (this) {
ConeConstantReference.NULL -> Operation.EqNull
ConeConstantReference.NOT_NULL -> Operation.NotEqNull
ConeBooleanConstantReference.TRUE -> Operation.EqTrue
@@ -27,9 +27,9 @@ class Foo {
}
fun A?.goodWithReceiver() {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (this@goodWithReceiver != null)
}
}<!>
}
fun A?.badWithReceiver() {
@@ -48,8 +48,8 @@ fun doWhileInContract(x: Any?) {
}
fun localValInContract(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
val y: Int = 42
returns() implies (x is String)
}
}<!>
}
@@ -5,29 +5,29 @@
import kotlin.contracts.*
inline fun <reified T> referToReifiedGeneric(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is T)
}
}<!>
}
class Generic<T> {
fun referToCaptured(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is T)
}
}<!>
}
}
fun referToSubstituted(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is Generic<String>)
}
}<!>
}
fun referToSubstitutedWithStar(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is Generic<*>)
}
}<!>
}
typealias GenericString = Generic<String>
@@ -35,19 +35,19 @@ typealias FunctionalType = () -> Unit
typealias SimpleType = Int
fun referToAliasedGeneric(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is GenericString)
}
}<!>
}
fun referToAliasedFunctionType(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is FunctionalType)
}
}<!>
}
fun referToAliasedSimpleType(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is SimpleType)
}
}<!>
}
@@ -5,29 +5,29 @@
import kotlin.contracts.*
inline fun <reified T> referToReifiedGeneric(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is T)
}
}<!>
}
class Generic<T> {
fun referToCaptured(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is T)
}
}<!>
}
}
fun referToSubstituted(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is Generic<String>)
}
}<!>
}
fun referToSubstitutedWithStar(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is Generic<*>)
}
}<!>
}
typealias GenericString = Generic<String>
@@ -35,19 +35,19 @@ typealias FunctionalType = () -> Unit
typealias SimpleType = Int
fun referToAliasedGeneric(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is GenericString)
}
}<!>
}
fun referToAliasedFunctionType(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is FunctionalType)
}
}<!>
}
fun referToAliasedSimpleType(x: Any?) {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is SimpleType)
}
}<!>
}
@@ -5,9 +5,9 @@
import kotlin.contracts.*
fun safeIsString(x: Any?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (x is String)
}
}<!>
return x?.let { it is String }
}
@@ -5,9 +5,9 @@
import kotlin.contracts.*
fun safeIsString(x: Any?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (x is String)
}
}<!>
return x?.let { it is String }
}
@@ -21,12 +21,12 @@ inline fun case_1(value_1: Int?, block: () -> Unit): Boolean {
// TESTCASE NUMBER: 2
inline fun <T> T?.case_2(value_1: Int?, value_2: Any?, block: () -> Unit): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
returns(true) implies (value_1 == null && this@case_2 == null && value_2 !is Boolean?)
returns(false) implies (value_2 is Boolean?)
returns(null) implies ((value_1 != null || this@case_2 != null) && value_2 !is Boolean?)
}
}<!>
block()
@@ -53,8 +53,8 @@ fun case_8(value_1: Any?) {
if (!funWithReturnsTrueAndInvertCondition(value_1 !is String)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (funWithReturnsFalse(value_1 is String)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (funWithReturnsFalseAndInvertCondition(value_1 !is String)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (funWithReturnsNotNull(value_1 is String) == null) println(value_1.length)
if (!(funWithReturnsNotNull(value_1 is String) != null)) println(value_1.length)
if (funWithReturnsNotNull(value_1 is String) == null) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!(funWithReturnsNotNull(value_1 is String) != null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!(funWithReturnsNull(value_1 is String) == null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (funWithReturnsNull(value_1 is String) != null) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
}
@@ -65,8 +65,8 @@ fun case_9(value_1: String?) {
if (!funWithReturnsTrueAndInvertCondition(value_1 == null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (funWithReturnsFalse(value_1 != null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (funWithReturnsFalseAndInvertCondition(value_1 == null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (funWithReturnsNotNull(value_1 != null) == null) println(value_1.length)
if (funWithReturnsNotNullAndInvertCondition(value_1 == null) == null) println(value_1.length)
if (funWithReturnsNotNull(value_1 != null) == null) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (funWithReturnsNotNullAndInvertCondition(value_1 == null) == null) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (funWithReturnsNull(value_1 != null) != null) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (funWithReturnsNullAndInvertCondition(value_1 == null) != null) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
}
@@ -75,8 +75,8 @@ fun case_9(value_1: String?) {
fun case_10(value_1: Any?) {
if (!funWithReturnsTrueAndTypeCheck(value_1)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!!funWithReturnsFalseAndTypeCheck(value_1)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!(funWithReturnsNotNullAndTypeCheck(value_1) != null)) println(value_1.length)
if (!!(funWithReturnsNotNullAndTypeCheck(value_1) == null)) println(value_1.length)
if (!(funWithReturnsNotNullAndTypeCheck(value_1) != null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!!(funWithReturnsNotNullAndTypeCheck(value_1) == null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!!(funWithReturnsNullAndTypeCheck(value_1) != null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!(funWithReturnsNullAndTypeCheck(value_1) == null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
}
@@ -87,8 +87,8 @@ fun case_11(value_1: Number?) {
if (!funWithReturnsTrueAndNullCheck(value_1)) println(value_1)
if (funWithReturnsFalseAndNotNullCheck(value_1)) println(value_1.<!INAPPLICABLE_CANDIDATE!>toByte<!>())
if (funWithReturnsFalseAndNullCheck(value_1)) println(value_1)
if ((funWithReturnsNotNullAndNotNullCheck(value_1) == null)) println(value_1.toByte())
if (!!!(funWithReturnsNotNullAndNotNullCheck(value_1) != null)) println(value_1.toByte())
if ((funWithReturnsNotNullAndNotNullCheck(value_1) == null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>toByte<!>())
if (!!!(funWithReturnsNotNullAndNotNullCheck(value_1) != null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>toByte<!>())
if (!!(funWithReturnsNotNullAndNullCheck(value_1) == null)) println(value_1)
if (!(funWithReturnsNullAndNotNullCheck(value_1) == null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>toByte<!>())
if (!!(funWithReturnsNullAndNotNullCheck(value_1) != null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>toByte<!>())
@@ -9,45 +9,45 @@ import kotlin.contracts.*
// TESTCASE NUMBER: 3
fun <T> T?.case_3(value_1: Int?, value_2: Boolean): Boolean {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (value_1 != null)
returns(false) implies (value_1 == null && !value_2)
returns(null) implies (value_1 == null && value_2)
}
}<!>
return value_1 == null
}
// TESTCASE NUMBER: 4
fun case_4(value_1: Number, block: (() -> Unit)?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (block != null)
returns(false) implies (value_1 is Int)
returns(null) implies (block == null)
}
}<!>
return value_1 == null
}
// TESTCASE NUMBER: 5
fun String?.case_5(value_1: Number?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (value_1 == null)
returns(false) implies (this@case_5 == null)
returnsNotNull() implies (value_1 is Int)
}
}<!>
return value_1 == null
}
// TESTCASE NUMBER: 6
fun <T> T?.case_6(value_1: Number, value_2: String?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (this@case_6 == null)
returns(false) implies (value_1 is Int)
returns(null) implies (this@case_6 is String)
returnsNotNull() implies (value_2 == null)
}
}<!>
return value_1 == null
}
@@ -62,7 +62,7 @@ fun case_1(value_1: Any?) {
<!AMBIGUITY!>println<!>(value_1?.<!UNRESOLVED_REFERENCE!>toByte<!>())
if (funWithReturnsTrue(value_1 !is Number)) {
<!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>toByte<!>())
if (funWithReturnsNotNull(value_1 is Int) == null) println(value_1.inv())
if (funWithReturnsNotNull(value_1 is Int) == null) <!AMBIGUITY!>println<!>(value_1.inv())
}
}
@@ -52,8 +52,8 @@ fun case_8(value_1: Any?) {
if (funWithReturnsTrueAndInvertCondition(value_1 !is String)) println(value_1.length)
if (!funWithReturnsFalse(value_1 is String)) println(value_1.length)
if (!funWithReturnsFalseAndInvertCondition(value_1 !is String)) println(value_1.length)
if (funWithReturnsNotNull(value_1 is String) != null) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!(funWithReturnsNotNull(value_1 is String) == null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (funWithReturnsNotNull(value_1 is String) != null) println(value_1.length)
if (!(funWithReturnsNotNull(value_1 is String) == null)) println(value_1.length)
}
// TESTCASE NUMBER: 9
@@ -62,17 +62,17 @@ fun case_9(value_1: String?) {
if (funWithReturnsTrueAndInvertCondition(value_1 == null)) println(value_1.length)
if (!funWithReturnsFalse(value_1 != null)) println(value_1.length)
if (!funWithReturnsFalseAndInvertCondition(value_1 == null)) println(value_1.length)
if (funWithReturnsNotNull(value_1 != null) != null) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (!(funWithReturnsNotNull(value_1 != null) == null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (!(funWithReturnsNotNullAndInvertCondition(value_1 == null) == null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>length<!>)
if (funWithReturnsNotNull(value_1 != null) != null) println(value_1.length)
if (!(funWithReturnsNotNull(value_1 != null) == null)) println(value_1.length)
if (!(funWithReturnsNotNullAndInvertCondition(value_1 == null) == null)) println(value_1.length)
}
// TESTCASE NUMBER: 10
fun case_10(value_1: Any?) {
if (funWithReturnsTrueAndTypeCheck(value_1)) println(value_1.length)
if (!funWithReturnsFalseAndTypeCheck(value_1)) println(value_1.length)
if (funWithReturnsNotNullAndTypeCheck(value_1) != null) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (!(funWithReturnsNotNullAndTypeCheck(value_1) == null)) <!AMBIGUITY!>println<!>(value_1.<!UNRESOLVED_REFERENCE!>length<!>)
if (funWithReturnsNotNullAndTypeCheck(value_1) != null) println(value_1.length)
if (!(funWithReturnsNotNullAndTypeCheck(value_1) == null)) println(value_1.length)
}
// TESTCASE NUMBER: 11
@@ -82,7 +82,7 @@ fun case_11(value_1: Number?, value_2: Int?) {
if (!funWithReturnsFalseAndNotNullCheck(value_2)) value_2.inc()
if (!funWithReturnsFalseAndNotNullCheck(value_1)) println(value_1.toByte())
if (!funWithReturnsFalseAndNullCheck(value_1)) println(value_1)
if (!(funWithReturnsNotNullAndNotNullCheck(value_1) == null)) println(value_1.<!INAPPLICABLE_CANDIDATE!>toByte<!>())
if (funWithReturnsNotNullAndNotNullCheck(value_1) != null) println(value_1.<!INAPPLICABLE_CANDIDATE!>toByte<!>())
if (!(funWithReturnsNotNullAndNotNullCheck(value_1) == null)) println(value_1.toByte())
if (funWithReturnsNotNullAndNotNullCheck(value_1) != null) println(value_1.toByte())
if (funWithReturnsNotNullAndNullCheck(value_1) != null) println(value_1)
}
@@ -15,7 +15,7 @@ fun case_1(value_1: Int?, value_2: Int? = 10): Boolean {
// TESTCASE NUMBER: 2
fun case_2(value_1: Int? = 10, value_2: Int? = 10, value_3: Int? = 10): Boolean {
contract { returns(true) implies (value_2 != null) }
<!WRONG_IMPLIES_CONDITION!>contract { returns(true) implies (value_2 != null) }<!>
return value_1 != null
}
@@ -17,27 +17,27 @@ fun case_1(value_1: Int?): Boolean? {
// TESTCASE NUMBER: 2
fun case_2(value_1: Int?): Boolean {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(false) implies (value_1 != null)
}
}<!>
return value_1 != null
}
// TESTCASE NUMBER: 3
fun case_3(value_1: Int?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returnsNotNull() implies (value_1 != null)
}
}<!>
return value_1 != null
}
// TESTCASE NUMBER: 4
fun case_4(value_1: Any?): Boolean {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returnsNotNull() implies (value_1 is Number)
}
}<!>
return value_1 is Number
}
@@ -70,6 +70,6 @@ fun case_3(value_1: Int?) {
// TESTCASE NUMBER: 4
fun case_4(value_1: Any?) {
if (contracts.case_4(value_1) != null) {
value_1.<!UNRESOLVED_REFERENCE!>toByte<!>()
value_1.toByte()
}
}
@@ -20,34 +20,34 @@ fun <T> T?.case_3(value_1: Int?, value_2: Boolean): Boolean {
// TESTCASE NUMBER: 4
fun case_4(value_1: Number, block: (() -> Unit)?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (value_1 is Int)
returns(false) implies (block == null)
returns(null) implies (block != null)
}
}<!>
return value_1 == null
}
// TESTCASE NUMBER: 5
fun String?.case_5(value_1: Number?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (value_1 != null)
returns(false) implies (value_1 is Int)
returnsNotNull() implies (this@case_5 != null)
}
}<!>
return value_1 == null
}
// TESTCASE NUMBER: 6
fun <T> T?.case_6(value_1: Number, value_2: String?): Boolean? {
contract {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract {
returns(true) implies (this@case_6 != null)
returns(false) implies (this@case_6 is String)
returns(null) implies (value_1 is Int)
returnsNotNull() implies (value_2 != null)
}
}<!>
return value_1 == null
}
@@ -62,7 +62,7 @@ fun case_1(value_1: Any?) {
println(value_1?.toByte())
if (funWithReturnsTrue(value_1 is Number)) {
println(value_1.toByte())
if (funWithReturnsNotNull(value_1 is Int) != null) <!AMBIGUITY!>println<!>(value_1.inv())
if (funWithReturnsNotNull(value_1 is Int) != null) println(value_1.inv())
}
}
@@ -0,0 +1,32 @@
// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts
import kotlin.contracts.*
// TESTCASE NUMBER: 1
inline fun case_1(block: () -> Unit) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return block()
}
// TESTCASE NUMBER: 2
inline fun case_2(value_1: Int?, block: () -> Unit): Boolean {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
returns(true) implies (value_1 != null)
}
block()
return value_1 != null
}
// TESTCASE NUMBER: 3
inline fun <T> T?.case_3(value_1: Int?, value_2: Boolean, value_3: Int?, block: () -> Unit): Boolean? {
<!WRONG_IMPLIES_CONDITION!>contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
returns(true) implies (value_1 != null)
returns(false) implies (!value_2)
returnsNotNull() implies (this@case_3 != null && value_3 != null)
}<!>
block()
return value_1 != null
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts
/*