From df2c95e444bbf97a9c3dbf668d6787950c8f9b62 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Wed, 12 May 2021 19:32:01 +0300 Subject: [PATCH] [Jspecify] Use bounds of type parameters for warnings reporting as well --- .../NullnessUnspecifiedTypeParameter.fir.kt | 20 +++---- .../NullnessUnspecifiedTypeParameter.kt | 19 +++---- .../jspecify/warnMode/SelfType.fir.kt | 20 +++---- .../java8Tests/jspecify/warnMode/SelfType.kt | 20 +++---- .../TypeArgumentsFromParameterBounds.fir.kt | 8 +-- .../TypeArgumentsFromParameterBounds.kt | 8 +-- ...orTypeArgumentsInferenceWithNestedCalls.kt | 4 +- .../typeEnhancement/signatureEnhancement.kt | 52 ++++++++++++------- 8 files changed, 82 insertions(+), 69 deletions(-) diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.fir.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.fir.kt index 88d72080abb..33ddb1f6f2a 100644 --- a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.fir.kt +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.fir.kt @@ -14,23 +14,23 @@ public class Test {} // FILE: main.kt // jspecify_nullness_mismatch fun main(a1: NullnessUnspecifiedTypeParameter, a2: NullnessUnspecifiedTypeParameter<Any?>, x: Test): Unit { - // jspecify_nullness_mismatch{mute} - a1.foo(null) + // jspecify_nullness_mismatch + a1.foo(null) a1.foo(1) - // jspecify_nullness_mismatch{mute} - a2.foo(null) + // jspecify_nullness_mismatch + a2.foo(null) a2.foo(1) + // jspecify_nullness_mismatch, jspecify_nullness_mismatch + a1.bar(null, null) // jspecify_nullness_mismatch - a1.bar(null, null) - // jspecify_nullness_mismatch{mute} - a1.bar(x, null) + a1.bar(x, null) a1.bar(x, 1) + // jspecify_nullness_mismatch, jspecify_nullness_mismatch + a2.bar(null, null) // jspecify_nullness_mismatch - a2.bar(null, null) - // jspecify_nullness_mismatch{mute} - a2.bar(x, null) + a2.bar(x, null) a2.bar(x, 1) } diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.kt index da92c40346c..7b05a230ea9 100644 --- a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.kt +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/NullnessUnspecifiedTypeParameter.kt @@ -14,22 +14,23 @@ public class Test {} // FILE: main.kt // jspecify_nullness_mismatch fun main(a1: NullnessUnspecifiedTypeParameter, a2: NullnessUnspecifiedTypeParameter<Any?>, x: Test): Unit { - // jspecify_nullness_mismatch{mute} - a1.foo(null) + // jspecify_nullness_mismatch + a1.foo(null) a1.foo(1) - // jspecify_nullness_mismatch{mute} - a2.foo(null) + // jspecify_nullness_mismatch + a2.foo(null) a2.foo(1) + // jspecify_nullness_mismatch, jspecify_nullness_mismatch + a1.bar(null, null) // jspecify_nullness_mismatch - a1.bar(null, null) - a1.bar(x, null) + a1.bar(x, null) a1.bar(x, 1) + // jspecify_nullness_mismatch, jspecify_nullness_mismatch + a2.bar(null, null) // jspecify_nullness_mismatch - a2.bar(null, null) - // jspecify_nullness_mismatch{mute} - a2.bar(x, null) + a2.bar(x, null) a2.bar(x, 1) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.fir.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.fir.kt index 447ebaf3e83..eae15a6b15a 100644 --- a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.fir.kt +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.fir.kt @@ -37,20 +37,20 @@ public class CKN extends C<@Nullable CK> {} // FILE: main.kt fun main(ak: AK, akn: AKN, bk: BK, ck: CK, ckn: CKN): Unit { ak.foo(ak) - // jspecify_nullness_mismatch{mute} - ak.foo(null) + // jspecify_nullness_mismatch + ak.foo(null) - // jspecify_nullness_mismatch{mute} - akn.foo(null) + // jspecify_nullness_mismatch + akn.foo(null) bk.foo(bk) - // jspecify_nullness_mismatch{mute} - bk.foo(null) + // jspecify_nullness_mismatch + bk.foo(null) ck.foo(ck) - // jspecify_nullness_mismatch{mute} - ck.foo(null) + // jspecify_nullness_mismatch + ck.foo(null) - // jspecify_nullness_mismatch{mute} - ckn.foo(null) + // jspecify_nullness_mismatch + ckn.foo(null) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.kt index 447ebaf3e83..eae15a6b15a 100644 --- a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.kt +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/SelfType.kt @@ -37,20 +37,20 @@ public class CKN extends C<@Nullable CK> {} // FILE: main.kt fun main(ak: AK, akn: AKN, bk: BK, ck: CK, ckn: CKN): Unit { ak.foo(ak) - // jspecify_nullness_mismatch{mute} - ak.foo(null) + // jspecify_nullness_mismatch + ak.foo(null) - // jspecify_nullness_mismatch{mute} - akn.foo(null) + // jspecify_nullness_mismatch + akn.foo(null) bk.foo(bk) - // jspecify_nullness_mismatch{mute} - bk.foo(null) + // jspecify_nullness_mismatch + bk.foo(null) ck.foo(ck) - // jspecify_nullness_mismatch{mute} - ck.foo(null) + // jspecify_nullness_mismatch + ck.foo(null) - // jspecify_nullness_mismatch{mute} - ckn.foo(null) + // jspecify_nullness_mismatch + ckn.foo(null) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.fir.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.fir.kt index 4341f6aa5ee..a888731c574 100644 --- a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.fir.kt +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.fir.kt @@ -38,10 +38,10 @@ fun main( // jspecify_nullness_mismatch a.bar(aNotNullNullNull) - // jspecify_nullness_mismatch{mute} - b.bar(aNotNullNotNullNotNull) - // jspecify_nullness_mismatch{mute} - b.bar(aNotNullNotNullNull) + // jspecify_nullness_mismatch + b.bar(aNotNullNotNullNotNull) + // jspecify_nullness_mismatch + b.bar(aNotNullNotNullNull) b.bar(aNotNullNullNotNull) b.bar(aNotNullNullNull) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.kt index 4341f6aa5ee..a888731c574 100644 --- a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.kt +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/jspecify/warnMode/TypeArgumentsFromParameterBounds.kt @@ -38,10 +38,10 @@ fun main( // jspecify_nullness_mismatch a.bar(aNotNullNullNull) - // jspecify_nullness_mismatch{mute} - b.bar(aNotNullNotNullNotNull) - // jspecify_nullness_mismatch{mute} - b.bar(aNotNullNotNullNull) + // jspecify_nullness_mismatch + b.bar(aNotNullNotNullNotNull) + // jspecify_nullness_mismatch + b.bar(aNotNullNotNullNull) b.bar(aNotNullNullNotNull) b.bar(aNotNullNullNull) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt index 79691b4f1bd..a21eeae5748 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt @@ -14,5 +14,5 @@ typealias C2 = MapLike val test1 = C(1, C(2, null)) val test2 = C(1, C("", null)) -val test23 = C2(if (true) 1 else null) -val test234 = C2(C2(if (true) 1 else null)) +val test23 = C2(if (true) 1 else null) +val test234 = C2(C2(if (true) 1 else null)) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt index 63c304b99bc..27447a64da0 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt @@ -411,23 +411,24 @@ class SignatureEnhancement( } val boundsFromTypeParameterForArgument = typeParameterForArgument?.boundsNullability() ?: return result - if (result == null) return NullabilityQualifierWithMigrationStatus(boundsFromTypeParameterForArgument) + if (result == null) return boundsFromTypeParameterForArgument - return NullabilityQualifierWithMigrationStatus( - mostSpecific(boundsFromTypeParameterForArgument, result.qualifier) - ) + return mostSpecific(boundsFromTypeParameterForArgument, result) } - private fun mostSpecific(a: NullabilityQualifier, b: NullabilityQualifier): NullabilityQualifier { - if (a == NullabilityQualifier.FORCE_FLEXIBILITY) return b - if (b == NullabilityQualifier.FORCE_FLEXIBILITY) return a - if (a == NullabilityQualifier.NULLABLE) return b - if (b == NullabilityQualifier.NULLABLE) return a - assert(a == b && a == NullabilityQualifier.NOT_NULL) { + private fun mostSpecific( + a: NullabilityQualifierWithMigrationStatus, + b: NullabilityQualifierWithMigrationStatus + ): NullabilityQualifierWithMigrationStatus { + if (a.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return b + if (b.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return a + if (a.qualifier == NullabilityQualifier.NULLABLE) return b + if (b.qualifier == NullabilityQualifier.NULLABLE) return a + assert(a.qualifier == b.qualifier && a.qualifier == NullabilityQualifier.NOT_NULL) { "Expected everything is NOT_NULL, but $a and $b are found" } - return NullabilityQualifier.NOT_NULL + return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL) } private fun KotlinType.nullabilityInfoBoundsForTypeParameterUsage(): Pair { @@ -440,20 +441,31 @@ class SignatureEnhancement( // void foo(T t); // should be loaded as "fun foo(t: T)" but not as "fun foo(t: T?)" // } return Pair( - NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL), - typeParameterBoundsNullability == NullabilityQualifier.NOT_NULL + NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL, typeParameterBoundsNullability.isForWarningOnly), + typeParameterBoundsNullability.qualifier == NullabilityQualifier.NOT_NULL ) } - private fun TypeParameterDescriptor.boundsNullability(): NullabilityQualifier? { + private fun TypeParameterDescriptor.boundsNullability(): NullabilityQualifierWithMigrationStatus? { // Do not use bounds from Kotlin-defined type parameters - if (this !is LazyJavaTypeParameterDescriptor) return null - return when { - upperBounds.all(KotlinType::isError) -> null - upperBounds.all(KotlinType::isNullabilityFlexible) -> null - upperBounds.any { !it.isNullable() } -> NullabilityQualifier.NOT_NULL - else -> NullabilityQualifier.NULLABLE + if (this !is LazyJavaTypeParameterDescriptor || upperBounds.all(KotlinType::isError)) return null + + if (upperBounds.all(KotlinType::isNullabilityFlexible)) { + if (upperBounds.any { it is FlexibleTypeWithEnhancement && !it.enhancement.isNullable() }) { + return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL, isForWarningOnly = true) + } + + if (upperBounds.any { it is FlexibleTypeWithEnhancement && it.enhancement.isNullable() }) { + return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE, isForWarningOnly = true) + } + + return null } + + val resultingQualifier = + if (upperBounds.any { !it.isNullable() }) NullabilityQualifier.NOT_NULL else NullabilityQualifier.NULLABLE + + return NullabilityQualifierWithMigrationStatus(resultingQualifier) } private fun Annotations.extractNullability(