[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:
committed by
Space Team
parent
b1bdb619d8
commit
1a4ab9bb4b
@@ -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);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
+5
@@ -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
|
||||
|
||||
+22
-1
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user