Report warnings by enhanced base types
This commit is contained in:
+18
-19
@@ -35,11 +35,9 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerContext
|
import org.jetbrains.kotlin.types.checker.*
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
|
||||||
import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker
|
import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker
|
||||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
|
|
||||||
class JavaNullabilityChecker(val upperBoundChecker: UpperBoundChecker) : AdditionalTypeChecker {
|
class JavaNullabilityChecker(val upperBoundChecker: UpperBoundChecker) : AdditionalTypeChecker {
|
||||||
@@ -233,31 +231,21 @@ class JavaNullabilityChecker(val upperBoundChecker: UpperBoundChecker) : Additio
|
|||||||
) {
|
) {
|
||||||
if (TypeUtils.noExpectedType(expectedType)) return
|
if (TypeUtils.noExpectedType(expectedType)) return
|
||||||
|
|
||||||
val doesExpectedTypeContainsEnhancement = expectedType.contains { it is TypeWithEnhancement }
|
@Suppress("NAME_SHADOWING")
|
||||||
val doesExpressionTypeContainsEnhancement = expressionType.contains { it is TypeWithEnhancement }
|
val expressionType = exactedExpressionTypeByDataFlowNullability(expressionType, expressionTypeDataFlowValue, dataFlowInfo)
|
||||||
|
|
||||||
if (!doesExpectedTypeContainsEnhancement && !doesExpressionTypeContainsEnhancement) return
|
val isEnhancedExpectedTypeSubtypeOfExpressionType = typeCheckerForEnhancedTypes.isSubtypeOf(expressionType, expectedType)
|
||||||
|
|
||||||
val enhancedExpectedType = if (doesExpectedTypeContainsEnhancement) expectedType.unwrapEnhancementDeeply() else expectedType
|
|
||||||
val enhancedExpressionType = enhanceExpressionTypeByDataFlowNullability(
|
|
||||||
if (doesExpressionTypeContainsEnhancement) expressionType.unwrapEnhancementDeeply() else expressionType,
|
|
||||||
expressionTypeDataFlowValue,
|
|
||||||
dataFlowInfo
|
|
||||||
)
|
|
||||||
|
|
||||||
val isEnhancedExpectedTypeSubtypeOfExpressionType =
|
|
||||||
KotlinTypeChecker.DEFAULT.isSubtypeOf(enhancedExpressionType, enhancedExpectedType)
|
|
||||||
|
|
||||||
if (isEnhancedExpectedTypeSubtypeOfExpressionType) return
|
if (isEnhancedExpectedTypeSubtypeOfExpressionType) return
|
||||||
|
|
||||||
val isExpectedTypeSubtypeOfExpressionType = KotlinTypeChecker.DEFAULT.isSubtypeOf(expressionType, expectedType)
|
val isExpectedTypeSubtypeOfExpressionType = typeCheckerForBaseTypes.isSubtypeOf(expressionType, expectedType)
|
||||||
|
|
||||||
if (!isEnhancedExpectedTypeSubtypeOfExpressionType && isExpectedTypeSubtypeOfExpressionType) {
|
if (!isEnhancedExpectedTypeSubtypeOfExpressionType && isExpectedTypeSubtypeOfExpressionType) {
|
||||||
reportWarning(enhancedExpectedType, enhancedExpressionType)
|
reportWarning(expectedType.unwrapEnhancementDeeply(), expressionType.unwrapEnhancementDeeply())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enhanceExpressionTypeByDataFlowNullability(
|
private fun exactedExpressionTypeByDataFlowNullability(
|
||||||
expressionType: KotlinType,
|
expressionType: KotlinType,
|
||||||
expressionTypeDataFlowValue: () -> DataFlowValue,
|
expressionTypeDataFlowValue: () -> DataFlowValue,
|
||||||
dataFlowInfo: DataFlowInfo,
|
dataFlowInfo: DataFlowInfo,
|
||||||
@@ -276,6 +264,17 @@ class JavaNullabilityChecker(val upperBoundChecker: UpperBoundChecker) : Additio
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val typeCheckerForEnhancedTypes = NewKotlinTypeCheckerImpl(
|
||||||
|
kotlinTypeRefiner = KotlinTypeRefiner.Default,
|
||||||
|
kotlinTypePreparator = object : KotlinTypePreparator() {
|
||||||
|
override fun prepareType(type: KotlinTypeMarker): UnwrappedType =
|
||||||
|
super.prepareType(type).let { it.getEnhancementDeeply() ?: it }.unwrap()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val typeCheckerForBaseTypes = NewKotlinTypeCheckerImpl(KotlinTypeRefiner.Default)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnhancedNullabilityInfo(val enhancedType: KotlinType, val isFromJava: Boolean) {
|
class EnhancedNullabilityInfo(val enhancedType: KotlinType, val isFromJava: Boolean) {
|
||||||
|
|||||||
Vendored
+2
-4
@@ -42,8 +42,7 @@ fun main(ak: AK, akn: AKN, bk: BK, ck: CK, ckn: CKN): Unit {
|
|||||||
// jspecify_nullness_mismatch
|
// jspecify_nullness_mismatch
|
||||||
ak.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
ak.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
||||||
|
|
||||||
// jspecify_nullness_mismatch
|
akn.foo(null)
|
||||||
akn.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
|
||||||
|
|
||||||
bk.foo(bk)
|
bk.foo(bk)
|
||||||
// jspecify_nullness_mismatch
|
// jspecify_nullness_mismatch
|
||||||
@@ -53,6 +52,5 @@ fun main(ak: AK, akn: AKN, bk: BK, ck: CK, ckn: CKN): Unit {
|
|||||||
// jspecify_nullness_mismatch
|
// jspecify_nullness_mismatch
|
||||||
ck.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
ck.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
||||||
|
|
||||||
// jspecify_nullness_mismatch
|
ckn.foo(null)
|
||||||
ckn.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
|
||||||
}
|
}
|
||||||
Vendored
+2
-4
@@ -42,8 +42,7 @@ fun main(ak: AK, akn: AKN, bk: BK, ck: CK, ckn: CKN): Unit {
|
|||||||
// jspecify_nullness_mismatch
|
// jspecify_nullness_mismatch
|
||||||
ak.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
ak.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
||||||
|
|
||||||
// jspecify_nullness_mismatch
|
akn.foo(null) // the corresponding warning/error is present on the Java side
|
||||||
akn.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
|
||||||
|
|
||||||
bk.foo(bk)
|
bk.foo(bk)
|
||||||
// jspecify_nullness_mismatch
|
// jspecify_nullness_mismatch
|
||||||
@@ -53,6 +52,5 @@ fun main(ak: AK, akn: AKN, bk: BK, ck: CK, ckn: CKN): Unit {
|
|||||||
// jspecify_nullness_mismatch
|
// jspecify_nullness_mismatch
|
||||||
ck.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
ck.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
||||||
|
|
||||||
// jspecify_nullness_mismatch
|
ckn.foo(null) // the corresponding warning/error is present on the Java side
|
||||||
ckn.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>)
|
|
||||||
}
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
||||||
|
// SKIP_TXT
|
||||||
|
// MUTE_FOR_PSI_CLASS_FILES_READING
|
||||||
|
|
||||||
|
// We've already had errors in source mode, so it's relevant only for binaries for now
|
||||||
|
|
||||||
|
// FILE: Base.java
|
||||||
|
// INCLUDE_JAVA_AS_BINARY
|
||||||
|
public class Base<K> {}
|
||||||
|
|
||||||
|
// FILE: Test.java
|
||||||
|
// INCLUDE_JAVA_AS_BINARY
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
class Test extends Base<@Nullable String> {}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
fun takeBaseOfNotNullStrings(x: Base<String>) {}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val x = takeBaseOfNotNullStrings(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>Test()<!>)
|
||||||
|
}
|
||||||
+1
-1
@@ -11,7 +11,7 @@ public open class Basic_DisabledImprovements</*0*/ T : @org.jetbrains.annotation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public/*package*/ interface G</*0*/ T : kotlin.Any!> : test.Basic_DisabledImprovements.G2<@org.jetbrains.annotations.NotNull T!, @org.jetbrains.annotations.NotNull kotlin.String!> {
|
public/*package*/ interface G</*0*/ T : kotlin.Any!> : test.Basic_DisabledImprovements.G2<@org.jetbrains.annotations.NotNull T, @org.jetbrains.annotations.NotNull kotlin.String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public/*package*/ interface G2</*0*/ A : kotlin.Any!, /*1*/ B : kotlin.Any!> {
|
public/*package*/ interface G2</*0*/ A : kotlin.Any!, /*1*/ B : kotlin.Any!> {
|
||||||
|
|||||||
+6
@@ -1044,6 +1044,12 @@ public class ForeignAnnotationsCompiledJavaTestGenerated extends AbstractForeign
|
|||||||
public void testValueParameter_fir() throws Exception {
|
public void testValueParameter_fir() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/valueParameter.fir.kt");
|
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/valueParameter.fir.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("warningsBasedOnEnhancedBasedType.kt")
|
||||||
|
public void testWarningsBasedOnEnhancedBasedType() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/warningsBasedOnEnhancedBasedType.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -1044,6 +1044,12 @@ public class ForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated exte
|
|||||||
public void testValueParameter_fir() throws Exception {
|
public void testValueParameter_fir() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/valueParameter.fir.kt");
|
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/valueParameter.fir.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("warningsBasedOnEnhancedBasedType.kt")
|
||||||
|
public void testWarningsBasedOnEnhancedBasedType() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/warningsBasedOnEnhancedBasedType.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -1044,6 +1044,12 @@ public class ForeignAnnotationsSourceJavaTestGenerated extends AbstractForeignAn
|
|||||||
public void testValueParameter_fir() throws Exception {
|
public void testValueParameter_fir() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/valueParameter.fir.kt");
|
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/valueParameter.fir.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("warningsBasedOnEnhancedBasedType.kt")
|
||||||
|
public void testWarningsBasedOnEnhancedBasedType() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/warningsBasedOnEnhancedBasedType.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -215,10 +215,7 @@ class LazyJavaClassDescriptor(
|
|||||||
|
|
||||||
for (javaType in javaTypes) {
|
for (javaType in javaTypes) {
|
||||||
val kotlinType = c.typeResolver.transformJavaType(javaType, TypeUsage.SUPERTYPE.toAttributes())
|
val kotlinType = c.typeResolver.transformJavaType(javaType, TypeUsage.SUPERTYPE.toAttributes())
|
||||||
val areImprovementsInStrictMode = c.components.settings.typeEnhancementImprovementsInStrictMode
|
val enhancedKotlinType = c.components.signatureEnhancement.enhanceSuperType(kotlinType, c)
|
||||||
val enhancedKotlinType = if (areImprovementsInStrictMode) {
|
|
||||||
c.components.signatureEnhancement.enhanceSuperType(kotlinType, c)
|
|
||||||
} else kotlinType
|
|
||||||
|
|
||||||
if (enhancedKotlinType.constructor.declarationDescriptor is NotFoundClasses.MockClassDescriptor) {
|
if (enhancedKotlinType.constructor.declarationDescriptor is NotFoundClasses.MockClassDescriptor) {
|
||||||
incomplete.add(javaType)
|
incomplete.add(javaType)
|
||||||
|
|||||||
@@ -188,7 +188,10 @@ public class TypeSubstitutor implements TypeSubstitutorMarker {
|
|||||||
);
|
);
|
||||||
|
|
||||||
KotlinType substitutedEnhancement = substitute(enhancement, originalProjection.getProjectionKind());
|
KotlinType substitutedEnhancement = substitute(enhancement, originalProjection.getProjectionKind());
|
||||||
KotlinType resultingType = TypeWithEnhancementKt.wrapEnhancement(substitution.getType().unwrap(), substitutedEnhancement);
|
KotlinType resultingType = TypeWithEnhancementKt.wrapEnhancement(
|
||||||
|
substitution.getType().unwrap(),
|
||||||
|
substitutedEnhancement instanceof TypeWithEnhancement ? ((TypeWithEnhancement) substitutedEnhancement).getEnhancement() : substitutedEnhancement
|
||||||
|
);
|
||||||
|
|
||||||
return new TypeProjectionImpl(substitution.getProjectionKind(), resultingType);
|
return new TypeProjectionImpl(substitution.getProjectionKind(), resultingType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,11 +77,14 @@ class NewKotlinTypeCheckerImpl(
|
|||||||
override val overridingUtil: OverridingUtil = OverridingUtil.createWithTypeRefiner(kotlinTypeRefiner)
|
override val overridingUtil: OverridingUtil = OverridingUtil.createWithTypeRefiner(kotlinTypeRefiner)
|
||||||
|
|
||||||
override fun isSubtypeOf(subtype: KotlinType, supertype: KotlinType): Boolean =
|
override fun isSubtypeOf(subtype: KotlinType, supertype: KotlinType): Boolean =
|
||||||
ClassicTypeCheckerContext(true, kotlinTypeRefiner = kotlinTypeRefiner)
|
ClassicTypeCheckerContext(
|
||||||
.isSubtypeOf(subtype.unwrap(), supertype.unwrap()) // todo fix flag errorTypeEqualsToAnything
|
true, kotlinTypeRefiner = kotlinTypeRefiner, kotlinTypePreparator = kotlinTypePreparator
|
||||||
|
).isSubtypeOf(subtype.unwrap(), supertype.unwrap()) // todo fix flag errorTypeEqualsToAnything
|
||||||
|
|
||||||
override fun equalTypes(a: KotlinType, b: KotlinType): Boolean =
|
override fun equalTypes(a: KotlinType, b: KotlinType): Boolean =
|
||||||
ClassicTypeCheckerContext(false, kotlinTypeRefiner = kotlinTypeRefiner).equalTypes(a.unwrap(), b.unwrap())
|
ClassicTypeCheckerContext(
|
||||||
|
false, kotlinTypeRefiner = kotlinTypeRefiner, kotlinTypePreparator = kotlinTypePreparator
|
||||||
|
).equalTypes(a.unwrap(), b.unwrap())
|
||||||
|
|
||||||
fun ClassicTypeCheckerContext.equalTypes(a: UnwrappedType, b: UnwrappedType): Boolean {
|
fun ClassicTypeCheckerContext.equalTypes(a: UnwrappedType, b: UnwrappedType): Boolean {
|
||||||
return AbstractTypeChecker.equalTypes(this as AbstractTypeCheckerContext, a, b)
|
return AbstractTypeChecker.equalTypes(this as AbstractTypeCheckerContext, a, b)
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import java.util.Arrays.asList
|
import java.util.Arrays.asList
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
val x: /*T2@*/List</*T1@*/Int> = asList</*T0@*/Int>(1/*LIT*/)/*MutableList<T0@Int>*/
|
val x: /*T2@*/List</*T1@*/Int> = asList</*T0@*/Int>(1/*LIT*/)/*MutableList<T0@Int>!!L*/
|
||||||
val i: /*T3@*/Int = Integer/*LIT*/.valueOf(""/*LIT*/)/*Int!!L*/
|
val i: /*T3@*/Int = Integer/*LIT*/.valueOf(""/*LIT*/)/*Int!!L*/
|
||||||
}
|
}
|
||||||
|
|
||||||
//LOWER <: T0 due to 'PARAMETER'
|
//LOWER <: T0 due to 'PARAMETER'
|
||||||
//T1 := T0 due to 'INITIALIZER'
|
//T1 := T0 due to 'INITIALIZER'
|
||||||
|
//LOWER <: T2 due to 'INITIALIZER'
|
||||||
//LOWER <: T3 due to 'INITIALIZER'
|
//LOWER <: T3 due to 'INITIALIZER'
|
||||||
|
|||||||
Reference in New Issue
Block a user