Add applicability checks & tests for SubclassOptInRequired

This commit is contained in:
Mikhail Glukhikh
2022-08-08 11:46:46 +02:00
committed by teamcity
parent 66e710704a
commit 80a9f22052
16 changed files with 431 additions and 18 deletions
@@ -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 {
@@ -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 {
@@ -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)
@@ -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
)
}
}
}
}
@@ -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,