From 77ab13400e2c1066f5bddc4b8be1ed5e7c3af71f Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Tue, 22 Aug 2023 16:21:43 +0200 Subject: [PATCH] [FE1.0] Fix false reporting of non-matching actual annotations in IDE It was reported because in composite module analysis we have both expect and actual annotation descriptors, which may differ. This may lead to bugs in the checker of ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT diagnostic. `fqName` comparison can be safely removed, because we already check types in `areCompatibleExpectActualTypes`. Tests: - typealiasedAnnotation.kt - for annotation use (so it has different ClassId) - typealiasedAnnotationAsArgument.kt - when typealiased annotation used in another annotation as argument - sourceRetentionAnnotationsWhenTypealias.kt - tests same as compiler/testData/diagnostics/tests/multiplatform/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt No special test added for opt-in, because it's prohibited to have expect/actual opt-ins by EXPECT_ACTUAL_OPT_IN_ANNOTATION checker. ^KTIJ-26700 --- ...DiagnosticsWithLightTreeTestGenerated.java | 28 +++++++++++++++++++ ...endMPPDiagnosticsWithPsiTestGenerated.java | 28 +++++++++++++++++++ .../ClassicExpectActualMatchingContext.kt | 24 +++++++++++++--- .../constExpressionValuesEqualityChecker.kt | 3 +- ...sourceRetentionAnnotationsWhenTypealias.kt | 15 ++++++++++ .../typealiasedAnnotation.kt | 22 +++++++++++++++ .../typealiasedAnnotationAsArgument.kt | 19 +++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 28 +++++++++++++++++++ 8 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotation.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotationAsArgument.kt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java index 654666d009a..cc0e2022666 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java @@ -1392,6 +1392,34 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst public void testSealedInheritorsInComplexModuleStructure() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/sealedInheritorsInComplexModuleStructure.kt"); } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching") + @TestDataPath("$PROJECT_ROOT") + public class AnnotationMatching { + @Test + public void testAllFilesPresentInAnnotationMatching() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("sourceRetentionAnnotationsWhenTypealias.kt") + public void testSourceRetentionAnnotationsWhenTypealias() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt"); + } + + @Test + @TestMetadata("typealiasedAnnotation.kt") + public void testTypealiasedAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotation.kt"); + } + + @Test + @TestMetadata("typealiasedAnnotationAsArgument.kt") + public void testTypealiasedAnnotationAsArgument() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotationAsArgument.kt"); + } + } } } diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java index 0907f1cfa8b..37a0a3c9d85 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java @@ -1392,6 +1392,34 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi public void testSealedInheritorsInComplexModuleStructure() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/sealedInheritorsInComplexModuleStructure.kt"); } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching") + @TestDataPath("$PROJECT_ROOT") + public class AnnotationMatching { + @Test + public void testAllFilesPresentInAnnotationMatching() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("sourceRetentionAnnotationsWhenTypealias.kt") + public void testSourceRetentionAnnotationsWhenTypealias() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt"); + } + + @Test + @TestMetadata("typealiasedAnnotation.kt") + public void testTypealiasedAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotation.kt"); + } + + @Test + @TestMetadata("typealiasedAnnotationAsArgument.kt") + public void testTypealiasedAnnotationAsArgument() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotationAsArgument.kt"); + } + } } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt index 79648708387..87b146b7815 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.multiplatform import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.mpp.* import org.jetbrains.kotlin.name.CallableId @@ -368,19 +369,34 @@ class ClassicExpectActualMatchingContext( ) } - private class AnnotationCallInfoImpl( + private inner class AnnotationCallInfoImpl( val annotationDescriptor: AnnotationDescriptor, ) : AnnotationCallInfo { override val annotationSymbol: AnnotationDescriptor = annotationDescriptor override val classId: ClassId? - get() = annotationDescriptor.annotationClass?.classId + get() = getAnnotationClassDescriptor()?.classId override val isRetentionSource: Boolean - get() = annotationDescriptor.isSourceAnnotation + get() = getAnnotationClassDescriptor()?.getAnnotationRetention() == KotlinRetention.SOURCE override val isOptIn: Boolean - get() = annotationDescriptor.annotationClass?.annotations?.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: false + get() = getAnnotationClassDescriptor()?.annotations?.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: false + + private fun getAnnotationClassDescriptor(): ClassDescriptor? { + val classDescriptor = annotationDescriptor.annotationClass ?: return null + if (!classDescriptor.isExpect) { + return classDescriptor + } + val classId = classDescriptor.classId + // For IDE composite module analysis, when actual annotation may differ + val platformDescriptor = platformModule.findClassifierAcrossModuleDependencies(classId) + return when (platformDescriptor) { + is ClassDescriptor -> platformDescriptor + is TypeAliasDescriptor -> platformDescriptor.classDescriptor ?: classDescriptor + else -> classDescriptor + } + } } override val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/constExpressionValuesEqualityChecker.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/constExpressionValuesEqualityChecker.kt index 977f46b6aa8..4c9e6c771f8 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/constExpressionValuesEqualityChecker.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/constExpressionValuesEqualityChecker.kt @@ -19,8 +19,7 @@ internal fun ExpectActualMatchingContext<*>.areExpressionConstValuesEqual( a is AnnotationDescriptor && b is AnnotationDescriptor -> { val aArgs = a.allValueArguments val bArgs = b.allValueArguments - a.fqName == b.fqName && - aArgs.size == bArgs.size && + aArgs.size == bArgs.size && areCompatibleExpectActualTypes(a.type, b.type) && aArgs.keys.all { k -> areExpressionConstValuesEqual(aArgs[k], bArgs[k], collectionArgumentsCompatibilityCheckStrategy) } } diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt new file mode 100644 index 00000000000..8e1fa87e5f3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt @@ -0,0 +1,15 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// MODULE: common +// TARGET_PLATFORM: Common +expect annotation class Ann() // No @Retention SOURCE set + +@Ann +expect annotation class CommonVolatile + +// MODULE: main()()(common) +// TARGET_PLATFORM: JVM +@Retention(AnnotationRetention.SOURCE) +actual annotation class Ann + +actual typealias CommonVolatile = kotlin.jvm.Volatile diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotation.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotation.kt new file mode 100644 index 00000000000..a67b0a2b78e --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotation.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// MODULE: common +// TARGET_PLATFORM: Common +expect annotation class Test() + +@Test +expect fun unexpandedOnActual() + +@Test +expect fun expandedOnActual() + +// MODULE: main()()(common) +annotation class JunitTestInLib + +actual typealias Test = JunitTestInLib + +@Test +actual fun unexpandedOnActual() {} + +@JunitTestInLib +actual fun expandedOnActual() {} + diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotationAsArgument.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotationAsArgument.kt new file mode 100644 index 00000000000..f1d9e6647a2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotationAsArgument.kt @@ -0,0 +1,19 @@ +// FIR_IDENTICAL +// DIAGNOSTICS: -TYPE_MISMATCH +// MODULE: common +// TARGET_PLATFORM: Common +expect annotation class Typealiased() + +annotation class Ann(val p: Typealiased) + +@Ann(Typealiased()) +expect fun test() + +// MODULE: main()()(common) +annotation class TypealiasedImpl + +actual typealias Typealiased = TypealiasedImpl + +@Ann(Typealiased()) +actual fun test() {} + diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 762c6c44535..1cd752d4e8b 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -24179,6 +24179,34 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { public void testSealedInheritorsInComplexModuleStructure() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/sealedInheritorsInComplexModuleStructure.kt"); } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching") + @TestDataPath("$PROJECT_ROOT") + public class AnnotationMatching { + @Test + public void testAllFilesPresentInAnnotationMatching() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); + } + + @Test + @TestMetadata("sourceRetentionAnnotationsWhenTypealias.kt") + public void testSourceRetentionAnnotationsWhenTypealias() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt"); + } + + @Test + @TestMetadata("typealiasedAnnotation.kt") + public void testTypealiasedAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotation.kt"); + } + + @Test + @TestMetadata("typealiasedAnnotationAsArgument.kt") + public void testTypealiasedAnnotationAsArgument() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasedAnnotationAsArgument.kt"); + } + } } }