Remove Experimental and UseExperimental from compiler OptIn usage checkers

This commit is contained in:
Abduqodiri Qurbonzoda
2022-09-13 17:12:42 +03:00
committed by Space
parent bc684b8d36
commit f2b0c30cba
5 changed files with 26 additions and 54 deletions
@@ -29,7 +29,7 @@ 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
import org.jetbrains.kotlin.resolve.checkers.OptInNames.OPT_IN_ANNOTATION_CLASS
object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -45,7 +45,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
if (arguments.isEmpty()) {
reporter.reportOn(expression.source, FirErrors.OPT_IN_WITHOUT_ARGUMENTS, context)
} else {
val annotationClasses = expression.findArgumentByName(USE_EXPERIMENTAL_ANNOTATION_CLASS)
val annotationClasses = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)
for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) {
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
}
@@ -73,7 +73,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
return
}
}
val classSymbol = expression.findArgumentByName(USE_EXPERIMENTAL_ANNOTATION_CLASS)?.extractClassFromArgument() ?: return
val classSymbol = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)?.extractClassFromArgument() ?: return
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
}
}
@@ -97,7 +97,7 @@ object FirOptInUsageBaseChecker {
)
if (fromSupertype) {
if (annotationType.lookupTag.classId == OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) {
val annotationClass = annotation.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS) ?: continue
val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
result.addIfNotNull(
annotationClass.extractClassFromArgument()?.loadExperimentalityForMarkerAnnotation()?.copy(fromSupertype = true)
)
@@ -320,7 +320,7 @@ object FirOptInUsageBaseChecker {
if (coneType?.lookupTag?.classId != OptInNames.OPT_IN_CLASS_ID) {
continue
}
val annotationClasses = annotation.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS) ?: continue
val annotationClasses = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
if (annotationClasses.extractClassesFromArgument().any { it.classId == annotationClassId }) {
return true
}
@@ -334,7 +334,7 @@ object FirOptInUsageBaseChecker {
if (coneType?.lookupTag?.classId != OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) {
continue
}
val annotationClass = annotation.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS) ?: continue
val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
if (annotationClass.extractClassFromArgument()?.classId == annotationClassId) {
return true
}
@@ -42,23 +42,23 @@ class OptInMarkerDeclarationAnnotationChecker(private val module: ModuleDescript
for (entry in entries) {
val annotation = trace.bindingContext.get(BindingContext.ANNOTATION, entry) ?: continue
when (annotation.fqName) {
in OptInNames.OPT_IN_FQ_NAMES -> {
OptInNames.OPT_IN_FQ_NAME -> {
val annotationClasses =
annotation.allValueArguments[OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS]
annotation.allValueArguments[OptInNames.OPT_IN_ANNOTATION_CLASS]
.safeAs<ArrayValue>()?.value.orEmpty()
checkOptInUsage(annotationClasses, trace, entry)
}
OptInNames.SUBCLASS_OPT_IN_REQUIRED_FQ_NAME -> {
val annotationClass =
annotation.allValueArguments[OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS]
annotation.allValueArguments[OptInNames.OPT_IN_ANNOTATION_CLASS]
checkSubclassOptInUsage(annotated, listOfNotNull(annotationClass), trace, entry)
}
in OptInNames.REQUIRES_OPT_IN_FQ_NAMES -> {
OptInNames.REQUIRES_OPT_IN_FQ_NAME -> {
hasOptIn = true
}
}
val annotationClass = annotation.annotationClass ?: continue
if (annotationClass.annotations.any { it.fqName in OptInNames.REQUIRES_OPT_IN_FQ_NAMES }) {
if (annotationClass.annotations.any { it.fqName == OptInNames.REQUIRES_OPT_IN_FQ_NAME }) {
val applicableTargets = AnnotationChecker.applicableTargetSet(annotationClass)
val possibleTargets = applicableTargets.intersect(actualTargets)
val annotationUseSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()
@@ -36,14 +36,10 @@ import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.checkers.OptInNames.OLD_EXPERIMENTAL_FQ_NAME
import org.jetbrains.kotlin.resolve.checkers.OptInNames.OLD_USE_EXPERIMENTAL_FQ_NAME
import org.jetbrains.kotlin.resolve.checkers.OptInNames.OPT_IN_FQ_NAME
import org.jetbrains.kotlin.resolve.checkers.OptInNames.OPT_IN_FQ_NAMES
import org.jetbrains.kotlin.resolve.checkers.OptInNames.REQUIRES_OPT_IN_FQ_NAME
import org.jetbrains.kotlin.resolve.checkers.OptInNames.REQUIRES_OPT_IN_FQ_NAMES
import org.jetbrains.kotlin.resolve.checkers.OptInNames.SUBCLASS_OPT_IN_REQUIRED_FQ_NAME
import org.jetbrains.kotlin.resolve.checkers.OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS
import org.jetbrains.kotlin.resolve.checkers.OptInNames.OPT_IN_ANNOTATION_CLASS
import org.jetbrains.kotlin.resolve.checkers.OptInNames.WAS_EXPERIMENTAL_FQ_NAME
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
@@ -286,9 +282,7 @@ class OptInUsageChecker(project: Project) : CallChecker {
subclassesOnly: Boolean = false
): OptInDescription? {
val optInAnnotationDescriptor =
annotations.findAnnotation(REQUIRES_OPT_IN_FQ_NAME)
?: annotations.findAnnotation(OLD_EXPERIMENTAL_FQ_NAME)
?: return null
annotations.findAnnotation(REQUIRES_OPT_IN_FQ_NAME) ?: return null
val arguments = optInAnnotationDescriptor.allValueArguments
val severity = when ((arguments[LEVEL] as? EnumValue)?.enumEntryName) {
@@ -308,7 +302,7 @@ class OptInUsageChecker(project: Project) : CallChecker {
private fun AnnotationDescriptor.loadSubclassOptInRequired(context: BindingContext?): OptInDescription? {
if (this.fqName != SUBCLASS_OPT_IN_REQUIRED_FQ_NAME) return null
val markerClass = allValueArguments[USE_EXPERIMENTAL_ANNOTATION_CLASS]
val markerClass = allValueArguments[OPT_IN_ANNOTATION_CLASS]
if (markerClass !is KClassValue) return null
val value = markerClass.value
if (value !is KClassValue.Value.NormalClass) return null
@@ -320,7 +314,7 @@ class OptInUsageChecker(project: Project) : CallChecker {
isOptInAllowed(annotationFqName, context.languageVersionSettings, context.trace.bindingContext, subclassesOnly)
/**
* Checks whether there's an element lexically above in the tree, annotated with `@UseExperimental(X::class)`, or a declaration
* Checks whether there's an element lexically above in the tree, annotated with `@OptIn(X::class)`, or a declaration
* annotated with `@X` where [annotationFqName] is the FQ name of X
*/
fun PsiElement.isOptInAllowed(
@@ -354,8 +348,8 @@ class OptInUsageChecker(project: Project) : CallChecker {
private fun PsiElement.isElementAnnotatedWithOptIn(annotationFqName: FqName, bindingContext: BindingContext): Boolean {
return this is KtAnnotated && annotationEntries.any { entry ->
val descriptor = bindingContext.get(BindingContext.ANNOTATION, entry)
if (descriptor != null && descriptor.fqName in OPT_IN_FQ_NAMES) {
val annotationClasses = descriptor.allValueArguments[USE_EXPERIMENTAL_ANNOTATION_CLASS]
if (descriptor != null && descriptor.fqName == OPT_IN_FQ_NAME) {
val annotationClasses = descriptor.allValueArguments[OPT_IN_ANNOTATION_CLASS]
annotationClasses is ArrayValue && annotationClasses.value.any { annotationClass ->
annotationClass is KClassValue && annotationClass.value.let { value ->
value is KClassValue.Value.NormalClass &&
@@ -373,7 +367,7 @@ class OptInUsageChecker(project: Project) : CallChecker {
return this is KtAnnotated && annotationEntries.any { entry ->
val descriptor = bindingContext.get(BindingContext.ANNOTATION, entry)
if (descriptor != null && descriptor.fqName == SUBCLASS_OPT_IN_REQUIRED_FQ_NAME) {
val annotationClass = descriptor.allValueArguments[USE_EXPERIMENTAL_ANNOTATION_CLASS]
val annotationClass = descriptor.allValueArguments[OPT_IN_ANNOTATION_CLASS]
annotationClass is KClassValue && annotationClass.value.let { value ->
value is KClassValue.Value.NormalClass &&
value.classId.asSingleFqName() == annotationFqName && value.arrayDimensions == 0
@@ -429,7 +423,7 @@ class OptInUsageChecker(project: Project) : CallChecker {
}
languageVersionSettings.getFlag(AnalysisFlags.optIn).forEach { fqName ->
if (fqName != REQUIRES_OPT_IN_FQ_NAME.asString() && fqName != OLD_EXPERIMENTAL_FQ_NAME.asString()) {
if (fqName != REQUIRES_OPT_IN_FQ_NAME.asString()) {
checkAnnotation(fqName)
}
}
@@ -441,12 +435,10 @@ class OptInUsageChecker(project: Project) : CallChecker {
override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) {
val name = targetDescriptor.name
if (name == OLD_EXPERIMENTAL_FQ_NAME.shortName() || name == REQUIRES_OPT_IN_FQ_NAME.shortName() ||
name == OLD_USE_EXPERIMENTAL_FQ_NAME.shortName() || name == OPT_IN_FQ_NAME.shortName()
) {
if (name == REQUIRES_OPT_IN_FQ_NAME.shortName() || name == OPT_IN_FQ_NAME.shortName()) {
val fqName = targetDescriptor.fqNameSafe
if (fqName in REQUIRES_OPT_IN_FQ_NAMES || fqName in OPT_IN_FQ_NAMES) {
checkUsageOfKotlinExperimentalOrOptIn(element, context)
if (fqName == REQUIRES_OPT_IN_FQ_NAME || fqName == OPT_IN_FQ_NAME) {
checkUsageOfKotlinRequiresOptInOrOptIn(element, context)
return
}
}
@@ -481,11 +473,10 @@ class OptInUsageChecker(project: Project) : CallChecker {
}
}
private fun checkUsageOfKotlinExperimentalOrOptIn(element: PsiElement, context: CheckerContext) {
private fun checkUsageOfKotlinRequiresOptInOrOptIn(element: PsiElement, context: CheckerContext) {
val optInFqNames = context.languageVersionSettings.getFlag(AnalysisFlags.optIn)
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.OptInRelease) &&
REQUIRES_OPT_IN_FQ_NAME.asString() !in optInFqNames &&
OLD_EXPERIMENTAL_FQ_NAME.asString() !in optInFqNames
REQUIRES_OPT_IN_FQ_NAME.asString() !in optInFqNames
) {
context.trace.report(Errors.OPT_IN_IS_NOT_ENABLED.on(element))
}
@@ -515,7 +506,7 @@ class OptInUsageChecker(project: Project) : CallChecker {
parent.parent.parent is KtValueArgumentList &&
parent.parent.parent.parent.let { entry ->
entry is KtAnnotationEntry && bindingContext.get(BindingContext.ANNOTATION, entry)?.let { annotation ->
annotation.fqName in OPT_IN_FQ_NAMES || annotation.fqName == WAS_EXPERIMENTAL_FQ_NAME ||
annotation.fqName == OPT_IN_FQ_NAME || annotation.fqName == WAS_EXPERIMENTAL_FQ_NAME ||
annotation.fqName == SUBCLASS_OPT_IN_REQUIRED_FQ_NAME
} == true
}
@@ -10,8 +10,6 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
object OptInNames {
val OLD_EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental")
val OLD_USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental")
val REQUIRES_OPT_IN_FQ_NAME = FqName("kotlin.RequiresOptIn")
val REQUIRES_OPT_IN_CLASS_ID = ClassId.topLevel(REQUIRES_OPT_IN_FQ_NAME)
val OPT_IN_FQ_NAME = FqName("kotlin.OptIn")
@@ -21,26 +19,9 @@ object OptInNames {
val WAS_EXPERIMENTAL_FQ_NAME = FqName("kotlin.WasExperimental")
val WAS_EXPERIMENTAL_CLASS_ID = ClassId.topLevel(WAS_EXPERIMENTAL_FQ_NAME)
val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
val OPT_IN_ANNOTATION_CLASS = Name.identifier("markerClass")
val WAS_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
val REQUIRES_OPT_IN_FQ_NAMES = setOf(OLD_EXPERIMENTAL_FQ_NAME, REQUIRES_OPT_IN_FQ_NAME)
val OPT_IN_FQ_NAMES = setOf(OLD_USE_EXPERIMENTAL_FQ_NAME, OPT_IN_FQ_NAME)
@Deprecated(
message = "EXPERIMENTAL_FQ_NAMES is deprecated, please use REQUIRES_OPT_IN_FQ_NAMES instead",
ReplaceWith("REQUIRES_OPT_IN_FQ_NAMES", imports = ["org.jetbrains.kotlin.resolve.checkers.OptInNames.REQUIRES_OPT_IN_FQ_NAMES"])
)
@Suppress("unused")
val EXPERIMENTAL_FQ_NAMES = REQUIRES_OPT_IN_FQ_NAMES
@Deprecated(
message = "USE_EXPERIMENTAL_FQ_NAMES is deprecated, please use OPT_IN_FQ_NAMES instead",
ReplaceWith("OPT_IN_FQ_NAMES", imports = ["org.jetbrains.kotlin.resolve.checkers.OptInNames.OPT_IN_FQ_NAMES"])
)
@Suppress("unused")
val USE_EXPERIMENTAL_FQ_NAMES = OPT_IN_FQ_NAMES
fun buildDefaultDiagnosticMessage(prefix: String, markerName: String): String {
return "$prefix with '@$markerName' or '@OptIn($markerName::class)'"
}