[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
This commit is contained in:
Roman Efremov
2023-08-22 16:21:43 +02:00
committed by Space Team
parent dce1c1c70a
commit 77ab13400e
8 changed files with 161 additions and 6 deletions
@@ -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");
}
}
}
}
@@ -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");
}
}
}
}
@@ -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
@@ -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) }
}
@@ -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
@@ -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() {}
@@ -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() {}
@@ -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");
}
}
}
}