Extract common parts from ExperimentalUsageChecker

This commit is contained in:
Mikhail Glukhikh
2021-05-25 18:24:21 +03:00
parent f0453d2f5c
commit 0972020324
9 changed files with 80 additions and 47 deletions
@@ -52,18 +52,18 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
for (entry in entries) { for (entry in entries) {
val annotation = trace.bindingContext.get(BindingContext.ANNOTATION, entry) ?: continue val annotation = trace.bindingContext.get(BindingContext.ANNOTATION, entry) ?: continue
when (annotation.fqName) { when (annotation.fqName) {
in ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAMES -> { in OptInNames.USE_EXPERIMENTAL_FQ_NAMES -> {
val annotationClasses = val annotationClasses =
annotation.allValueArguments[ExperimentalUsageChecker.USE_EXPERIMENTAL_ANNOTATION_CLASS] annotation.allValueArguments[OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS]
.safeAs<ArrayValue>()?.value.orEmpty() .safeAs<ArrayValue>()?.value.orEmpty()
checkUseExperimentalUsage(annotationClasses, trace, entry) checkUseExperimentalUsage(annotationClasses, trace, entry)
} }
in ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAMES -> { in OptInNames.EXPERIMENTAL_FQ_NAMES -> {
isAnnotatedWithExperimental = true isAnnotatedWithExperimental = true
} }
} }
val annotationClass = annotation.annotationClass ?: continue val annotationClass = annotation.annotationClass ?: continue
if (annotationClass.annotations.any { it.fqName in ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAMES }) { if (annotationClass.annotations.any { it.fqName in OptInNames.EXPERIMENTAL_FQ_NAMES }) {
val annotationUseSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget() val annotationUseSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()
if (KotlinTarget.PROPERTY_GETTER in actualTargets || if (KotlinTarget.PROPERTY_GETTER in actualTargets ||
annotationUseSiteTarget == AnnotationUseSiteTarget.PROPERTY_GETTER annotationUseSiteTarget == AnnotationUseSiteTarget.PROPERTY_GETTER
@@ -34,6 +34,14 @@ import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.checkers.OptInNames.EXPERIMENTAL_FQ_NAMES
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.REQUIRES_OPT_IN_FQ_NAME
import org.jetbrains.kotlin.resolve.checkers.OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS
import org.jetbrains.kotlin.resolve.checkers.OptInNames.USE_EXPERIMENTAL_FQ_NAMES
import org.jetbrains.kotlin.resolve.checkers.OptInNames.WAS_EXPERIMENTAL_FQ_NAME
import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.constants.KClassValue
@@ -55,14 +63,6 @@ import org.jetbrains.kotlin.utils.addIfNotNull
class ExperimentalUsageChecker(project: Project) : CallChecker { class ExperimentalUsageChecker(project: Project) : CallChecker {
private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project) private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project)
data class Experimentality(val annotationFqName: FqName, val severity: Severity, val message: String?) {
enum class Severity { WARNING, ERROR }
companion object {
val DEFAULT_SEVERITY = Severity.ERROR
}
}
interface ExperimentalityDiagnostic { interface ExperimentalityDiagnostic {
fun report(trace: BindingTrace, element: PsiElement, fqName: FqName, message: String?) fun report(trace: BindingTrace, element: PsiElement, fqName: FqName, message: String?)
} }
@@ -113,18 +113,6 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
} }
companion object { companion object {
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 OPT_IN_FQ_NAME = FqName("kotlin.OptIn")
val EXPERIMENTAL_FQ_NAMES = setOf(OLD_EXPERIMENTAL_FQ_NAME, REQUIRES_OPT_IN_FQ_NAME)
val USE_EXPERIMENTAL_FQ_NAMES = setOf(OLD_USE_EXPERIMENTAL_FQ_NAME, OPT_IN_FQ_NAME)
internal val WAS_EXPERIMENTAL_FQ_NAME = FqName("kotlin.WasExperimental")
internal val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
internal val WAS_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
private val LEVEL = Name.identifier("level") private val LEVEL = Name.identifier("level")
private val MESSAGE = Name.identifier("message") private val MESSAGE = Name.identifier("message")
private val WARNING_LEVEL = Name.identifier("WARNING") private val WARNING_LEVEL = Name.identifier("WARNING")
@@ -329,8 +317,12 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
// Ideally, we should run full resolution (with all classifier usage checkers) on classifiers used in "-Xexperimental" and // Ideally, we should run full resolution (with all classifier usage checkers) on classifiers used in "-Xexperimental" and
// "-Xuse-experimental" arguments. However, it's not easy to do this. This should be solved in the future with the support of // "-Xuse-experimental" arguments. However, it's not easy to do this. This should be solved in the future with the support of
// module annotations. For now, we only check deprecations because this is needed to correctly retire unneeded compiler arguments. // module annotations. For now, we only check deprecations because this is needed to correctly retire unneeded compiler arguments.
val deprecationResolver = val deprecationResolver = DeprecationResolver(
DeprecationResolver(LockBasedStorageManager("ExperimentalUsageChecker"), languageVersionSettings, CoroutineCompatibilitySupport.ENABLED, DeprecationSettings.Default) LockBasedStorageManager("ExperimentalUsageChecker"),
languageVersionSettings,
CoroutineCompatibilitySupport.ENABLED,
DeprecationSettings.Default
)
// Returns true if fqName resolves to a valid opt-in requirement marker. // Returns true if fqName resolves to a valid opt-in requirement marker.
fun checkAnnotation(fqName: String): Boolean { fun checkAnnotation(fqName: String): Boolean {
@@ -372,7 +364,8 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) { override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) {
val name = targetDescriptor.name val name = targetDescriptor.name
if (name == OLD_EXPERIMENTAL_FQ_NAME.shortName() || name == REQUIRES_OPT_IN_FQ_NAME.shortName() || 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()) { name == OLD_USE_EXPERIMENTAL_FQ_NAME.shortName() || name == OPT_IN_FQ_NAME.shortName()
) {
val fqName = targetDescriptor.fqNameSafe val fqName = targetDescriptor.fqNameSafe
if (fqName in EXPERIMENTAL_FQ_NAMES || fqName in USE_EXPERIMENTAL_FQ_NAMES) { if (fqName in EXPERIMENTAL_FQ_NAMES || fqName in USE_EXPERIMENTAL_FQ_NAMES) {
checkUsageOfKotlinExperimentalOrUseExperimental(element, context) checkUsageOfKotlinExperimentalOrUseExperimental(element, context)
@@ -412,7 +405,8 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
private fun checkUsageOfKotlinExperimentalOrUseExperimental(element: PsiElement, context: CheckerContext) { private fun checkUsageOfKotlinExperimentalOrUseExperimental(element: PsiElement, context: CheckerContext) {
val useExperimentalFqNames = context.languageVersionSettings.getFlag(AnalysisFlags.useExperimental) val useExperimentalFqNames = context.languageVersionSettings.getFlag(AnalysisFlags.useExperimental)
if (REQUIRES_OPT_IN_FQ_NAME.asString() !in useExperimentalFqNames && if (REQUIRES_OPT_IN_FQ_NAME.asString() !in useExperimentalFqNames &&
OLD_EXPERIMENTAL_FQ_NAME.asString() !in useExperimentalFqNames) { OLD_EXPERIMENTAL_FQ_NAME.asString() !in useExperimentalFqNames
) {
context.trace.report(Errors.EXPERIMENTAL_IS_NOT_ENABLED.on(element)) context.trace.report(Errors.EXPERIMENTAL_IS_NOT_ENABLED.on(element))
} }
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
@@ -88,8 +88,8 @@ private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): SinceKotlinValue?
(this as? PropertyAccessorDescriptor)?.correspondingProperty?.consider() (this as? PropertyAccessorDescriptor)?.correspondingProperty?.consider()
val typeAlias = this as? TypeAliasDescriptor val typeAlias = this as? TypeAliasDescriptor
?: (this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor ?: (this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor
?: (this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor ?: (this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor
typeAlias?.consider() typeAlias?.consider()
@@ -103,9 +103,9 @@ private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): SinceKotlinValue?
} }
private fun DeclarationDescriptor.loadWasExperimentalMarkerClasses(): List<ClassDescriptor> { private fun DeclarationDescriptor.loadWasExperimentalMarkerClasses(): List<ClassDescriptor> {
val wasExperimental = annotations.findAnnotation(ExperimentalUsageChecker.WAS_EXPERIMENTAL_FQ_NAME) val wasExperimental = annotations.findAnnotation(OptInNames.WAS_EXPERIMENTAL_FQ_NAME)
if (wasExperimental != null) { if (wasExperimental != null) {
val annotationClasses = wasExperimental.allValueArguments[ExperimentalUsageChecker.WAS_EXPERIMENTAL_ANNOTATION_CLASS] val annotationClasses = wasExperimental.allValueArguments[OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS]
if (annotationClasses is ArrayValue) { if (annotationClasses is ArrayValue) {
return annotationClasses.value.mapNotNull { annotationClass -> return annotationClasses.value.mapNotNull { annotationClass ->
(annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor (annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2021 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.resolve.checkers
import org.jetbrains.kotlin.name.FqName
data class Experimentality(val annotationFqName: FqName, val severity: Severity, val message: String?) {
enum class Severity { WARNING, ERROR }
companion object {
val DEFAULT_SEVERITY = Severity.ERROR
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2021 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.resolve.checkers
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 OPT_IN_FQ_NAME = FqName("kotlin.OptIn")
val WAS_EXPERIMENTAL_FQ_NAME = FqName("kotlin.WasExperimental")
val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
val WAS_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
val EXPERIMENTAL_FQ_NAMES = setOf(OLD_EXPERIMENTAL_FQ_NAME, REQUIRES_OPT_IN_FQ_NAME)
val USE_EXPERIMENTAL_FQ_NAMES = setOf(OLD_USE_EXPERIMENTAL_FQ_NAME, OPT_IN_FQ_NAME)
}
@@ -40,8 +40,8 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.withOptions
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier.* import org.jetbrains.kotlin.renderer.DescriptorRendererModifier.*
import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
import org.jetbrains.kotlin.resolve.checkers.ExplicitApiDeclarationChecker import org.jetbrains.kotlin.resolve.checkers.ExplicitApiDeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.checkers.explicitApiEnabled import org.jetbrains.kotlin.resolve.checkers.explicitApiEnabled
import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden
import org.jetbrains.kotlin.util.findCallableMemberBySignature import org.jetbrains.kotlin.util.findCallableMemberBySignature
@@ -209,8 +209,8 @@ private val OVERRIDE_RENDERER = withOptions {
renderUnabbreviatedType = false renderUnabbreviatedType = false
annotationFilter = { annotationFilter = {
val annotations = it.type.constructor.declarationDescriptor?.annotations val annotations = it.type.constructor.declarationDescriptor?.annotations
annotations != null && (annotations.hasAnnotation(ExperimentalUsageChecker.REQUIRES_OPT_IN_FQ_NAME) || annotations != null && (annotations.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ||
annotations.hasAnnotation(ExperimentalUsageChecker.OLD_EXPERIMENTAL_FQ_NAME)) annotations.hasAnnotation(OptInNames.OLD_EXPERIMENTAL_FQ_NAME))
} }
presentableUnresolvedTypes = true presentableUnresolvedTypes = true
informativeErrorType = false informativeErrorType = false
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
import org.jetbrains.kotlin.resolve.AnnotationChecker import org.jetbrains.kotlin.resolve.AnnotationChecker
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
object ExperimentalFixesFactory : KotlinIntentionActionsFactory() { object ExperimentalFixesFactory : KotlinIntentionActionsFactory() {
@@ -103,8 +103,8 @@ object ExperimentalFixesFactory : KotlinIntentionActionsFactory() {
} }
private val ModuleDescriptor.OPT_IN_FQ_NAME: FqName private val ModuleDescriptor.OPT_IN_FQ_NAME: FqName
get() = ExperimentalUsageChecker.OPT_IN_FQ_NAME.takeIf { fqNameIsExisting(it) } get() = OptInNames.OPT_IN_FQ_NAME.takeIf { fqNameIsExisting(it) }
?: ExperimentalUsageChecker.OLD_USE_EXPERIMENTAL_FQ_NAME ?: OptInNames.OLD_USE_EXPERIMENTAL_FQ_NAME
fun ModuleDescriptor.fqNameIsExisting(fqName: FqName): Boolean = resolveClassByFqName(fqName, NoLookupLocation.FROM_IDE) != null fun ModuleDescriptor.fqNameIsExisting(fqName: FqName): Boolean = resolveClassByFqName(fqName, NoLookupLocation.FROM_IDE) != null
@@ -24,14 +24,14 @@ import org.jetbrains.kotlin.idea.roots.invalidateProjectRoots
import org.jetbrains.kotlin.idea.util.projectStructure.module import org.jetbrains.kotlin.idea.util.projectStructure.module
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker import org.jetbrains.kotlin.resolve.checkers.OptInNames
class MakeModuleExperimentalFix( class MakeModuleExperimentalFix(
file: KtFile, file: KtFile,
private val module: Module, private val module: Module,
private val annotationFqName: FqName private val annotationFqName: FqName
) : KotlinQuickFixAction<KtFile>(file) { ) : KotlinQuickFixAction<KtFile>(file) {
private val experimentalPrefix = if (module.toDescriptor()?.fqNameIsExisting(ExperimentalUsageChecker.REQUIRES_OPT_IN_FQ_NAME) == true) private val experimentalPrefix = if (module.toDescriptor()?.fqNameIsExisting(OptInNames.REQUIRES_OPT_IN_FQ_NAME) == true)
"opt-in" "opt-in"
else else
"use-experimental" "use-experimental"
@@ -64,7 +64,7 @@ class MakeModuleExperimentalFix(
val facet = KotlinFacet.get(module) ?: return true val facet = KotlinFacet.get(module) ?: return true
val facetSettings = facet.configuration.settings val facetSettings = facet.configuration.settings
val compilerSettings = facetSettings.compilerSettings ?: return true val compilerSettings = facetSettings.compilerSettings ?: return true
return if (annotationFqName != ExperimentalUsageChecker.REQUIRES_OPT_IN_FQ_NAME && annotationFqName != ExperimentalUsageChecker.OLD_EXPERIMENTAL_FQ_NAME) { return if (annotationFqName != OptInNames.REQUIRES_OPT_IN_FQ_NAME && annotationFqName != OptInNames.OLD_EXPERIMENTAL_FQ_NAME) {
compilerArgument !in compilerSettings.additionalArgumentsAsList compilerArgument !in compilerSettings.additionalArgumentsAsList
} else { } else {
compilerSettings.additionalArgumentsAsList.none { compilerSettings.additionalArgumentsAsList.none {
@@ -80,9 +80,9 @@ class MakeModuleExperimentalFix(
return MakeModuleExperimentalFix( return MakeModuleExperimentalFix(
containingKtFile, containingKtFile,
module, module,
ExperimentalUsageChecker.REQUIRES_OPT_IN_FQ_NAME.takeIf { OptInNames.REQUIRES_OPT_IN_FQ_NAME.takeIf {
module.toDescriptor()?.fqNameIsExisting(it) == true module.toDescriptor()?.fqNameIsExisting(it) == true
} ?: ExperimentalUsageChecker.OLD_EXPERIMENTAL_FQ_NAME } ?: OptInNames.OLD_EXPERIMENTAL_FQ_NAME
) )
} }
} }
@@ -44,7 +44,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
@@ -262,8 +262,8 @@ private fun KtPsiFactory.repairSuperTypeList(
private val forbiddenAnnotationFqNames = setOf( private val forbiddenAnnotationFqNames = setOf(
OptionalAnnotationUtil.OPTIONAL_EXPECTATION_FQ_NAME, OptionalAnnotationUtil.OPTIONAL_EXPECTATION_FQ_NAME,
FqName("kotlin.ExperimentalMultiplatform"), FqName("kotlin.ExperimentalMultiplatform"),
ExperimentalUsageChecker.OPT_IN_FQ_NAME, OptInNames.OPT_IN_FQ_NAME,
ExperimentalUsageChecker.OLD_USE_EXPERIMENTAL_FQ_NAME OptInNames.OLD_USE_EXPERIMENTAL_FQ_NAME
) )
internal fun generateCallable( internal fun generateCallable(