[FE] Skip more special annotations in KMP annotation matching

Many errors are reported in stdlib with these annotations
(SinceKotlin, Deprecated, so on).
But having them only on expect is a valid case. E.g. SinceKotlin added
if some old platform-specific API becomes commonized.

^KT-58551
This commit is contained in:
Roman Efremov
2023-06-27 12:39:44 +02:00
committed by Space Team
parent 1dcdcee452
commit ad84c83ee9
11 changed files with 69 additions and 26 deletions
@@ -287,9 +287,9 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
}
@Test
@TestMetadata("optionalExpectation.kt")
public void testOptionalExpectation() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/optionalExpectation.kt");
@TestMetadata("skippedAnnotations.kt")
public void testSkippedAnnotations() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/skippedAnnotations.kt");
}
@Test
@@ -287,9 +287,9 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
}
@Test
@TestMetadata("optionalExpectation.kt")
public void testOptionalExpectation() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/optionalExpectation.kt");
@TestMetadata("skippedAnnotations.kt")
public void testSkippedAnnotations() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/skippedAnnotations.kt");
}
@Test
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance
@@ -357,6 +358,9 @@ class FirExpectActualMatchingContextImpl private constructor(
override val isRetentionSource: Boolean
get() = getAnnotationClass()?.getRetention(actualSession) == AnnotationRetention.SOURCE
override val isOptIn: Boolean
get() = getAnnotationClass()?.hasAnnotation(OptInNames.REQUIRES_OPT_IN_CLASS_ID, actualSession) ?: false
private fun getAnnotationClass(): FirRegularClassSymbol? =
annotation.annotationTypeRef.coneType.toRegularClassSymbol(actualSession)
}
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance
@@ -467,10 +468,14 @@ internal abstract class IrExpectActualMatchingContext(
get() = irElement.type.getClass()?.classId
override val isRetentionSource: Boolean
get() {
val annotationClass = irElement.symbol.owner.parent as? IrClass ?: return false
return annotationClass.getAnnotationRetention() == KotlinRetention.SOURCE
}
get() = getAnnotationClass()?.getAnnotationRetention() == KotlinRetention.SOURCE
override val isOptIn: Boolean
get() = getAnnotationClass()?.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: false
private fun getAnnotationClass(): IrClass? {
return irElement.symbol.owner.parent as? IrClass
}
}
override val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean
@@ -10,6 +10,16 @@ import org.jetbrains.kotlin.mpp.TypeAliasSymbolMarker
import org.jetbrains.kotlin.name.StandardClassIds
object AbstractExpectActualAnnotationMatchChecker {
private val SKIPPED_CLASS_IDS = setOf(
StandardClassIds.Annotations.Deprecated,
StandardClassIds.Annotations.DeprecatedSinceKotlin,
StandardClassIds.Annotations.OptionalExpectation,
StandardClassIds.Annotations.RequireKotlin,
StandardClassIds.Annotations.SinceKotlin,
StandardClassIds.Annotations.Suppress,
StandardClassIds.Annotations.WasExperimental,
)
class Incompatibility(val expectSymbol: DeclarationSymbolMarker, val actualSymbol: DeclarationSymbolMarker)
fun areAnnotationsCompatible(
@@ -36,7 +46,7 @@ object AbstractExpectActualAnnotationMatchChecker {
val actualAnnotationsByName = actualSymbol.annotations.groupBy { it.classId }
for (expectAnnotation in expectSymbol.annotations) {
if (expectAnnotation.classId == StandardClassIds.Annotations.OptionalExpectation) {
if (expectAnnotation.classId in SKIPPED_CLASS_IDS || expectAnnotation.isOptIn) {
continue
}
if (expectAnnotation.isRetentionSource && skipSourceAnnotations) {
@@ -167,5 +167,6 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
interface AnnotationCallInfo {
val classId: ClassId?
val isRetentionSource: Boolean
val isOptIn: Boolean
}
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.components.ClassicTypeSystemContextForCS
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -351,6 +352,9 @@ class ClassicExpectActualMatchingContext(val platformModule: ModuleDescriptor) :
override val isRetentionSource: Boolean
get() = annotationDescriptor.isSourceAnnotation
override val isOptIn: Boolean
get() = annotationDescriptor.annotationClass?.annotations?.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: false
}
override val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean
@@ -1,12 +0,0 @@
// FIR_IDENTICAL
// WITH_STDLIB
// MODULE: m1-common
// FILE: common.kt
@OptIn(ExperimentalMultiplatform::class)
@OptionalExpectation
expect annotation class MyAnnotation
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@OptIn(ExperimentalMultiplatform::class)
actual annotation class MyAnnotation
@@ -0,0 +1,30 @@
// FIR_IDENTICAL
// WITH_STDLIB
// MODULE: m1-common
// FILE: common.kt
package kotlin
@OptIn(ExperimentalMultiplatform::class)
@OptionalExpectation
expect annotation class OptionalExpectationOnExpectOnly
@RequiresOptIn
annotation class MyOptIn
@SinceKotlin("1.8")
@Deprecated(message = "Some text")
@DeprecatedSinceKotlin("1.8")
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@MyOptIn
@WasExperimental(MyOptIn::class)
@kotlin.internal.RequireKotlin(version = "1.8")
expect fun skippedAnnotationsOnExpectOnly()
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
package kotlin
@OptIn(ExperimentalMultiplatform::class)
actual annotation class OptionalExpectationOnExpectOnly
actual fun skippedAnnotationsOnExpectOnly() {}
@@ -22832,9 +22832,9 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
}
@Test
@TestMetadata("optionalExpectation.kt")
public void testOptionalExpectation() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/optionalExpectation.kt");
@TestMetadata("skippedAnnotations.kt")
public void testSkippedAnnotations() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/skippedAnnotations.kt");
}
@Test
@@ -153,6 +153,7 @@ object StandardClassIds {
val ContextFunctionTypeParams = "ContextFunctionTypeParams".baseId()
val Deprecated = "Deprecated".baseId()
val DeprecatedSinceKotlin = "DeprecatedSinceKotlin".baseId()
val RequireKotlin = "RequireKotlin".internalId()
val HidesMembers = "HidesMembers".internalId()
val DynamicExtension = "DynamicExtension".internalId()