Support contracts in PartialBodyResolveFilter

Previously, PartialBodyResolveFilter didn't know about contracts and was
filtering out calls of such functions, leading to unstable completion in
cases like that:

fun test(x: Any?) {
    require(x is String)
    x.<caret>
}

However, PartialBodyResolveFilter works by pure PSI, while to determine
if function has a contract we have to resolve it.

To solve it, we do something very similar to what has been done with
Nothin-returning functions: introduce
KotlinProbablyContractedFunctionShortNameIndex, which collects all
function which *may* have a contract during indexing.

^KT-25275 Fixed
This commit is contained in:
Dmitry Savvinov
2018-07-20 19:35:45 +03:00
parent 5ab79a111d
commit f90b29c2e3
23 changed files with 293 additions and 2 deletions
@@ -0,0 +1,17 @@
Resolve target: value-parameter x: kotlin.Any? smart-cast to kotlin.String
----------------------------------------------
// COMPILER_ARGUMENTS: -XXLanguage:+ReadDeserializedContracts -XXLanguage:+UseReturnsEffect
package test
fun irrelevantConsume(y: Any?) {}
fun testContractFromBinaryDependency(x: Any?, y: Any?) {
require(x is String)
require(y is String)
/* STATEMENT DELETED: irrelevantConsume(x) */
<caret>x.length
/* STATEMENT DELETED: require(x is Int) */
}
@@ -0,0 +1,17 @@
Resolve target: value-parameter x: kotlin.Any? smart-cast to kotlin.String
----------------------------------------------
// COMPILER_ARGUMENTS: -XXLanguage:+ReadDeserializedContracts -XXLanguage:+UseReturnsEffect
package test
fun irrelevantConsume(y: Any?) {}
fun testContractFromBinaryDependency(x: Any?, y: Any?) {
require(x is String)
/* STATEMENT DELETED: require(y is String) */
/* STATEMENT DELETED: irrelevantConsume(x) */
<caret>x.length
/* STATEMENT DELETED: require(x is Int) */
}
@@ -0,0 +1,15 @@
// COMPILER_ARGUMENTS: -XXLanguage:+ReadDeserializedContracts -XXLanguage:+UseReturnsEffect
package test
fun irrelevantConsume(y: Any?) {}
fun testContractFromBinaryDependency(x: Any?, y: Any?) {
require(x is String)
require(y is String)
irrelevantConsume(x)
<caret>x.length
require(x is Int)
}
@@ -0,0 +1,22 @@
Resolve target: value-parameter x: kotlin.Any? smart-cast to kotlin.String
----------------------------------------------
// COMPILER_ARGUMENTS: -XXLanguage:+AllowContractsForCustomFunctions -XXLanguage:+UseReturnsEffect
@file:Suppress("INVISIBLE_MEMBER")
package test
import kotlin.internal.contracts.*
fun myRequire(x: Boolean) {
contract {
returns() implies x
}
}
fun testContractFromSource(x: Any?, y: Any?) {
myRequire(x is String)
myRequire(y is String)
<caret>x.length
/* STATEMENT DELETED: myRequire(x is Int) */
}
@@ -0,0 +1,22 @@
Resolve target: value-parameter x: kotlin.Any? smart-cast to kotlin.String
----------------------------------------------
// COMPILER_ARGUMENTS: -XXLanguage:+AllowContractsForCustomFunctions -XXLanguage:+UseReturnsEffect
@file:Suppress("INVISIBLE_MEMBER")
package test
import kotlin.internal.contracts.*
fun myRequire(x: Boolean) {
contract {
returns() implies x
}
}
fun testContractFromSource(x: Any?, y: Any?) {
myRequire(x is String)
/* STATEMENT DELETED: myRequire(y is String) */
<caret>x.length
/* STATEMENT DELETED: myRequire(x is Int) */
}
@@ -0,0 +1,20 @@
// COMPILER_ARGUMENTS: -XXLanguage:+AllowContractsForCustomFunctions -XXLanguage:+UseReturnsEffect
@file:Suppress("INVISIBLE_MEMBER")
package test
import kotlin.internal.contracts.*
fun myRequire(x: Boolean) {
contract {
returns() implies x
}
}
fun testContractFromSource(x: Any?, y: Any?) {
myRequire(x is String)
myRequire(y is String)
<caret>x.length
myRequire(x is Int)
}
@@ -9,7 +9,10 @@ fun foo(p: Int) {
val v2 = v1 * v1
val v3 = v1 * v2
/* STATEMENT DELETED: run { val v2 = 1 println(v2) } */
run {
val v2 = 1
println(v2)
}
print(<caret>v2)
@@ -9,7 +9,10 @@ fun foo(p: Int) {
val v2 = v1 * v1
/* STATEMENT DELETED: val v3 = v1 * v2 */
/* STATEMENT DELETED: run { val v2 = 1 println(v2) } */
run {
val v2 = 1
println(v2)
}
print(<caret>v2)