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
@@ -1041,6 +1041,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.SUBCLASS_OPT_IN_INAPPLICABLE) { firDiagnostic ->
SubclassOptInInapplicableImpl(
firDiagnostic.a,
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.EXPOSED_TYPEALIAS_EXPANDED_TYPE) { firDiagnostic -> add(FirErrors.EXPOSED_TYPEALIAS_EXPANDED_TYPE) { firDiagnostic ->
ExposedTypealiasExpandedTypeImpl( ExposedTypealiasExpandedTypeImpl(
firDiagnostic.a, firDiagnostic.a,
@@ -751,6 +751,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = OptInMarkerOnOverrideWarning::class override val diagnosticClass get() = OptInMarkerOnOverrideWarning::class
} }
abstract class SubclassOptInInapplicable : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = SubclassOptInInapplicable::class
abstract val target: String
}
abstract class ExposedTypealiasExpandedType : KtFirDiagnostic<KtNamedDeclaration>() { abstract class ExposedTypealiasExpandedType : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = ExposedTypealiasExpandedType::class override val diagnosticClass get() = ExposedTypealiasExpandedType::class
abstract val elementVisibility: EffectiveVisibility abstract val elementVisibility: EffectiveVisibility
@@ -902,6 +902,12 @@ internal class OptInMarkerOnOverrideWarningImpl(
override val token: KtLifetimeToken, override val token: KtLifetimeToken,
) : KtFirDiagnostic.OptInMarkerOnOverrideWarning(), KtAbstractFirDiagnostic<KtAnnotationEntry> ) : KtFirDiagnostic.OptInMarkerOnOverrideWarning(), KtAbstractFirDiagnostic<KtAnnotationEntry>
internal class SubclassOptInInapplicableImpl(
override val target: String,
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.SubclassOptInInapplicable(), KtAbstractFirDiagnostic<KtAnnotationEntry>
internal class ExposedTypealiasExpandedTypeImpl( internal class ExposedTypealiasExpandedTypeImpl(
override val elementVisibility: EffectiveVisibility, override val elementVisibility: EffectiveVisibility,
override val restrictingDeclaration: KtSymbol, override val restrictingDeclaration: KtSymbol,
@@ -37548,6 +37548,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/overrideInAnonymousObject.kt"); 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 @Test
@TestMetadata("subClassOptInRequired.kt") @TestMetadata("subClassOptInRequired.kt")
public void testSubClassOptInRequired() throws Exception { public void testSubClassOptInRequired() throws Exception {
@@ -37548,6 +37548,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/overrideInAnonymousObject.kt"); 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 @Test
@TestMetadata("subClassOptInRequired.kt") @TestMetadata("subClassOptInRequired.kt")
public void testSubClassOptInRequired() throws Exception { public void testSubClassOptInRequired() throws Exception {
@@ -37548,6 +37548,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/overrideInAnonymousObject.kt"); 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 @Test
@TestMetadata("subClassOptInRequired.kt") @TestMetadata("subClassOptInRequired.kt")
public void testSubClassOptInRequired() throws Exception { 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.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase 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.FirModuleData
import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.* 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 by error<KtAnnotationEntry>()
val OPT_IN_MARKER_ON_OVERRIDE_WARNING by warning<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") { 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_WRONG_TARGET by error1<KtAnnotationEntry, String>()
val OPT_IN_MARKER_ON_OVERRIDE by error0<KtAnnotationEntry>() val OPT_IN_MARKER_ON_OVERRIDE by error0<KtAnnotationEntry>()
val OPT_IN_MARKER_ON_OVERRIDE_WARNING by warning0<KtAnnotationEntry>() val OPT_IN_MARKER_ON_OVERRIDE_WARNING by warning0<KtAnnotationEntry>()
val SUBCLASS_OPT_IN_INAPPLICABLE by error1<KtAnnotationEntry, String>()
// Exposed visibility // Exposed visibility
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<KtNamedDeclaration, EffectiveVisibility, FirBasedSymbol<*>, EffectiveVisibility>(SourceElementPositioningStrategies.DECLARATION_NAME) 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.KtSourceElement
import org.jetbrains.kotlin.config.AnalysisFlags import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.config.LanguageFeature 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.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.extractClassesFromArgument import org.jetbrains.kotlin.fir.analysis.checkers.extractClassesFromArgument
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn 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.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.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.arguments import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.languageVersionSettings 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.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.checkers.OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS
object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() { object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -27,6 +37,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
val classId = lookupTag.classId val classId = lookupTag.classId
val isRequiresOptIn = classId == OptInNames.REQUIRES_OPT_IN_CLASS_ID val isRequiresOptIn = classId == OptInNames.REQUIRES_OPT_IN_CLASS_ID
val isOptIn = classId == OptInNames.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) { if (isRequiresOptIn || isOptIn) {
checkOptInIsEnabled(expression.source, context, reporter) checkOptInIsEnabled(expression.source, context, reporter)
if (isOptIn) { if (isOptIn) {
@@ -34,21 +45,36 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
if (arguments.isEmpty()) { if (arguments.isEmpty()) {
reporter.reportOn(expression.source, FirErrors.OPT_IN_WITHOUT_ARGUMENTS, context) reporter.reportOn(expression.source, FirErrors.OPT_IN_WITHOUT_ARGUMENTS, context)
} else { } else {
val annotationClasses = expression.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS) val annotationClasses = expression.findArgumentByName(USE_EXPERIMENTAL_ANNOTATION_CLASS)
for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) { for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) {
with(FirOptInUsageBaseChecker) { checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
if (classSymbol.loadExperimentalityForMarkerAnnotation() == null) {
reporter.reportOn(
expression.source,
FirErrors.OPT_IN_ARGUMENT_IS_NOT_MARKER,
classSymbol.classId.asSingleFqName(),
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) 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.SINGLETON_IN_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE 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.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.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.SUPERTYPES_FOR_ANNOTATION_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE 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_MARKER_ON_OVERRIDE_WARNING,
"Opt-in requirement marker annotation on override makes no sense without the same marker on base declaration" "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(NOT_A_CLASS, "Not a class")
map.put( map.put(
WRONG_EXTENSION_FUNCTION_TYPE, WRONG_EXTENSION_FUNCTION_TYPE,
@@ -11,7 +11,6 @@ import com.intellij.psi.impl.source.tree.LeafPsiElement;
import kotlin.Pair; import kotlin.Pair;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.config.LanguageVersionSettings; import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.rendering.DeclarationWithDiagnosticComponents; import org.jetbrains.kotlin.diagnostics.rendering.DeclarationWithDiagnosticComponents;
@@ -27,7 +26,6 @@ import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier; import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier;
import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo; import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo;
@@ -43,7 +41,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.jetbrains.kotlin.config.LanguageFeature.ReportTypeVarianceConflictOnQualifierArguments;
import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.ACTUAL_DECLARATION_NAME; import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.ACTUAL_DECLARATION_NAME;
import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.INCOMPATIBLE_DECLARATION; import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.INCOMPATIBLE_DECLARATION;
import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*; import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*;
@@ -339,6 +336,8 @@ public interface Errors {
DiagnosticFactory0<KtAnnotationEntry> OPT_IN_MARKER_ON_OVERRIDE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtAnnotationEntry> OPT_IN_MARKER_ON_OVERRIDE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> OPT_IN_MARKER_ON_OVERRIDE_WARNING = DiagnosticFactory0.create(WARNING); DiagnosticFactory0<KtAnnotationEntry> OPT_IN_MARKER_ON_OVERRIDE_WARNING = DiagnosticFactory0.create(WARNING);
DiagnosticFactory1<KtAnnotationEntry, String> SUBCLASS_OPT_IN_INAPPLICABLE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS = DiagnosticFactory1.create(WARNING); DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS = DiagnosticFactory1.create(WARNING);
DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS_ERROR = DiagnosticFactory1.create(ERROR); DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS_ERROR = DiagnosticFactory1.create(ERROR);
@@ -180,6 +180,8 @@ public class DefaultErrorMessages {
MAP.put(OPT_IN_MARKER_ON_OVERRIDE, "Opt-in requirement marker annotation on override requires the same marker on base declaration"); MAP.put(OPT_IN_MARKER_ON_OVERRIDE, "Opt-in requirement marker annotation on override requires the same marker on base declaration");
MAP.put(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(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(EXPERIMENTAL_UNSIGNED_LITERALS, "{0}", STRING); MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS, "{0}", STRING);
MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS_ERROR, "{0}", STRING); MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS_ERROR, "{0}", STRING);
@@ -8,14 +8,15 @@ package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.* import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.*
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.resolve.AdditionalAnnotationChecker import org.jetbrains.kotlin.resolve.AdditionalAnnotationChecker
import org.jetbrains.kotlin.resolve.AnnotationChecker import org.jetbrains.kotlin.resolve.AnnotationChecker
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -47,6 +48,11 @@ class OptInMarkerDeclarationAnnotationChecker(private val module: ModuleDescript
.safeAs<ArrayValue>()?.value.orEmpty() .safeAs<ArrayValue>()?.value.orEmpty()
checkOptInUsage(annotationClasses, trace, entry) checkOptInUsage(annotationClasses, trace, entry)
} }
OptInNames.SUBCLASS_OPT_IN_REQUIRED_FQ_NAME -> {
val annotationClass =
annotation.allValueArguments[OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS]
checkSubclassOptInUsage(annotated, listOfNotNull(annotationClass), trace, entry)
}
in OptInNames.REQUIRES_OPT_IN_FQ_NAMES -> { in OptInNames.REQUIRES_OPT_IN_FQ_NAMES -> {
hasOptIn = true hasOptIn = true
} }
@@ -89,7 +95,53 @@ class OptInMarkerDeclarationAnnotationChecker(private val module: ModuleDescript
trace.report(Errors.OPT_IN_WITHOUT_ARGUMENTS.on(entry)) trace.report(Errors.OPT_IN_WITHOUT_ARGUMENTS.on(entry))
return return
} }
checkArgumentsAreMarkers(annotationClasses, trace, entry)
}
private fun checkSubclassOptInUsage(
annotated: KtAnnotated?,
annotationClasses: List<ConstantValue<*>>,
trace: BindingTrace,
entry: KtAnnotationEntry
) {
when (annotated) {
is KtAnnotatedExpression -> {
if (annotated.baseExpression is KtObjectLiteralExpression) {
trace.report(Errors.SUBCLASS_OPT_IN_INAPPLICABLE.on(entry, "object"))
}
return
}
is KtClassOrObject -> {
val descriptor = trace[BindingContext.CLASS, annotated]
if (descriptor != null) {
val kind = descriptor.kind
if (kind == ClassKind.OBJECT || kind == ClassKind.ENUM_CLASS || kind == ClassKind.ANNOTATION_CLASS) {
trace.report(Errors.SUBCLASS_OPT_IN_INAPPLICABLE.on(entry, kind.toString()))
return
}
if (kind != ClassKind.ENUM_ENTRY) {
// ^ We don't report anything on enum entries because it's anyway inapplicable target
val modality = descriptor.modality
if (modality != Modality.ABSTRACT && modality != Modality.OPEN) {
trace.report(Errors.SUBCLASS_OPT_IN_INAPPLICABLE.on(entry, "$modality $kind"))
return
}
if (descriptor.isFun) {
trace.report(Errors.SUBCLASS_OPT_IN_INAPPLICABLE.on(entry, "fun interface"))
return
}
if (annotated.isLocal) {
trace.report(Errors.SUBCLASS_OPT_IN_INAPPLICABLE.on(entry, "local $kind"))
return
}
}
}
}
}
checkArgumentsAreMarkers(annotationClasses, trace, entry)
}
private fun checkArgumentsAreMarkers(annotationClasses: List<ConstantValue<*>>, trace: BindingTrace, entry: KtAnnotationEntry) {
for (annotationClass in annotationClasses) { for (annotationClass in annotationClasses) {
val classDescriptor = val classDescriptor =
(annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor (annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor
@@ -0,0 +1,102 @@
// FIR_IDENTICAL
@RequiresOptIn
annotation class Marker
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
class Final
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
sealed class SealedClass {
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
object O : SealedClass()
}
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
sealed interface SealedInterface
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
fun interface FunInterface {
fun doIt()
}
sealed class Normal
// Ok!
@SubclassOptInRequired(Marker::class)
open class NormalOpen : Normal()
// Ok!
@SubclassOptInRequired(Marker::class)
abstract class NormalAbstract : Normal()
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
sealed class ErrorSealed : Normal()
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
class ErrorFinal : Normal()
// Ok!
@SubclassOptInRequired(Marker::class)
abstract class Abstract
// Error! Should be replaced with OptIn
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
class Derived1 : Abstract()
// Ok!
@OptIn(Marker::class)
class Derived2 : Abstract()
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
object Obj
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
enum class E1 {
FIRST;
}
enum class E2 {
// Error!
<!WRONG_ANNOTATION_TARGET!>@SubclassOptInRequired(Marker::class)<!>
SECOND;
}
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
annotation class A
// Local stuff
fun foo() {
// Error!
val v = <!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!> object : Any() {
}
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
open class OpenLocal
// Ok!
@OptIn(Marker::class)
class DerivedLocal : OpenLocal()
// Error!
<!SUBCLASS_OPT_IN_INAPPLICABLE!>@SubclassOptInRequired(Marker::class)<!>
class Local
}
// Common rules with OptIn
annotation class Simple
// Error! Opt-in marker required
<!OPT_IN_ARGUMENT_IS_NOT_MARKER!>@SubclassOptInRequired(Simple::class)<!>
open class Some
@@ -0,0 +1,166 @@
package
public fun foo(): kotlin.Unit
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public final annotation class A : kotlin.Annotation {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public abstract class Abstract {
public constructor Abstract()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public final class Derived1 : Abstract {
public constructor Derived1()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.OptIn(markerClass = {Marker::class}) public final class Derived2 : Abstract {
public constructor Derived2()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public final enum class E1 : kotlin.Enum<E1> {
enum entry FIRST
private constructor E1()
@kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E1): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<E1!>!
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E1
public final /*synthesized*/ fun values(): kotlin.Array<E1>
}
public final enum class E2 : kotlin.Enum<E2> {
@kotlin.SubclassOptInRequired(markerClass = Marker::class) enum entry SECOND
private constructor E2()
@kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E2): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<E2!>!
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E2
public final /*synthesized*/ fun values(): kotlin.Array<E2>
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public final class ErrorFinal : Normal {
public constructor ErrorFinal()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public sealed class ErrorSealed : Normal {
protected constructor ErrorSealed()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public final class Final {
public constructor Final()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public fun interface FunInterface {
public abstract fun doIt(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.RequiresOptIn public final annotation class Marker : kotlin.Annotation {
public constructor Marker()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public sealed class Normal {
protected constructor Normal()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public abstract class NormalAbstract : Normal {
public constructor NormalAbstract()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public open class NormalOpen : Normal {
public constructor NormalOpen()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public object Obj {
private constructor Obj()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public sealed class SealedClass {
protected constructor SealedClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public object O : SealedClass {
private constructor O()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@kotlin.SubclassOptInRequired(markerClass = Marker::class) public sealed interface SealedInterface {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final annotation class Simple : kotlin.Annotation {
public constructor Simple()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.SubclassOptInRequired(markerClass = Simple::class) public open class Some {
public constructor Some()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -37638,6 +37638,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/overrideInAnonymousObject.kt"); 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 @Test
@TestMetadata("subClassOptInRequired.kt") @TestMetadata("subClassOptInRequired.kt")
public void testSubClassOptInRequired() throws Exception { public void testSubClassOptInRequired() throws Exception {