[FE] Implement FE logic of expect actual annotations matching

This implementation only checks annotations set on expect/actual
declarations and requires further refinement (e.g. checking of other
annotation targets, class scopes within typealiases).

^KT-58551
This commit is contained in:
Roman Efremov
2023-06-22 15:22:17 +02:00
committed by Space Team
parent b1bdb619d8
commit 1a4ab9bb4b
49 changed files with 1481 additions and 25 deletions
@@ -840,6 +840,8 @@ public interface Errors {
DiagnosticFactory0<KtNamedDeclaration> EXPECT_ACTUAL_OPT_IN_ANNOTATION = DiagnosticFactory0.create(ERROR, EXPECT_ACTUAL_MODIFIER);
DiagnosticFactory1<KtTypeAlias, ClassId> ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION =
DiagnosticFactory1.create(ERROR, TYPEALIAS_TYPE_REFERENCE);
DiagnosticFactory2<KtNamedDeclaration, DeclarationDescriptor, DeclarationDescriptor> ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT
= DiagnosticFactory2.create(WARNING, DECLARATION_NAME_ONLY);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -396,6 +396,11 @@ public class DefaultErrorMessages {
MAP.put(ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION,
"`actual typealias` to annotation which affects code compilation can lead to incorrect behavior. Instead, use ''{0}'' annotation directly.",
TO_STRING);
MAP.put(ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
"All annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.\n" +
" Expected: {0}\n" +
" Actual: {1}",
DESCRIPTOR_WITH_ANNOTATIONS, DESCRIPTOR_WITH_ANNOTATIONS);
MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties");
MAP.put(SUPERTYPE_NOT_INITIALIZED, "This type has a constructor, and thus must be initialized here");
@@ -32,8 +32,10 @@ import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.AnnotationArgumentsRenderingPolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.DEBUG_TEXT
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.renderer.PropertyAccessorRenderingPolicy
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.inference.*
@@ -708,6 +710,14 @@ object Renderers {
val SHORT_NAMES_IN_TYPES = DescriptorRenderer.SHORT_NAMES_IN_TYPES.asRenderer()
@JvmField
val COMPACT_WITH_MODIFIERS = DescriptorRenderer.COMPACT_WITH_MODIFIERS.asRenderer()
@JvmField
val DESCRIPTOR_WITH_ANNOTATIONS = DescriptorRenderer.withOptions {
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
modifiers = modifiers.plus(DescriptorRendererModifier.ANNOTATIONS)
withDefinedIn = true
}.asRenderer()
@JvmField
val DEPRECATION_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.withOptions {
withoutTypeParameters = false
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor
import org.jetbrains.kotlin.resolve.descriptorUtil.isPrimaryConstructorOfInlineClass
@@ -338,9 +339,11 @@ class ExpectedActualDeclarationChecker(
checkOptInAnnotation(reportOn, descriptor, expected, trace)
}
}
val expectSingleCandidate = compatibility.values.singleOrNull()?.singleOrNull()
// We want to report errors even if a candidate is incompatible, but it's single
val expectSingleCandidate = (compatibility[Compatible] ?: compatibility.values.singleOrNull())?.singleOrNull()
if (expectSingleCandidate != null) {
checkIfExpectHasDefaultArgumentsAndActualizedWithTypealias(expectSingleCandidate, reportOn, trace)
checkAnnotationsMatch(expectSingleCandidate, descriptor, reportOn, trace)
}
}
@@ -443,6 +446,24 @@ class ExpectedActualDeclarationChecker(
}
}
private fun checkAnnotationsMatch(
expectDescriptor: MemberDescriptor,
actualDescriptor: MemberDescriptor,
reportOn: KtNamedDeclaration,
trace: BindingTrace
) {
val context = ClassicExpectActualMatchingContext(actualDescriptor.module)
val incompatibility =
AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectDescriptor, actualDescriptor, context) ?: return
trace.report(
Errors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT.on(
reportOn,
incompatibility.expectSymbol as DeclarationDescriptor,
incompatibility.actualSymbol as DeclarationDescriptor
)
)
}
companion object {
fun Map<out ExpectActualCompatibility<MemberDescriptor>, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
this.keys.all { it is Incompatible && it.kind == IncompatibilityKind.STRONG }