From 2005446296fa746838e7f45cb9947b07374676da Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Mon, 23 Oct 2023 11:57:32 +0200 Subject: [PATCH] [FIR] Preserve enhanced mutability when nullability was enhanced for warning #KT-56989 --- ...nAnnotationsCompiledJavaTestGenerated.java | 6 ++++ ...dJavaWithPsiClassReadingTestGenerated.java | 6 ++++ ...ignAnnotationsSourceJavaTestGenerated.java | 6 ++++ ...xpressionJavaNullabilityWarningCheckers.kt | 31 ++++++++++++------- .../fir/java/enhancement/javaTypeUtils.kt | 9 +++++- .../mutabilityAndNullabilityForWarning.fir.kt | 27 ++++++++++++++++ .../mutabilityAndNullabilityForWarning.kt | 27 ++++++++++++++++ ...nAnnotationsCompiledJavaTestGenerated.java | 6 ++++ ...dJavaWithPsiClassReadingTestGenerated.java | 6 ++++ ...ignAnnotationsSourceJavaTestGenerated.java | 6 ++++ 10 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.fir.kt create mode 100644 compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaTestGenerated.java index 38ad3ab3b3f..47bc89327dc 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaTestGenerated.java @@ -1189,6 +1189,12 @@ public class FirPsiOldFrontendForeignAnnotationsCompiledJavaTestGenerated extend runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/methodWithTypeParameter.kt"); } + @Test + @TestMetadata("mutabilityAndNullabilityForWarning.kt") + public void testMutabilityAndNullabilityForWarning() throws Exception { + runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt"); + } + @Test @TestMetadata("notNullVarargsOverrides.kt") public void testNotNullVarargsOverrides() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java index c443fa4d26d..7e7a9ca869e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java @@ -1247,6 +1247,12 @@ public class FirPsiOldFrontendForeignAnnotationsCompiledJavaWithPsiClassReadingT runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/methodWithTypeParameter.kt"); } + @Test + @TestMetadata("mutabilityAndNullabilityForWarning.kt") + public void testMutabilityAndNullabilityForWarning() throws Exception { + runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt"); + } + @Test @TestMetadata("notNullVarargsOverrides.kt") public void testNotNullVarargsOverrides() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsSourceJavaTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsSourceJavaTestGenerated.java index 0fc845e3ab2..8e11515d260 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsSourceJavaTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendForeignAnnotationsSourceJavaTestGenerated.java @@ -1247,6 +1247,12 @@ public class FirPsiOldFrontendForeignAnnotationsSourceJavaTestGenerated extends runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/methodWithTypeParameter.kt"); } + @Test + @TestMetadata("mutabilityAndNullabilityForWarning.kt") + public void testMutabilityAndNullabilityForWarning() throws Exception { + runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt"); + } + @Test @TestMetadata("notNullVarargsOverrides.kt") public void testNotNullVarargsOverrides() throws Exception { diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt index c9161028989..77f201c2218 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt @@ -151,7 +151,10 @@ internal fun FirExpression.checkExpressionForEnhancedTypeMismatch( val (actualTypeForComparison, expectedTypeForComparison) = getEnhancedTypesForComparison(actualType, expectedType, context) ?: return - if (!actualTypeForComparison.isSubtypeOf(context.session.typeContext, expectedTypeForComparison)) { + if (!actualTypeForComparison.isSubtypeOf(context.session.typeContext, expectedTypeForComparison) && + // Don't report anything if the original types didn't match. + actualType.isSubtypeOf(context.session.typeContext, expectedType.asExpectedFunctionTypeIfSam(actualType, context)) + ) { reporter.reportOn(source, factory, actualTypeForComparison, expectedTypeForComparison, context) } } @@ -175,16 +178,22 @@ private fun getEnhancedTypesForComparison( val actualTypeForComparison = enhancedActualType ?: actualType val expectedTypeForComparison = enhancedExpectedType ?: expectedType - val expectedTypeAsFunctionTypeIfSam = if ( - actualTypeForComparison.isSomeFunctionType(context.session) && - !expectedTypeForComparison.isSomeFunctionType(context.session) - ) { - // TODO remove after KT-62847 - val samResolver = FirSamResolver(context.session, context.scopeSession) - samResolver.getFunctionTypeForPossibleSamType(expectedTypeForComparison) ?: expectedTypeForComparison - } else { - expectedTypeForComparison - } + val expectedTypeAsFunctionTypeIfSam = expectedTypeForComparison.asExpectedFunctionTypeIfSam(actualTypeForComparison, context) return actualTypeForComparison to expectedTypeAsFunctionTypeIfSam } + +/** + * If the receiver is a SAM type and the [actualType] is a function type, converts the receiver to the corresponding function type. + * Otherwise, returns receiver. + * + * TODO remove after KT-62847 + * */ +private fun ConeKotlinType.asExpectedFunctionTypeIfSam(actualType: ConeKotlinType, context: CheckerContext): ConeKotlinType { + if (!actualType.isSomeFunctionType(context.session) || isSomeFunctionType(context.session)) { + return this + } + + val samResolver = FirSamResolver(context.session, context.scopeSession) + return samResolver.getFunctionTypeForPossibleSamType(this) ?: this +} diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/javaTypeUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/javaTypeUtils.kt index 4fed763b7ee..18cc25803c7 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/javaTypeUtils.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/javaTypeUtils.kt @@ -106,7 +106,14 @@ private fun ConeSimpleKotlinType.enhanceInflexibleType( ) return if (effectiveQualifiers.isNullabilityQualifierForWarning && enhanced != null) { - this.withAttributes(attributes.plus(EnhancedTypeForWarningAttribute(enhanced))) + val newAttributes = attributes.plus(EnhancedTypeForWarningAttribute(enhanced)) + + if (enhancedTag != lookupTag) { + // Handle case when mutability was enhanced and nullability was enhanced for warning. + enhancedTag.constructType(typeArguments, isNullable, newAttributes) + } else { + this.withAttributes(newAttributes) + } } else { enhanced } diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.fir.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.fir.kt new file mode 100644 index 00000000000..f0ada60007c --- /dev/null +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.fir.kt @@ -0,0 +1,27 @@ +// NULLABILITY_ANNOTATIONS: @org.jetbrains.annotations:warn +// DIAGNOSTICS: -UNUSED_PARAMETER + +// FILE: ReadOnly.java +package org.jetbrains.annotations; + +public @interface ReadOnly {} + +// FILE: J.java +import java.util.List; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.ReadOnly; + +public class J { + @ReadOnly + @Nullable + public static List foo() { + return null; + } +} + +// FILE: main.kt +fun main() { + takeMutable(J.foo()) +} + +fun takeMutable(l: MutableList) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt new file mode 100644 index 00000000000..293c669c128 --- /dev/null +++ b/compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt @@ -0,0 +1,27 @@ +// NULLABILITY_ANNOTATIONS: @org.jetbrains.annotations:warn +// DIAGNOSTICS: -UNUSED_PARAMETER + +// FILE: ReadOnly.java +package org.jetbrains.annotations; + +public @interface ReadOnly {} + +// FILE: J.java +import java.util.List; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.ReadOnly; + +public class J { + @ReadOnly + @Nullable + public static List foo() { + return null; + } +} + +// FILE: main.kt +fun main() { + takeMutable(J.foo()) +} + +fun takeMutable(l: MutableList) {} \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaTestGenerated.java index f648761a693..4eae38fe573 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaTestGenerated.java @@ -1189,6 +1189,12 @@ public class ForeignAnnotationsCompiledJavaTestGenerated extends AbstractForeign runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/methodWithTypeParameter.kt"); } + @Test + @TestMetadata("mutabilityAndNullabilityForWarning.kt") + public void testMutabilityAndNullabilityForWarning() throws Exception { + runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt"); + } + @Test @TestMetadata("notNullVarargsOverrides.kt") public void testNotNullVarargsOverrides() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java index a45fb8c3ae4..e821f33077a 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated.java @@ -1247,6 +1247,12 @@ public class ForeignAnnotationsCompiledJavaWithPsiClassReadingTestGenerated exte runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/methodWithTypeParameter.kt"); } + @Test + @TestMetadata("mutabilityAndNullabilityForWarning.kt") + public void testMutabilityAndNullabilityForWarning() throws Exception { + runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt"); + } + @Test @TestMetadata("notNullVarargsOverrides.kt") public void testNotNullVarargsOverrides() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsSourceJavaTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsSourceJavaTestGenerated.java index 06cf6d515df..494ab606d8b 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsSourceJavaTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ForeignAnnotationsSourceJavaTestGenerated.java @@ -1247,6 +1247,12 @@ public class ForeignAnnotationsSourceJavaTestGenerated extends AbstractForeignAn runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/methodWithTypeParameter.kt"); } + @Test + @TestMetadata("mutabilityAndNullabilityForWarning.kt") + public void testMutabilityAndNullabilityForWarning() throws Exception { + runTest("compiler/testData/diagnostics/foreignAnnotationsTests/java8Tests/misc/mutabilityAndNullabilityForWarning.kt"); + } + @Test @TestMetadata("notNullVarargsOverrides.kt") public void testNotNullVarargsOverrides() throws Exception {