[FIR] Fix lower bound of flexible type variable not becoming DNN in warning-severity case
The bug was introduced as part of the implementation for Java nullability warnings. #KT-58933 Fixed
This commit is contained in:
committed by
Space Team
parent
711e7ce8d7
commit
31256e259e
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.applyIf
|
||||
|
||||
internal fun ConeKotlinType.enhance(session: FirSession, qualifiers: IndexedJavaTypeQualifiers): ConeKotlinType? =
|
||||
enhanceConeKotlinType(session, qualifiers, 0, mutableListOf<Int>().apply { computeSubtreeSizes(this) })
|
||||
@@ -115,6 +116,10 @@ private fun ConeSimpleKotlinType.enhanceInflexibleType(
|
||||
enhancedTag.constructType(typeArguments, isNullable, newAttributes)
|
||||
} else {
|
||||
this.withAttributes(newAttributes)
|
||||
}.applyIf(isFromDefinitelyNotNullType) {
|
||||
// If the original type was DNN, we need to wrap the result in a DNN type because `this` is the non-DNN part of the original.
|
||||
// In the non-warning case, this happens in the nested call and so `enhanced` is already DNN.
|
||||
ConeDefinitelyNotNullType.create(this, session.typeContext)
|
||||
}
|
||||
} else {
|
||||
enhanced
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
// JSPECIFY_STATE: warn
|
||||
// FILE: J1.java
|
||||
import org.jspecify.annotations.*;
|
||||
|
||||
@NullMarked
|
||||
public interface J1<T extends @Nullable Object> {
|
||||
T foo();
|
||||
}
|
||||
|
||||
// FILE: J2.java
|
||||
public interface J2 {
|
||||
J1<?> bar();
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
// jspecify_nullness_mismatch
|
||||
fun baz(j2: J2): Any = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>j2.bar().foo()<!> // Any..Any?
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// JSPECIFY_STATE: warn
|
||||
// FILE: J1.java
|
||||
import org.jspecify.annotations.*;
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
// JSPECIFY_STATE: warn
|
||||
// FILE: J1.java
|
||||
import org.jspecify.nullness.*;
|
||||
|
||||
@NullMarked
|
||||
public interface J1<T extends @Nullable Object> {
|
||||
T foo();
|
||||
}
|
||||
|
||||
// FILE: J2.java
|
||||
public interface J2 {
|
||||
J1<?> bar();
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
fun baz(j2: J2): Any = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>j2.bar().foo()<!> // Any..Any?
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// JSPECIFY_STATE: warn
|
||||
// FILE: J1.java
|
||||
import org.jspecify.nullness.*;
|
||||
|
||||
+20
@@ -62,3 +62,23 @@ abstract class A<T> : Provider<T> {
|
||||
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun getSet(x: T): T = x
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> fun getSetNullable(x: T): T = x
|
||||
}
|
||||
|
||||
abstract class B<T> : Provider<T> {
|
||||
override fun get(): T & Any = null!!
|
||||
override fun getNullable(): T? = null!!
|
||||
override fun set(x: T & Any) {} // False positive in K1
|
||||
override fun setNullable(x: T?) {}
|
||||
|
||||
override fun getSet(x: T & Any): T & Any = x // False positive in K1
|
||||
override fun getSetNullable(x: T?): T? = x
|
||||
}
|
||||
|
||||
abstract class C<T> : Provider<T> {
|
||||
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun getSet(x: T): T & Any = x!! // Missing warning in K1, K2 get's this right
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> fun getSetNullable(x: T): T? = x
|
||||
}
|
||||
|
||||
abstract class D<T> : Provider<T> {
|
||||
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun getSet(x: T & Any): T = x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!> // False positive in K1
|
||||
override fun getSetNullable(x: T?): T = x!!
|
||||
}
|
||||
Vendored
+20
@@ -62,3 +62,23 @@ abstract class A<T> : Provider<T> {
|
||||
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun getSet(x: T): T = x
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> fun getSetNullable(x: T): T = x
|
||||
}
|
||||
|
||||
abstract class B<T> : Provider<T> {
|
||||
override fun get(): T & Any = null!!
|
||||
override fun getNullable(): T? = null!!
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> fun set(x: T & Any) {} // False positive in K1
|
||||
override fun setNullable(x: T?) {}
|
||||
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> fun getSet(x: T & Any): T & Any = x // False positive in K1
|
||||
override fun getSetNullable(x: T?): T? = x
|
||||
}
|
||||
|
||||
abstract class C<T> : Provider<T> {
|
||||
override fun getSet(x: T): T & Any = x!! // Missing warning in K1, K2 get's this right
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> fun getSetNullable(x: T): T? = x
|
||||
}
|
||||
|
||||
abstract class D<T> : Provider<T> {
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> fun getSet(x: T & Any): T = x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!> // False positive in K1
|
||||
override fun getSetNullable(x: T?): T = x!!
|
||||
}
|
||||
Reference in New Issue
Block a user