[FIR] Report empty intersection on responsible call

When reporting INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION, search
for a call to a declaration with the type parameter that got inferred
into an empty intersection inside the expression.

#KT-56377 Fixed
This commit is contained in:
Kirill Rakhman
2023-06-20 09:44:48 +02:00
committed by Space Team
parent 3f58782273
commit 585d4b3098
13 changed files with 187 additions and 8 deletions
@@ -0,0 +1,41 @@
interface I
open class C
fun completed(): String = "..."
fun <T> incomplete(): T = null!!
fun <T : I> incompatibleI(): T = null!!
fun <T : C> incompatibleC(): T = null!!
val p = false
fun expectUnit(x: Unit) = x
fun test1() = run {
if (p) return@run
completed() // ok, not returned
}
fun test2() = run {
if (p) return@run
incomplete() // ? either uninferred T or Unit
}
fun test3() = run {
if (p) return@run
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>incompatibleI<!>() // ? either uninferred T or error (Unit </: I)
}
fun test4() = run {
if (p) return@run
<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>incompatibleC<!>() // ? either uninferred T or error (Unit </: C)
}
fun main() {
// all ok
expectUnit(test1())
expectUnit(test2())
expectUnit(test3())
expectUnit(test4())
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
interface I
open class C
@@ -0,0 +1,12 @@
// !LANGUAGE: -ForbidInferringTypeVariablesIntoEmptyIntersection
// RENDER_DIAGNOSTICS_FULL_TEXT
open class Foo
inline fun <reified T : Foo> g(): T? = null
inline fun <R> f(block: ()->R?): R? {
return block()
}
fun main() {
f<Int> { <!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>g<!>() }
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: -ForbidInferringTypeVariablesIntoEmptyIntersection
// RENDER_DIAGNOSTICS_FULL_TEXT
open class Foo
@@ -0,0 +1,8 @@
// !LANGUAGE: -ForbidInferringTypeVariablesIntoEmptyIntersection
fun <T : <!FINAL_UPPER_BOUND!>String<!>> g(): T? = null
fun <R> f(block: () -> R?): R? = block()
fun main() {
f<Int> { <!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>g<!>() /* OK, g() is inferred into {Int & String}? */ }
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: -ForbidInferringTypeVariablesIntoEmptyIntersection
fun <T : <!FINAL_UPPER_BOUND!>String<!>> g(): T? = null
@@ -13,7 +13,7 @@ fun <T: A> emptyNullableListOfA(): List<T>? = null
fun testExclExcl() {
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION("T; a/A, kotlin/Int; final class and interface")!>doList<!>(emptyNullableListOfA()!!) //should be an error here
val l: List<Int> = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<a.A & kotlin.Int>")!><!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>id<!>(emptyNullableListOfA()!!)<!>
val l: List<Int> = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.List<a.A & kotlin.Int>")!>id(<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>emptyNullableListOfA<!>()!!)<!>
doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase)
}
@@ -0,0 +1,34 @@
// !LANGUAGE: +ForbidInferringTypeVariablesIntoEmptyIntersection
// FILE: ObjectNode.java
public interface ObjectNode {
<T extends JsonNode> T set(String fieldName, JsonNode value);
}
// FILE: JsonNode.java
public class JsonNode
// FILE: test.kt
interface JsonObject
class SomeJsonObject() : JsonObject
fun String.put(value: JsonObject?, node: ObjectNode) {
select(
node.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR!>set<!>(this, null),
Unit,
)
if (value == null) node.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR!>set<!>(this, null)
else if (value is SomeJsonObject) Unit
when (value) {
null -> node.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR!>set<!>(this, null)
is SomeJsonObject -> Unit
else -> TODO()
}
}
fun TODO(): Nothing = null!!
fun <K> select(vararg values: K): K = values[0]
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: +ForbidInferringTypeVariablesIntoEmptyIntersection
// FILE: ObjectNode.java
@@ -16,6 +15,14 @@ interface JsonObject
class SomeJsonObject() : JsonObject
fun String.put(value: JsonObject?, node: ObjectNode) {
<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR!>select<!>(
node.set(this, null),
Unit,
)
<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR!>if (value == null) node.set(this, null)
else if (value is SomeJsonObject) Unit<!>
<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR!>when (value) {
null -> node.set(this, null)
is SomeJsonObject -> Unit
@@ -24,3 +31,4 @@ fun String.put(value: JsonObject?, node: ObjectNode) {
}
fun TODO(): Nothing = null!!
fun <K> select(vararg values: K): K = values[0]
@@ -1,6 +1,7 @@
package
public fun TODO(): kotlin.Nothing
public fun </*0*/ K> select(/*0*/ vararg values: K /*kotlin.Array<out K>*/): K
public fun kotlin.String.put(/*0*/ value: JsonObject?, /*1*/ node: ObjectNode): kotlin.Unit
public open class JsonNode {
@@ -0,0 +1,35 @@
// !LANGUAGE: -ForbidInferringTypeVariablesIntoEmptyIntersection
// FILE: ObjectNode.java
public interface ObjectNode {
<T extends JsonNode> T set(String fieldName, JsonNode value);
}
// FILE: JsonNode.java
public class JsonNode
// FILE: test.kt
interface JsonObject
class SomeJsonObject() : JsonObject
fun String.put(value: JsonObject?, node: ObjectNode) {
select(
node.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>set<!>(this, null),
Unit,
)
if (value == null) node.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>set<!>(this, null)
else if (value is SomeJsonObject) Unit
when (value) {
null -> node.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>set<!>(this, null)
is SomeJsonObject -> Unit
else -> TODO()
}
}
fun TODO(): Nothing = null!!
fun <K> select(vararg values: K): K = values[0]
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: -ForbidInferringTypeVariablesIntoEmptyIntersection
// FILE: ObjectNode.java
@@ -16,6 +15,14 @@ interface JsonObject
class SomeJsonObject() : JsonObject
fun String.put(value: JsonObject?, node: ObjectNode) {
<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>select<!>(
node.set(this, null),
Unit,
)
<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>if (value == null) node.set(this, null)
else if (value is SomeJsonObject) Unit<!>
<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>when (value) {
null -> node.set(this, null)
is SomeJsonObject -> Unit
@@ -24,3 +31,5 @@ fun String.put(value: JsonObject?, node: ObjectNode) {
}
fun TODO(): Nothing = null!!
fun <K> select(vararg values: K): K = values[0]