FIR: enhance Java type parameter bounds

This also fixes handling of self-recursive types.

TODO: fix jspecify/{strict,warn}Mode/Captured.fir.kt
This commit is contained in:
pyos
2021-08-12 18:55:44 +02:00
committed by teamcityserver
parent e2a48d3945
commit 03304bce96
30 changed files with 222 additions and 382 deletions
@@ -0,0 +1,17 @@
// JSPECIFY_STATE: strict
// !LANGUAGE: +TypeEnhancementImprovementsInStrictMode
// FILE: J1.java
import org.jetbrains.annotations.Nullable;
public interface J1<T> {
@Nullable
public static <T> T foo(J1<T> x) { return null; }
}
// FILE: J2.java
import org.jspecify.nullness.Nullable;
public interface J2<V extends @Nullable Object> extends J1<V> { }
// FILE: kotlin.kt
private fun J2<*>.bar() = <!NEW_INFERENCE_ERROR!>J1.foo(this)<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// JSPECIFY_STATE: strict
// !LANGUAGE: +TypeEnhancementImprovementsInStrictMode
// FILE: J1.java
@@ -1,27 +0,0 @@
// !LANGUAGE: +ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated +TypeEnhancementImprovementsInStrictMode
// JSPECIFY_STATE: strict
// MUTE_FOR_PSI_CLASS_FILES_READING
// FILE: NonPlatformTypeParameter.java
import org.jspecify.nullness.*;
public class NonPlatformTypeParameter<T extends @Nullable Object> {
public void foo(T t) {}
public <E extends @Nullable Object> void bar(E e) {}
}
// FILE: Test.java
public class Test {}
// FILE: main.kt
fun <T : Test> main(a1: NonPlatformTypeParameter<Any?>, a2: NonPlatformTypeParameter<Test>, x: T): Unit {
a1.foo(null)
a1.bar<Test?>(null)
a1.bar<T>(null)
a1.bar<T>(x)
a2.foo(null)
a2.bar<Test?>(null)
a2.bar<T>(null)
a2.bar<T>(x)
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated +TypeEnhancementImprovementsInStrictMode
// JSPECIFY_STATE: strict
// MUTE_FOR_PSI_CLASS_FILES_READING
@@ -14,16 +14,19 @@ public class NullnessUnspecifiedTypeParameter<T> {
public class Test {}
// FILE: main.kt
fun main(a1: NullnessUnspecifiedTypeParameter<Any>, a2: NullnessUnspecifiedTypeParameter<Any?>, x: Test): Unit {
a1.foo(null)
// jspecify_nullness_mismatch
fun main(a1: NullnessUnspecifiedTypeParameter<Any>, a2: NullnessUnspecifiedTypeParameter<<!UPPER_BOUND_VIOLATED!>Any?<!>>, x: Test): Unit {
// jspecify_nullness_mismatch
a1.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.foo(1)
a2.foo(null)
a2.foo(1)
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
a1.bar(<!NULL_FOR_NONNULL_TYPE!>null<!>, <!NULL_FOR_NONNULL_TYPE!>null<!>)
// jspecify_nullness_mismatch
a1.bar(<!NULL_FOR_NONNULL_TYPE!>null<!>, null)
a1.bar(x, null)
a1.bar(x, <!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.bar(x, 1)
// jspecify_nullness_mismatch
@@ -21,20 +21,26 @@ public class B<T> {
public class Test {}
// FILE: main.kt
fun <T : Test> main(a1: A<Any?>, a2: A<Test>, b1: B<Any?>, b2: B<Test>, x: T): Unit {
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
fun <T : Test> main(a1: A<<!UPPER_BOUND_VIOLATED!>Any?<!>>, a2: A<Test>, b1: B<<!UPPER_BOUND_VIOLATED!>Any?<!>>, b2: B<Test>, x: T): Unit {
a1.foo(null)
a1.bar<T?>(null)
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
a1.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.bar<T>(x)
a2.foo(null)
a2.bar<T?>(null)
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
a2.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a2.bar<T>(x)
b1.foo(null)
b1.bar<T?>(null)
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
b1.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b1.bar<T>(x)
b2.foo(null)
b2.bar<T?>(null)
// jspecify_nullness_mismatch
b2.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
b2.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b2.bar<T>(x)
}
@@ -99,7 +99,8 @@ fun test3() {
// String in Foo is not null
isNotNullAndNullableStringInFoo(
UtilNullMarkedGeneric.getFooOfK(),
UtilNullMarkedGeneric.getFooOfK()
// jspecify_nullness_mismatch
<!ARGUMENT_TYPE_MISMATCH!>UtilNullMarkedGeneric.getFooOfK()<!>
)
}
@@ -14,5 +14,5 @@ public class Foo<T extends @Nullable Object> {
// FILE: main.kt
fun test(): Foo<String> {
return <!DEBUG_INFO_EXPRESSION_TYPE("Foo<kotlin.String..kotlin.String?!>")!>Foo.create()<!>
return <!DEBUG_INFO_EXPRESSION_TYPE("Foo<kotlin.String>")!>Foo.create()<!>
}
@@ -0,0 +1,17 @@
// JSPECIFY_STATE: warn
// !LANGUAGE: +TypeEnhancementImprovementsInStrictMode
// FILE: J1.java
import org.jetbrains.annotations.Nullable;
public interface J1<T> {
@Nullable
public static <T> T foo(J1<T> x) { return null; }
}
// FILE: J2.java
import org.jspecify.nullness.Nullable;
public interface J2<V extends @Nullable Object> extends J1<V> { }
// FILE: kotlin.kt
private fun J2<*>.bar() = <!NEW_INFERENCE_ERROR!>J1.foo(this)<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// JSPECIFY_STATE: warn
// !LANGUAGE: +TypeEnhancementImprovementsInStrictMode
// FILE: J1.java
@@ -15,4 +14,4 @@ import org.jspecify.nullness.Nullable;
public interface J2<V extends @Nullable Object> extends J1<V> { }
// FILE: kotlin.kt
private fun J2<*>.bar() = J1.foo(this)
private fun J2<*>.bar() = J1.foo(this)
@@ -17,11 +17,11 @@ public class Test {}
fun <T : Test> main(a1: NonPlatformTypeParameter<Any?>, a2: NonPlatformTypeParameter<Test>, x: T): Unit {
a1.foo(null)
a1.bar<Test?>(null)
a1.bar<T>(null)
a1.bar<T>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.bar<T>(x)
a2.foo(null)
a2.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a2.bar<Test?>(null)
a2.bar<T>(null)
a2.bar<T>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a2.bar<T>(x)
}
@@ -13,15 +13,15 @@ public class NullnessUnspecifiedTypeParameter<T> {
public class Test {}
// FILE: main.kt
fun main(a1: NullnessUnspecifiedTypeParameter<Any>, a2: NullnessUnspecifiedTypeParameter<Any?>, x: Test): Unit {
a1.foo(null)
fun main(a1: NullnessUnspecifiedTypeParameter<Any>, a2: NullnessUnspecifiedTypeParameter<<!UPPER_BOUND_VIOLATED!>Any?<!>>, x: Test): Unit {
a1.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.foo(1)
a2.foo(null)
a2.foo(1)
a1.bar(<!NULL_FOR_NONNULL_TYPE!>null<!>, null)
a1.bar(x, null)
a1.bar(<!NULL_FOR_NONNULL_TYPE!>null<!>, <!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.bar(x, <!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.bar(x, 1)
a2.bar(<!NULL_FOR_NONNULL_TYPE!>null<!>, null)
@@ -20,20 +20,20 @@ public class B<T> {
public class Test {}
// FILE: main.kt
fun <T : Test> main(a1: A<Any?>, a2: A<Test>, b1: B<Any?>, b2: B<Test>, x: T): Unit {
fun <T : Test> main(a1: A<<!UPPER_BOUND_VIOLATED!>Any?<!>>, a2: A<Test>, b1: B<<!UPPER_BOUND_VIOLATED!>Any?<!>>, b2: B<Test>, x: T): Unit {
a1.foo(null)
a1.bar<T?>(null)
a1.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a1.bar<T>(x)
a2.foo(null)
a2.bar<T?>(null)
a2.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
a2.bar<T>(x)
b1.foo(null)
b1.bar<T?>(null)
b1.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b1.bar<T>(x)
b2.foo(null)
b2.bar<T?>(null)
b2.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b2.bar<<!UPPER_BOUND_VIOLATED!>T?<!>>(<!NULL_FOR_NONNULL_TYPE!>null<!>)
b2.bar<T>(x)
}
@@ -13,13 +13,13 @@ public class ClassTypeParameterBound <T extends @NotNull String> {
}
// FILE: main.kt
fun main(x: ClassTypeParameterBound<String?>, y: ClassTypeParameterBound<String>, a: String?, b: String) {
val x2 = ClassTypeParameterBound<String?>()
fun main(x: ClassTypeParameterBound<<!UPPER_BOUND_VIOLATED!>String?<!>>, y: ClassTypeParameterBound<String>, a: String?, b: String) {
val x2 = ClassTypeParameterBound<<!UPPER_BOUND_VIOLATED!>String?<!>>()
val y2 = ClassTypeParameterBound<String>()
val x3 = ClassTypeParameterBound(a)
val x3 = ClassTypeParameterBound(<!ARGUMENT_TYPE_MISMATCH!>a<!>)
val y3 = ClassTypeParameterBound(b)
val x4: ClassTypeParameterBound<String?> = ClassTypeParameterBound()
val x4: ClassTypeParameterBound<<!UPPER_BOUND_VIOLATED!>String?<!>> = <!TYPE_MISMATCH!>ClassTypeParameterBound()<!>
val y4: ClassTypeParameterBound<String> = ClassTypeParameterBound()
}
@@ -13,13 +13,13 @@ public class ClassTypeParameterBoundWithWarnings <T extends @NotNull String> {
}
// FILE: main.kt
fun main(x: ClassTypeParameterBoundWithWarnings<String?>, y: ClassTypeParameterBoundWithWarnings<String>, a: String?, b: String) {
val x2 = ClassTypeParameterBoundWithWarnings<String?>()
fun main(x: ClassTypeParameterBoundWithWarnings<<!UPPER_BOUND_VIOLATED!>String?<!>>, y: ClassTypeParameterBoundWithWarnings<String>, a: String?, b: String) {
val x2 = ClassTypeParameterBoundWithWarnings<<!UPPER_BOUND_VIOLATED!>String?<!>>()
val y2 = ClassTypeParameterBoundWithWarnings<String>()
val x3 = ClassTypeParameterBoundWithWarnings(a)
val x3 = ClassTypeParameterBoundWithWarnings(<!ARGUMENT_TYPE_MISMATCH!>a<!>)
val y3 = ClassTypeParameterBoundWithWarnings(b)
val x4: ClassTypeParameterBoundWithWarnings<String?> = ClassTypeParameterBoundWithWarnings()
val x4: ClassTypeParameterBoundWithWarnings<<!UPPER_BOUND_VIOLATED!>String?<!>> = <!TYPE_MISMATCH!>ClassTypeParameterBoundWithWarnings()<!>
val y4: ClassTypeParameterBoundWithWarnings<String> = ClassTypeParameterBoundWithWarnings()
}
@@ -10,5 +10,5 @@ public interface MapLike<@org.jetbrains.annotations.NotNull K> {
// FILE: main.kt
fun test2(map : MapLike<Int>, x2: Int?) {
map.put(x2)
map.put(<!ARGUMENT_TYPE_MISMATCH!>x2<!>)
}