[FIR] KT-56877: Allow referencing implicit receivers in class contracts

This commit is contained in:
Nikolay Lunyak
2023-03-07 13:27:44 +02:00
committed by Space Team
parent 409249267c
commit c7a71fec17
10 changed files with 20 additions and 67 deletions
@@ -382,15 +382,9 @@ class BodyResolveContext(
fun <T> withRegularClass( fun <T> withRegularClass(
regularClass: FirRegularClass, regularClass: FirRegularClass,
holder: SessionHolder, holder: SessionHolder,
forContracts: Boolean = false,
f: () -> T f: () -> T
): T { ): T {
storeClassIfNotNested(regularClass, holder.session) storeClassIfNotNested(regularClass, holder.session)
if (forContracts) {
return withTypeParametersOf(regularClass) {
withContainerClass(regularClass, f)
}
}
return withTowerDataModeCleanup { return withTowerDataModeCleanup {
if (!regularClass.isInner && containerIfAny is FirRegularClass) { if (!regularClass.isInner && containerIfAny is FirRegularClass) {
towerDataMode = if (regularClass.isCompanion) { towerDataMode = if (regularClass.isCompanion) {
@@ -254,7 +254,7 @@ abstract class FirAbstractContractResolveTransformerDispatcher(
} }
override fun transformRegularClass(regularClass: FirRegularClass, data: ResolutionMode): FirStatement { override fun transformRegularClass(regularClass: FirRegularClass, data: ResolutionMode): FirStatement {
context.withRegularClass(regularClass, components, forContracts = true) { context.withRegularClass(regularClass, components) {
transformDeclarationContent(regularClass, data) transformDeclarationContent(regularClass, data)
} }
return regularClass return regularClass
+2 -2
View File
@@ -9,7 +9,7 @@ open class Result {
fun isSuccess1(): Boolean { fun isSuccess1(): Boolean {
contract { contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (this<!UNRESOLVED_LABEL!>@Result<!> is Success)<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (this@Result is Success)<!>
} }
return this@Result is Success return this@Result is Success
} }
@@ -18,7 +18,7 @@ open class Result {
contract { contract {
// implicit type ref // implicit type ref
// ERROR CLASS: Cannot calculate return type during full-body resolution (local class/object?) // ERROR CLASS: Cannot calculate return type during full-body resolution (local class/object?)
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (this<!UNRESOLVED_LABEL!>@Result<!>.someProperty == 10)<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (this@Result.someProperty == 10)<!>
} }
return this@Result.someProperty == 10 return this@Result.someProperty == 10
} }
@@ -1,42 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
package my
// Accepts block, can't be distinguished from kotlin.contract without resolve
fun contract(block: () -> Unit) {}
// Accepts int, potentially can be distinguished from kotlin.contract by PSI,
// but as of Kotlin 1.3.0, we don't do that
fun contract(i: Int) {}
fun doStuff() {}
class SomeClass {
fun contract() {}
fun callMemberContractWithThis() {
this.contract()
}
fun callMemberContractWithoutThis() {
<!NONE_APPLICABLE!>contract<!>()
}
fun callTopLevelSamePsiInMember() {
contract { }
}
}
fun callTopLevelSamePsi() {
contract { }
}
fun callTopLevelDifferentPsi() {
contract(42)
}
fun callTopLevelSamePsiNotFirstStatement() {
doStuff()
contract { }
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
package my package my
@@ -10,13 +10,13 @@ class Foo {
inner class Bar { inner class Bar {
fun good() { fun good() {
contract { contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this<!UNRESOLVED_LABEL!>@Bar<!> != null)<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this@Bar != null)<!>
} }
} }
fun badOuter() { fun badOuter() {
contract { contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this<!UNRESOLVED_LABEL!>@Foo<!> != null)<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this@Foo != null)<!>
} }
} }
@@ -34,7 +34,7 @@ class Foo {
fun A?.badWithReceiver() { fun A?.badWithReceiver() {
contract { contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this<!UNRESOLVED_LABEL!>@Bar<!> != null)<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this@Bar != null)<!>
} }
} }
} }
@@ -7,7 +7,7 @@ import kotlin.contracts.*
class Foo(val x: Int?) { class Foo(val x: Int?) {
fun isXNull(): Boolean { fun isXNull(): Boolean {
contract { contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (<!UNRESOLVED_REFERENCE!>x<!> != null)<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (x != null)<!>
} }
return x != null return x != null
} }
@@ -7,8 +7,8 @@ import kotlin.contracts.*
class Foo(val x: Int?) { class Foo(val x: Int?) {
fun isXNull(): Boolean { fun isXNull(): Boolean {
contract { contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (<!UNRESOLVED_REFERENCE!>x<!> != null)<!> <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (x != null)<!>
} }
return x != null return x != null
} }
} }
@@ -11,16 +11,16 @@ object case_1 {
private const val value_3 = false private const val value_3 = false
fun case_1_1(): Boolean? { fun case_1_1(): Boolean? {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returnsNotNull() implies (<!UNRESOLVED_REFERENCE!>value_1<!>)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returnsNotNull() implies (value_1)<!> }
return if (value_1) true else null return if (value_1) true else null
} }
fun case_1_2(): Boolean? { fun case_1_2(): Boolean? {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(null) implies (<!UNRESOLVED_REFERENCE!>value_2<!>)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(null) implies (value_2)<!> }
return if (value_2) null else true return if (value_2) null else true
} }
fun case_1_3(): Boolean { fun case_1_3(): Boolean {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (<!UNRESOLVED_REFERENCE!>value_3<!>)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (value_3)<!> }
return value_3 return value_3
} }
} }
@@ -42,22 +42,22 @@ class case_2(value_5: Boolean, val value_1: Boolean) {
} }
fun case_2_2(): Boolean? { fun case_2_2(): Boolean? {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(null) implies (<!UNRESOLVED_REFERENCE!>value_1<!>)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(null) implies (value_1)<!> }
return if (value_1) null else true return if (value_1) null else true
} }
fun case_2_3(): Boolean { fun case_2_3(): Boolean {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (<!UNRESOLVED_REFERENCE!>value_2<!>)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (value_2)<!> }
return value_2 return value_2
} }
fun case_2_4(): Boolean { fun case_2_4(): Boolean {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (<!UNRESOLVED_REFERENCE!>value_3<!>)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (value_3)<!> }
return !(value_3) return !(value_3)
} }
inline fun <reified K : Number> K.case_2_5(): Boolean? { inline fun <reified K : Number> K.case_2_5(): Boolean? {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returnsNotNull() implies (<!UNRESOLVED_REFERENCE!>value_4<!>)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returnsNotNull() implies (<!NON_PUBLIC_CALL_FROM_PUBLIC_INLINE!>value_4<!>)<!> }
return if (<!NON_PUBLIC_CALL_FROM_PUBLIC_INLINE!>value_4<!>) true else null return if (<!NON_PUBLIC_CALL_FROM_PUBLIC_INLINE!>value_4<!>) true else null
} }
} }
@@ -32,7 +32,7 @@ fun case_2() {
class case_4 : ClassLevel3() { class case_4 : ClassLevel3() {
fun <T : Number?>T.case_4_1(): Boolean { fun <T : Number?>T.case_4_1(): Boolean {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (this<!UNRESOLVED_LABEL!>@case_4<!> !is ClassLevel1)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(false) implies (<!USELESS_IS_CHECK!>this@case_4 !is ClassLevel1<!>)<!> }
return this == null return this == null
} }
@@ -60,12 +60,12 @@ class case_4 : ClassLevel3() {
class case_5<T> : ClassLevel5() { class case_5<T> : ClassLevel5() {
inner class case_5_1 { inner class case_5_1 {
fun <K : Number?>K.case_5_1_1() { fun <K : Number?>K.case_5_1_1() {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this<!UNRESOLVED_LABEL!>@case_5_1<!> !is ClassLevel1 && this<!UNRESOLVED_LABEL!>@case_5_1<!> != null || this<!UNRESOLVED_LABEL!>@case_5<!> is ClassLevel1 && this@case_5_1_1 is Float)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this@case_5_1 !is ClassLevel1 && <!SENSELESS_COMPARISON!>this@case_5_1 != null<!> || <!USELESS_IS_CHECK!>this@case_5 is ClassLevel1<!> && this@case_5_1_1 is Float)<!> }
if (!(this@case_5_1 !is ClassLevel1 && <!SENSELESS_COMPARISON!>this@case_5_1 != null<!> || <!USELESS_IS_CHECK!>this@case_5 is ClassLevel1<!> && this is Float)) throw Exception() if (!(this@case_5_1 !is ClassLevel1 && <!SENSELESS_COMPARISON!>this@case_5_1 != null<!> || <!USELESS_IS_CHECK!>this@case_5 is ClassLevel1<!> && this is Float)) throw Exception()
} }
fun case_5_1_2() { fun case_5_1_2() {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this<!UNRESOLVED_LABEL!>@case_5_1<!> !is ClassLevel1 || this<!UNRESOLVED_LABEL!>@case_5<!> is ClassLevel1 || this<!UNRESOLVED_LABEL!>@case_5_1<!> == null)<!> } contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (this@case_5_1 !is ClassLevel1 || <!USELESS_IS_CHECK!>this@case_5 is ClassLevel1<!> || <!SENSELESS_COMPARISON!>this@case_5_1 == null<!>)<!> }
if (!(this@case_5_1 !is ClassLevel1 || <!USELESS_IS_CHECK!>this@case_5 is ClassLevel1<!> || <!SENSELESS_COMPARISON!>this@case_5_1 == null<!>)) throw Exception() if (!(this@case_5_1 !is ClassLevel1 || <!USELESS_IS_CHECK!>this@case_5 is ClassLevel1<!> || <!SENSELESS_COMPARISON!>this@case_5_1 == null<!>)) throw Exception()
} }
} }