Add applicability checks & tests for SubclassOptInRequired
This commit is contained in:
committed by
teamcity
parent
66e710704a
commit
80a9f22052
+6
@@ -37548,6 +37548,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/overrideInAnonymousObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("subClassOptInModality.kt")
|
||||
public void testSubClassOptInModality() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/subClassOptInModality.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("subClassOptInRequired.kt")
|
||||
public void testSubClassOptInRequired() throws Exception {
|
||||
|
||||
+6
@@ -37548,6 +37548,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/overrideInAnonymousObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("subClassOptInModality.kt")
|
||||
public void testSubClassOptInModality() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/subClassOptInModality.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("subClassOptInRequired.kt")
|
||||
public void testSubClassOptInRequired() throws Exception {
|
||||
|
||||
+4
-2
@@ -15,8 +15,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||
import org.jetbrains.kotlin.diagnostics.deprecationError2
|
||||
import org.jetbrains.kotlin.diagnostics.error0
|
||||
import org.jetbrains.kotlin.fir.FirModuleData
|
||||
import org.jetbrains.kotlin.fir.PrivateForInline
|
||||
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
|
||||
@@ -328,6 +326,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
}
|
||||
val OPT_IN_MARKER_ON_OVERRIDE by error<KtAnnotationEntry>()
|
||||
val OPT_IN_MARKER_ON_OVERRIDE_WARNING by warning<KtAnnotationEntry>()
|
||||
|
||||
val SUBCLASS_OPT_IN_INAPPLICABLE by error<KtAnnotationEntry> {
|
||||
parameter<String>("target")
|
||||
}
|
||||
}
|
||||
|
||||
val EXPOSED_VISIBILITY by object : DiagnosticGroup("Exposed visibility") {
|
||||
|
||||
@@ -271,6 +271,7 @@ object FirErrors {
|
||||
val OPT_IN_MARKER_ON_WRONG_TARGET by error1<KtAnnotationEntry, String>()
|
||||
val OPT_IN_MARKER_ON_OVERRIDE by error0<KtAnnotationEntry>()
|
||||
val OPT_IN_MARKER_ON_OVERRIDE_WARNING by warning0<KtAnnotationEntry>()
|
||||
val SUBCLASS_OPT_IN_INAPPLICABLE by error1<KtAnnotationEntry, String>()
|
||||
|
||||
// Exposed visibility
|
||||
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<KtNamedDeclaration, EffectiveVisibility, FirBasedSymbol<*>, EffectiveVisibility>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
|
||||
+55
-11
@@ -8,18 +8,28 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractClassesFromArgument
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.loadExperimentalityForMarkerAnnotation
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractClassFromArgument
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isFun
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.arguments
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS
|
||||
|
||||
object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
||||
override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -27,6 +37,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
||||
val classId = lookupTag.classId
|
||||
val isRequiresOptIn = classId == OptInNames.REQUIRES_OPT_IN_CLASS_ID
|
||||
val isOptIn = classId == OptInNames.OPT_IN_CLASS_ID
|
||||
val isSubclassOptIn = classId == OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID
|
||||
if (isRequiresOptIn || isOptIn) {
|
||||
checkOptInIsEnabled(expression.source, context, reporter)
|
||||
if (isOptIn) {
|
||||
@@ -34,21 +45,36 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
||||
if (arguments.isEmpty()) {
|
||||
reporter.reportOn(expression.source, FirErrors.OPT_IN_WITHOUT_ARGUMENTS, context)
|
||||
} else {
|
||||
val annotationClasses = expression.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS)
|
||||
val annotationClasses = expression.findArgumentByName(USE_EXPERIMENTAL_ANNOTATION_CLASS)
|
||||
for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) {
|
||||
with(FirOptInUsageBaseChecker) {
|
||||
if (classSymbol.loadExperimentalityForMarkerAnnotation() == null) {
|
||||
reporter.reportOn(
|
||||
expression.source,
|
||||
FirErrors.OPT_IN_ARGUMENT_IS_NOT_MARKER,
|
||||
classSymbol.classId.asSingleFqName(),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isSubclassOptIn) {
|
||||
val declaration = context.containingDeclarations.lastOrNull() as? FirClass
|
||||
if (declaration != null) {
|
||||
val kind = declaration.classKind
|
||||
if (kind == ClassKind.ENUM_CLASS || kind == ClassKind.OBJECT || kind == ClassKind.ANNOTATION_CLASS) {
|
||||
reporter.reportOn(expression.source, FirErrors.SUBCLASS_OPT_IN_INAPPLICABLE, kind.toString(), context)
|
||||
return
|
||||
}
|
||||
val modality = declaration.modality()
|
||||
if (modality == Modality.FINAL || modality == Modality.SEALED) {
|
||||
reporter.reportOn(expression.source, FirErrors.SUBCLASS_OPT_IN_INAPPLICABLE, "$modality $kind", context)
|
||||
return
|
||||
}
|
||||
if (declaration.isFun) {
|
||||
reporter.reportOn(expression.source, FirErrors.SUBCLASS_OPT_IN_INAPPLICABLE, "fun interface", context)
|
||||
return
|
||||
}
|
||||
if (declaration.isLocal) {
|
||||
reporter.reportOn(expression.source, FirErrors.SUBCLASS_OPT_IN_INAPPLICABLE, "local $kind", context)
|
||||
return
|
||||
}
|
||||
}
|
||||
val classSymbol = expression.findArgumentByName(USE_EXPERIMENTAL_ANNOTATION_CLASS)?.extractClassFromArgument() ?: return
|
||||
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,4 +91,22 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
||||
reporter.reportOn(element, FirErrors.OPT_IN_IS_NOT_ENABLED, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOptInArgumentIsMarker(
|
||||
classSymbol: FirRegularClassSymbol,
|
||||
source: KtSourceElement?,
|
||||
reporter: DiagnosticReporter,
|
||||
context: CheckerContext
|
||||
) {
|
||||
with(FirOptInUsageBaseChecker) {
|
||||
if (classSymbol.loadExperimentalityForMarkerAnnotation() == null) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.OPT_IN_ARGUMENT_IS_NOT_MARKER,
|
||||
classSymbol.classId.asSingleFqName(),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -469,6 +469,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SETTER_VISIBILITY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SINGLETON_IN_SUPERTYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SPREAD_OF_NULLABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUBCLASS_OPT_IN_INAPPLICABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPES_FOR_ANNOTATION_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE
|
||||
@@ -865,6 +866,8 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
OPT_IN_MARKER_ON_OVERRIDE_WARNING,
|
||||
"Opt-in requirement marker annotation on override makes no sense without the same marker on base declaration"
|
||||
)
|
||||
map.put(SUBCLASS_OPT_IN_INAPPLICABLE, "@SubclassOptInRequired is inapplicable on {0}", STRING)
|
||||
|
||||
map.put(NOT_A_CLASS, "Not a class")
|
||||
map.put(
|
||||
WRONG_EXTENSION_FUNCTION_TYPE,
|
||||
|
||||
Reference in New Issue
Block a user