[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
+5
@@ -1180,6 +1180,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION by error<KtTypeAlias>(PositioningStrategy.TYPEALIAS_TYPE_REFERENCE) {
|
||||
parameter<ClassId>("typealiasedClassId")
|
||||
}
|
||||
|
||||
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning<KtElement>(PositioningStrategy.DECLARATION_NAME_ONLY) {
|
||||
parameter<Symbol>("expect")
|
||||
parameter<Symbol>("actual")
|
||||
}
|
||||
}
|
||||
|
||||
val DESTRUCTING_DECLARATION by object : DiagnosticGroup("Destructuring declaration") {
|
||||
|
||||
@@ -628,6 +628,7 @@ object FirErrors {
|
||||
val NOT_A_MULTIPLATFORM_COMPILATION by error0<PsiElement>()
|
||||
val EXPECT_ACTUAL_OPT_IN_ANNOTATION by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER)
|
||||
val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION by error1<KtTypeAlias, ClassId>(SourceElementPositioningStrategies.TYPEALIAS_TYPE_REFERENCE)
|
||||
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning2<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY)
|
||||
|
||||
// Destructuring declaration
|
||||
val INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION by error0<KtDestructuringDeclaration>()
|
||||
|
||||
+27
-4
@@ -20,17 +20,20 @@ import org.jetbrains.kotlin.fir.declarations.utils.isActual
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isTailRec
|
||||
import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.scopes.collectAllFunctions
|
||||
import org.jetbrains.kotlin.fir.scopes.getDeclaredConstructors
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.*
|
||||
@@ -176,7 +179,9 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
|
||||
else -> {}
|
||||
}
|
||||
val expectedSingleCandidate = symbol.getSingleExpectForActualOrNull()
|
||||
// We want to report errors even if a candidate is incompatible, but it's single
|
||||
val expectedSingleCandidate = compatibilityToMembersMap[Compatible]?.singleOrNull()
|
||||
?: symbol.getSingleExpectForActualOrNull()
|
||||
if (expectedSingleCandidate != null) {
|
||||
checkIfExpectHasDefaultArgumentsAndActualizedWithTypealias(
|
||||
expectedSingleCandidate,
|
||||
@@ -185,6 +190,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
reporter,
|
||||
)
|
||||
checkOptInAnnotation(declaration, expectedSingleCandidate, context, reporter)
|
||||
checkAnnotationsMatch(expectedSingleCandidate, symbol, context, reporter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +249,23 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkAnnotationsMatch(
|
||||
expectSymbol: FirBasedSymbol<*>,
|
||||
actualSymbol: FirBasedSymbol<*>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession)
|
||||
val incompatibility =
|
||||
AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, matchingContext) ?: return
|
||||
reporter.reportOn(
|
||||
actualSymbol.source, FirErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
|
||||
incompatibility.expectSymbol as FirBasedSymbol<*>,
|
||||
incompatibility.actualSymbol as FirBasedSymbol<*>,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
fun Map<out ExpectActualCompatibility<*>, *>.allStrongIncompatibilities(): Boolean {
|
||||
return keys.all { it is Incompatible && it.kind == IncompatibilityKind.STRONG }
|
||||
}
|
||||
|
||||
+10
@@ -178,4 +178,14 @@ object FirDiagnosticRenderers {
|
||||
}
|
||||
|
||||
val FUNCTIONAL_TYPE_KINDS = KtDiagnosticRenderers.COLLECTION(FUNCTIONAL_TYPE_KIND)
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
val SYMBOL_WITH_ANNOTATIONS = Renderer { s: FirBasedSymbol<*> ->
|
||||
FirRenderer(
|
||||
typeRenderer = ConeTypeRenderer(),
|
||||
idRenderer = ConeIdShortRenderer(),
|
||||
classMemberRenderer = null,
|
||||
bodyRenderer = null,
|
||||
).renderElementAsString(s.fir, trim = true)
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -51,6 +51,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_PROPERTY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_SUPER_CALL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_SUPER_CALL_WARNING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACCESSOR_FOR_DELEGATED_PROPERTY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_MISSING
|
||||
@@ -1854,6 +1855,13 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
"`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}",
|
||||
FirDiagnosticRenderers.SYMBOL_WITH_ANNOTATIONS, FirDiagnosticRenderers.SYMBOL_WITH_ANNOTATIONS
|
||||
)
|
||||
|
||||
// Destructuring declaration
|
||||
map.put(INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION, "Initializer required for destructuring declaration")
|
||||
|
||||
Reference in New Issue
Block a user