[FIR] Move FirReturnsImpliesAnalyzer to the extended checkers

This commit is contained in:
Nikolay Lunyak
2023-06-27 16:57:19 +03:00
committed by Space Team
parent db720d375d
commit 884cd6c754
21 changed files with 67 additions and 120 deletions
@@ -140,7 +140,6 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker> override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker>
get() = setOf( get() = setOf(
FirCallsEffectAnalyzer, FirCallsEffectAnalyzer,
FirReturnsImpliesAnalyzer,
) )
override val variableAssignmentCfaBasedCheckers: Set<AbstractFirPropertyInitializationChecker> override val variableAssignmentCfaBasedCheckers: Set<AbstractFirPropertyInitializationChecker>
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker
import org.jetbrains.kotlin.fir.analysis.cfa.FirReturnsImpliesAnalyzer
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.* import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
import org.jetbrains.kotlin.fir.analysis.checkers.extended.* import org.jetbrains.kotlin.fir.analysis.checkers.extended.*
@@ -36,6 +37,7 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() {
override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker> override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker>
get() = setOf( get() = setOf(
UnreachableCodeChecker, UnreachableCodeChecker,
FirReturnsImpliesAnalyzer,
) )
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker> override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
@@ -27,9 +27,9 @@ class Foo {
} }
fun A?.goodWithReceiver() { fun A?.goodWithReceiver() {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (this@goodWithReceiver != null) returns() implies (this@goodWithReceiver != null)
}<!> }
} }
fun A?.badWithReceiver() { fun A?.badWithReceiver() {
@@ -48,8 +48,8 @@ fun doWhileInContract(x: Any?) {
} }
fun localValInContract(x: Any?) { fun localValInContract(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>val y: Int = 42<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>val y: Int = 42<!>
returns() implies (x is String) returns() implies (x is String)
}<!> }
} }
@@ -5,9 +5,9 @@
import kotlin.contracts.* import kotlin.contracts.*
inline fun <reified T> referToReifiedGeneric(x: Any?) { inline fun <reified T> referToReifiedGeneric(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is T) returns() implies (x is T)
}<!> }
} }
class Generic<T> { class Generic<T> {
@@ -19,15 +19,15 @@ class Generic<T> {
} }
fun referToSubstituted(x: Any?) { fun referToSubstituted(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is Generic<String>) returns() implies (x is Generic<String>)
}<!> }
} }
fun referToSubstitutedWithStar(x: Any?) { fun referToSubstitutedWithStar(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is Generic<*>) returns() implies (x is Generic<*>)
}<!> }
} }
typealias GenericString = Generic<String> typealias GenericString = Generic<String>
@@ -35,19 +35,19 @@ typealias FunctionalType = () -> Unit
typealias SimpleType = Int typealias SimpleType = Int
fun referToAliasedGeneric(x: Any?) { fun referToAliasedGeneric(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is GenericString) returns() implies (x is GenericString)
}<!> }
} }
fun referToAliasedFunctionType(x: Any?) { fun referToAliasedFunctionType(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is FunctionalType) returns() implies (x is FunctionalType)
}<!> }
} }
fun referToAliasedSimpleType(x: Any?) { fun referToAliasedSimpleType(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is SimpleType) returns() implies (x is SimpleType)
}<!> }
} }
@@ -5,9 +5,9 @@
import kotlin.contracts.* import kotlin.contracts.*
inline fun <reified T> referToReifiedGeneric(x: Any?) { inline fun <reified T> referToReifiedGeneric(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is T) returns() implies (x is T)
}<!> }
} }
class Generic<T> { class Generic<T> {
@@ -19,15 +19,15 @@ class Generic<T> {
} }
fun referToSubstituted(x: Any?) { fun referToSubstituted(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is Generic<String>) returns() implies (x is Generic<String>)
}<!> }
} }
fun referToSubstitutedWithStar(x: Any?) { fun referToSubstitutedWithStar(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is Generic<*>) returns() implies (x is Generic<*>)
}<!> }
} }
typealias GenericString = Generic<String> typealias GenericString = Generic<String>
@@ -35,19 +35,19 @@ typealias FunctionalType = () -> Unit
typealias SimpleType = Int typealias SimpleType = Int
fun referToAliasedGeneric(x: Any?) { fun referToAliasedGeneric(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is GenericString) returns() implies (x is GenericString)
}<!> }
} }
fun referToAliasedFunctionType(x: Any?) { fun referToAliasedFunctionType(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is FunctionalType) returns() implies (x is FunctionalType)
}<!> }
} }
fun referToAliasedSimpleType(x: Any?) { fun referToAliasedSimpleType(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns() implies (x is SimpleType) returns() implies (x is SimpleType)
}<!> }
} }
@@ -1,19 +0,0 @@
// ISSUE: KT-45243
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@ExperimentalContracts
fun <T> assertNotNull(actual: T) {
<!WRONG_IMPLIES_CONDITION!>contract { returns() implies (actual != null) }<!>
}
@ExperimentalContracts
fun test_1() {
assertNotNull { }
}
@ExperimentalContracts
fun test_2() {
assertNotNull({ })
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// ISSUE: KT-45243 // ISSUE: KT-45243
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
@@ -5,7 +5,7 @@ fun calculateNumber(block: () -> Int): Int contract [
callsInPlace(block, InvocationKind.EXACTLY_ONCE) callsInPlace(block, InvocationKind.EXACTLY_ONCE)
] = block() ] = block()
fun <R> calculateResult(num: Int?, calculate: (Int?) -> R): R contract <!WRONG_IMPLIES_CONDITION!>[ fun <R> calculateResult(num: Int?, calculate: (Int?) -> R): R contract [
callsInPlace(calculate, InvocationKind.EXACTLY_ONCE), callsInPlace(calculate, InvocationKind.EXACTLY_ONCE),
returns() implies (num != null) returns() implies (num != null)
]<!> = calculate(num) ] = calculate(num)
@@ -17,7 +17,7 @@ open class Foo {
} }
inline fun <reified T> assertIs(value: Any) { inline fun <reified T> assertIs(value: Any) {
<!WRONG_IMPLIES_CONDITION!>contract { returns() implies (value is T) }<!> contract { returns() implies (value is T) }
} }
} }
@@ -31,9 +31,9 @@ fun testNullWhenNull(x: Int?) {
// NB. it is the same function as `nullWhenNull`, but annotations specifies other facet of the function behaviour // NB. it is the same function as `nullWhenNull`, but annotations specifies other facet of the function behaviour
fun notNullWhenNotNull (x: Int?): Int? { fun notNullWhenNotNull (x: Int?): Int? {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns(null) implies (x == null) returns(null) implies (x == null)
}<!> }
return x?.inc() return x?.inc()
} }
@@ -5,9 +5,9 @@
import kotlin.contracts.* import kotlin.contracts.*
fun safeIsString(x: Any?): Boolean? { fun safeIsString(x: Any?): Boolean? {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (x is String) returns(true) implies (x is String)
}<!> }
return x?.let { it is String } return x?.let { it is String }
} }
@@ -5,9 +5,9 @@
import kotlin.contracts.* import kotlin.contracts.*
fun safeIsString(x: Any?): Boolean? { fun safeIsString(x: Any?): Boolean? {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (x is String) returns(true) implies (x is String)
}<!> }
return x?.let { it is String } return x?.let { it is String }
} }
@@ -193,7 +193,9 @@ fun TestConfigurationBuilder.baseFirDiagnosticTestConfiguration(
forTestsMatching( forTestsMatching(
"compiler/fir/analysis-tests/testData/resolve/extendedCheckers/*" or "compiler/fir/analysis-tests/testData/resolve/extendedCheckers/*" or
"compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/*" "compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/*" or
"compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/bad/returnsImplies/*" or
"compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/*"
) { ) {
defaultDirectives { defaultDirectives {
+WITH_EXTENDED_CHECKERS +WITH_EXTENDED_CHECKERS
@@ -28,12 +28,12 @@ inline fun case_1(value_1: Int?, block: () -> Unit): Boolean {
// TESTCASE NUMBER: 2 // TESTCASE NUMBER: 2
inline fun <T> T?.case_2(value_1: Int?, value_2: Any?, block: () -> Unit): Boolean? { inline fun <T> T?.case_2(value_1: Int?, value_2: Any?, block: () -> Unit): Boolean? {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract { contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE) callsInPlace(block, InvocationKind.AT_MOST_ONCE)
returns(true) implies (value_1 == null && this@case_2 == null && value_2 !is Boolean?) returns(true) implies (value_1 == null && this@case_2 == null && value_2 !is Boolean?)
returns(false) implies (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?) returns(null) implies ((value_1 != null || this@case_2 != null) && value_2 !is Boolean?)
}<!> }
block() block()
@@ -18,45 +18,45 @@ import kotlin.contracts.*
// TESTCASE NUMBER: 3 // TESTCASE NUMBER: 3
fun <T> T?.case_3(value_1: Int?, value_2: Boolean): Boolean { fun <T> T?.case_3(value_1: Int?, value_2: Boolean): Boolean {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (value_1 != null) returns(true) implies (value_1 != null)
returns(false) implies (value_1 == null && !value_2) returns(false) implies (value_1 == null && !value_2)
returns(null) implies (value_1 == null && value_2) returns(null) implies (value_1 == null && value_2)
}<!> }
return value_1 == null return value_1 == null
} }
// TESTCASE NUMBER: 4 // TESTCASE NUMBER: 4
fun case_4(value_1: Number, block: (() -> Unit)?): Boolean? { fun case_4(value_1: Number, block: (() -> Unit)?): Boolean? {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (block != null) returns(true) implies (block != null)
returns(false) implies (value_1 is Int) returns(false) implies (value_1 is Int)
returns(null) implies (block == null) returns(null) implies (block == null)
}<!> }
return <!SENSELESS_COMPARISON!>value_1 == null<!> return <!SENSELESS_COMPARISON!>value_1 == null<!>
} }
// TESTCASE NUMBER: 5 // TESTCASE NUMBER: 5
fun String?.case_5(value_1: Number?): Boolean? { fun String?.case_5(value_1: Number?): Boolean? {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (value_1 == null) returns(true) implies (value_1 == null)
returns(false) implies (this@case_5 == null) returns(false) implies (this@case_5 == null)
returnsNotNull() implies (value_1 is Int) returnsNotNull() implies (value_1 is Int)
}<!> }
return value_1 == null return value_1 == null
} }
// TESTCASE NUMBER: 6 // TESTCASE NUMBER: 6
fun <T> T?.case_6(value_1: Number, value_2: String?): Boolean? { fun <T> T?.case_6(value_1: Number, value_2: String?): Boolean? {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (this@case_6 == null) returns(true) implies (this@case_6 == null)
returns(false) implies (value_1 is Int) returns(false) implies (value_1 is Int)
returns(null) implies (this@case_6 is String) returns(null) implies (this@case_6 is String)
returnsNotNull() implies (value_2 == null) returnsNotNull() implies (value_2 == null)
}<!> }
return <!SENSELESS_COMPARISON!>value_1 == null<!> return <!SENSELESS_COMPARISON!>value_1 == null<!>
} }
@@ -25,7 +25,7 @@ fun case_1(value_1: Int?, value_2: Int? = 10): Boolean {
// TESTCASE NUMBER: 2 // TESTCASE NUMBER: 2
fun case_2(value_1: Int? = 10, value_2: Int? = 10, value_3: Int? = 10): Boolean { fun case_2(value_1: Int? = 10, value_2: Int? = 10, value_3: Int? = 10): Boolean {
<!WRONG_IMPLIES_CONDITION!>contract { returns(true) implies (value_2 != null) }<!> contract { returns(true) implies (value_2 != null) }
return value_1 != null return value_1 != null
} }
@@ -27,27 +27,27 @@ fun case_1(value_1: Int?): Boolean? {
// TESTCASE NUMBER: 2 // TESTCASE NUMBER: 2
fun case_2(value_1: Int?): Boolean { fun case_2(value_1: Int?): Boolean {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns(false) implies (value_1 != null) returns(false) implies (value_1 != null)
}<!> }
return value_1 != null return value_1 != null
} }
// TESTCASE NUMBER: 3 // TESTCASE NUMBER: 3
fun case_3(value_1: Int?): Boolean? { fun case_3(value_1: Int?): Boolean? {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returnsNotNull() implies (value_1 != null) returnsNotNull() implies (value_1 != null)
}<!> }
return value_1 != null return value_1 != null
} }
// TESTCASE NUMBER: 4 // TESTCASE NUMBER: 4
fun case_4(value_1: Any?): Boolean { fun case_4(value_1: Any?): Boolean {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returnsNotNull() implies (value_1 is Number) returnsNotNull() implies (value_1 is Number)
}<!> }
return value_1 is Number return value_1 is Number
} }
@@ -29,34 +29,34 @@ fun <T> T?.case_3(value_1: Int?, value_2: Boolean): Boolean {
// TESTCASE NUMBER: 4 // TESTCASE NUMBER: 4
fun case_4(value_1: Number, block: (() -> Unit)?): Boolean? { fun case_4(value_1: Number, block: (() -> Unit)?): Boolean? {
<!WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (value_1 is Int) returns(true) implies (value_1 is Int)
returns(false) implies (block == null) returns(false) implies (block == null)
returns(null) implies (block != null) returns(null) implies (block != null)
}<!> }
return <!SENSELESS_COMPARISON!>value_1 == null<!> return <!SENSELESS_COMPARISON!>value_1 == null<!>
} }
// TESTCASE NUMBER: 5 // TESTCASE NUMBER: 5
fun String?.case_5(value_1: Number?): Boolean? { fun String?.case_5(value_1: Number?): Boolean? {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (value_1 != null) returns(true) implies (value_1 != null)
returns(false) implies (value_1 is Int) returns(false) implies (value_1 is Int)
returnsNotNull() implies (this@case_5 != null) returnsNotNull() implies (this@case_5 != null)
}<!> }
return value_1 == null return value_1 == null
} }
// TESTCASE NUMBER: 6 // TESTCASE NUMBER: 6
fun <T> T?.case_6(value_1: Number, value_2: String?): Boolean? { fun <T> T?.case_6(value_1: Number, value_2: String?): Boolean? {
<!WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION, WRONG_IMPLIES_CONDITION!>contract { contract {
returns(true) implies (this@case_6 != null) returns(true) implies (this@case_6 != null)
returns(false) implies (this@case_6 is String) returns(false) implies (this@case_6 is String)
returns(null) implies (value_1 is Int) returns(null) implies (value_1 is Int)
returnsNotNull() implies (value_2 != null) returnsNotNull() implies (value_2 != null)
}<!> }
return <!SENSELESS_COMPARISON!>value_1 == null<!> return <!SENSELESS_COMPARISON!>value_1 == null<!>
} }
@@ -1,39 +0,0 @@
// !OPT_IN: kotlin.contracts.ExperimentalContracts
/*
* KOTLIN DIAGNOSTICS NOT LINKED SPEC TEST (POSITIVE)
*
* SECTIONS: contracts, declarations, contractBuilder, common
* NUMBER: 1
* DESCRIPTION: Functions with simple contracts.
*/
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,3 +1,4 @@
// FIR_IDENTICAL
// !OPT_IN: kotlin.contracts.ExperimentalContracts // !OPT_IN: kotlin.contracts.ExperimentalContracts
/* /*