From a466b14264948c5216e64535828023d3d52f1d75 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Fri, 20 Oct 2023 10:57:35 +0200 Subject: [PATCH] [FIR] Refactor: extract ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT checker ...into separate object. ^KT-62559 --- .../checkers/CommonDeclarationCheckers.kt | 1 + .../FirActualAnnotationsMatchExpectChecker.kt | 61 +++++++++++++++++++ .../FirExpectActualDeclarationChecker.kt | 39 +++--------- 3 files changed, 69 insertions(+), 32 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualAnnotationsMatchExpectChecker.kt diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index 178d93723cc..4f01c44bc34 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -29,6 +29,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirExposedVisibilityDeclarationChecker, FirCyclicTypeBoundsChecker, FirExpectActualDeclarationChecker, + FirActualAnnotationsMatchExpectChecker, FirAmbiguousAnonymousTypeChecker, FirExplicitApiDeclarationChecker, FirAnnotationChecker, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualAnnotationsMatchExpectChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualAnnotationsMatchExpectChecker.kt new file mode 100644 index 00000000000..4dd760cb8c6 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualAnnotationsMatchExpectChecker.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.checkers.declaration + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.InternalDiagnosticFactoryMethod +import org.jetbrains.kotlin.diagnostics.requireNotNull +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration +import org.jetbrains.kotlin.fir.declarations.getSingleMatchedExpectForActualOrNull +import org.jetbrains.kotlin.fir.declarations.utils.isActual +import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker + +// TODO(Roman.Efremov): KT-62559 prevent reporting ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT twice in CLI mode in K2 +internal object FirActualAnnotationsMatchExpectChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration !is FirMemberDeclaration) return + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiplatformRestrictions)) return + if (!declaration.isActual) return + + val actualSymbol = declaration.symbol + val expectSymbol = actualSymbol.getSingleMatchedExpectForActualOrNull() ?: return + checkAnnotationsMatch(expectSymbol, actualSymbol, context, reporter) + } + + @OptIn(InternalDiagnosticFactoryMethod::class) + 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 + val actualAnnotationTargetSourceElement = (incompatibility.actualAnnotationTargetElement as FirSourceElement).element + + reporter.report( + FirErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT.on( + actualSymbol.source.requireNotNull(), + incompatibility.expectSymbol as FirBasedSymbol<*>, + incompatibility.actualSymbol as FirBasedSymbol<*>, + actualAnnotationTargetSourceElement, + incompatibility.type.mapAnnotationType { it.annotationSymbol as FirAnnotation }, + positioningStrategy = null, + ), + context, + ) + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt index 5b29d9ca081..f7b199e9a6e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt @@ -8,8 +8,11 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.diagnostics.* -import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirExpectActualMatchingContext +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors @@ -18,7 +21,8 @@ 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.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory +import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol @@ -28,7 +32,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.mpp.RegularClassSymbolMarker import org.jetbrains.kotlin.name.StandardClassIds -import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility @@ -212,7 +215,6 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { } if (expectedSingleCandidate != null) { checkOptInAnnotation(declaration, expectedSingleCandidate, context, reporter) - checkAnnotationsMatch(expectedSingleCandidate, symbol, context, reporter) } } @@ -270,33 +272,6 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { } } - // TODO(Roman.Efremov): KT-62559 prevent reporting ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT twice in CLI mode in K2 - @OptIn(InternalDiagnosticFactoryMethod::class) - private fun checkAnnotationsMatch( - expectSymbol: FirBasedSymbol<*>, - actualSymbol: FirBasedSymbol<*>, - context: CheckerContext, - reporter: DiagnosticReporter - ) { - if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiplatformRestrictions)) return - val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession) - val incompatibility = - AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, matchingContext) ?: return - val actualAnnotationTargetSourceElement = (incompatibility.actualAnnotationTargetElement as FirSourceElement).element - - reporter.report( - FirErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT.on( - actualSymbol.source.requireNotNull(), - incompatibility.expectSymbol as FirBasedSymbol<*>, - incompatibility.actualSymbol as FirBasedSymbol<*>, - actualAnnotationTargetSourceElement, - incompatibility.type.mapAnnotationType { it.annotationSymbol as FirAnnotation }, - positioningStrategy = null, - ), - context, - ) - } - // we don't require `actual` modifier on // - annotation constructors, because annotation classes can only have one constructor // - value class primary constructors, because value class must have primary constructor