[FIR] Add ReturnsImplies effect analyzer
This commit is contained in:
+35
@@ -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
|
||||
}
|
||||
+40
@@ -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|
|
||||
}
|
||||
+18
@@ -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
|
||||
}
|
||||
+20
@@ -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))
|
||||
}
|
||||
+121
@@ -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
|
||||
}
|
||||
+118
@@ -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|)
|
||||
}
|
||||
+57
@@ -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"
|
||||
}
|
||||
+58
@@ -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)
|
||||
}
|
||||
+56
@@ -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)
|
||||
}
|
||||
+59
@@ -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|)
|
||||
}
|
||||
+4
-4
@@ -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?) {
|
||||
|
||||
+19
@@ -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
|
||||
}
|
||||
+19
@@ -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| }
|
||||
}
|
||||
+39
@@ -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
|
||||
}
|
||||
}
|
||||
+69
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -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");
|
||||
|
||||
+212
@@ -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
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -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
|
||||
)
|
||||
}
|
||||
+1
-2
@@ -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
|
||||
|
||||
Vendored
+2
-2
@@ -27,9 +27,9 @@ class Foo {
|
||||
}
|
||||
|
||||
fun A?.goodWithReceiver() {
|
||||
contract {
|
||||
<!WRONG_IMPLIES_CONDITION!>contract {
|
||||
returns() implies (this@goodWithReceiver != null)
|
||||
}
|
||||
}<!>
|
||||
}
|
||||
|
||||
fun A?.badWithReceiver() {
|
||||
|
||||
+2
-2
@@ -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)
|
||||
}
|
||||
}<!>
|
||||
}
|
||||
Vendored
+14
-14
@@ -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)
|
||||
}
|
||||
}<!>
|
||||
}
|
||||
Vendored
+14
-14
@@ -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)
|
||||
}
|
||||
}<!>
|
||||
}
|
||||
+2
-2
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
|
||||
Vendored
+8
-8
@@ -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<!>())
|
||||
|
||||
Vendored
+9
-9
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+9
-9
@@ -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)
|
||||
}
|
||||
|
||||
Vendored
+1
-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
|
||||
}
|
||||
|
||||
|
||||
Vendored
+7
-7
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+7
-7
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
@@ -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
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user