diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt index 8329a93d690..ab3c37a86f6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt @@ -52,18 +52,18 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD for (entry in entries) { val annotation = trace.bindingContext.get(BindingContext.ANNOTATION, entry) ?: continue when (annotation.fqName) { - in ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAMES -> { + in OptInNames.USE_EXPERIMENTAL_FQ_NAMES -> { val annotationClasses = - annotation.allValueArguments[ExperimentalUsageChecker.USE_EXPERIMENTAL_ANNOTATION_CLASS] + annotation.allValueArguments[OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS] .safeAs()?.value.orEmpty() checkUseExperimentalUsage(annotationClasses, trace, entry) } - in ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAMES -> { + in OptInNames.EXPERIMENTAL_FQ_NAMES -> { isAnnotatedWithExperimental = true } } 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() if (KotlinTarget.PROPERTY_GETTER in actualTargets || annotationUseSiteTarget == AnnotationUseSiteTarget.PROPERTY_GETTER diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt index 72f1a35feef..49fc6552964 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt @@ -34,6 +34,14 @@ 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.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.EnumValue import org.jetbrains.kotlin.resolve.constants.KClassValue @@ -55,14 +63,6 @@ import org.jetbrains.kotlin.utils.addIfNotNull class ExperimentalUsageChecker(project: Project) : CallChecker { 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 { fun report(trace: BindingTrace, element: PsiElement, fqName: FqName, message: String?) } @@ -113,18 +113,6 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { } 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 MESSAGE = Name.identifier("message") 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 // "-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. - val deprecationResolver = - DeprecationResolver(LockBasedStorageManager("ExperimentalUsageChecker"), languageVersionSettings, CoroutineCompatibilitySupport.ENABLED, DeprecationSettings.Default) + val deprecationResolver = DeprecationResolver( + LockBasedStorageManager("ExperimentalUsageChecker"), + languageVersionSettings, + CoroutineCompatibilitySupport.ENABLED, + DeprecationSettings.Default + ) // Returns true if fqName resolves to a valid opt-in requirement marker. fun checkAnnotation(fqName: String): Boolean { @@ -372,7 +364,8 @@ class ExperimentalUsageChecker(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()) { + name == OLD_USE_EXPERIMENTAL_FQ_NAME.shortName() || name == OPT_IN_FQ_NAME.shortName() + ) { val fqName = targetDescriptor.fqNameSafe if (fqName in EXPERIMENTAL_FQ_NAMES || fqName in USE_EXPERIMENTAL_FQ_NAMES) { checkUsageOfKotlinExperimentalOrUseExperimental(element, context) @@ -412,7 +405,8 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { private fun checkUsageOfKotlinExperimentalOrUseExperimental(element: PsiElement, context: CheckerContext) { val useExperimentalFqNames = context.languageVersionSettings.getFlag(AnalysisFlags.useExperimental) 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)) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt index a18d94e7c2f..7f1818b25ed 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor 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.KClassValue import org.jetbrains.kotlin.resolve.descriptorUtil.module @@ -88,8 +88,8 @@ private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): SinceKotlinValue? (this as? PropertyAccessorDescriptor)?.correspondingProperty?.consider() val typeAlias = this as? TypeAliasDescriptor - ?: (this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor - ?: (this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor + ?: (this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor + ?: (this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor typeAlias?.consider() @@ -103,9 +103,9 @@ private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): SinceKotlinValue? } private fun DeclarationDescriptor.loadWasExperimentalMarkerClasses(): List { - val wasExperimental = annotations.findAnnotation(ExperimentalUsageChecker.WAS_EXPERIMENTAL_FQ_NAME) + val wasExperimental = annotations.findAnnotation(OptInNames.WAS_EXPERIMENTAL_FQ_NAME) 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) { return annotationClasses.value.mapNotNull { annotationClass -> (annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/Experimentality.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/Experimentality.kt new file mode 100644 index 00000000000..aa640f6b607 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/Experimentality.kt @@ -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 + } +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/OptInNames.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/OptInNames.kt new file mode 100644 index 00000000000..6ba4c707c02 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/OptInNames.kt @@ -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) +} \ No newline at end of file diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt index 1fa4f2a9bf9..4b7d2c5bd29 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt @@ -40,8 +40,8 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.withOptions import org.jetbrains.kotlin.renderer.DescriptorRendererModifier.* import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy 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.OptInNames import org.jetbrains.kotlin.resolve.checkers.explicitApiEnabled import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden import org.jetbrains.kotlin.util.findCallableMemberBySignature @@ -209,8 +209,8 @@ private val OVERRIDE_RENDERER = withOptions { renderUnabbreviatedType = false annotationFilter = { val annotations = it.type.constructor.declarationDescriptor?.annotations - annotations != null && (annotations.hasAnnotation(ExperimentalUsageChecker.REQUIRES_OPT_IN_FQ_NAME) || - annotations.hasAnnotation(ExperimentalUsageChecker.OLD_EXPERIMENTAL_FQ_NAME)) + annotations != null && (annotations.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) || + annotations.hasAnnotation(OptInNames.OLD_EXPERIMENTAL_FQ_NAME)) } presentableUnresolvedTypes = true informativeErrorType = false diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt index ec9954e2f93..dbdab8fc918 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate 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 object ExperimentalFixesFactory : KotlinIntentionActionsFactory() { @@ -103,8 +103,8 @@ object ExperimentalFixesFactory : KotlinIntentionActionsFactory() { } private val ModuleDescriptor.OPT_IN_FQ_NAME: FqName - get() = ExperimentalUsageChecker.OPT_IN_FQ_NAME.takeIf { fqNameIsExisting(it) } - ?: ExperimentalUsageChecker.OLD_USE_EXPERIMENTAL_FQ_NAME + get() = OptInNames.OPT_IN_FQ_NAME.takeIf { fqNameIsExisting(it) } + ?: OptInNames.OLD_USE_EXPERIMENTAL_FQ_NAME fun ModuleDescriptor.fqNameIsExisting(fqName: FqName): Boolean = resolveClassByFqName(fqName, NoLookupLocation.FROM_IDE) != null diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeModuleExperimentalFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeModuleExperimentalFix.kt index 251cf289656..f7566810bd1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeModuleExperimentalFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeModuleExperimentalFix.kt @@ -24,14 +24,14 @@ import org.jetbrains.kotlin.idea.roots.invalidateProjectRoots import org.jetbrains.kotlin.idea.util.projectStructure.module import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker +import org.jetbrains.kotlin.resolve.checkers.OptInNames class MakeModuleExperimentalFix( file: KtFile, private val module: Module, private val annotationFqName: FqName ) : KotlinQuickFixAction(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" else "use-experimental" @@ -64,7 +64,7 @@ class MakeModuleExperimentalFix( val facet = KotlinFacet.get(module) ?: return true val facetSettings = facet.configuration.settings 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 } else { compilerSettings.additionalArgumentsAsList.none { @@ -80,9 +80,9 @@ class MakeModuleExperimentalFix( return MakeModuleExperimentalFix( containingKtFile, module, - ExperimentalUsageChecker.REQUIRES_OPT_IN_FQ_NAME.takeIf { + OptInNames.REQUIRES_OPT_IN_FQ_NAME.takeIf { module.toDescriptor()?.fqNameIsExisting(it) == true - } ?: ExperimentalUsageChecker.OLD_EXPERIMENTAL_FQ_NAME + } ?: OptInNames.OLD_EXPERIMENTAL_FQ_NAME ) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt index d320d75af0a..9f66373bffb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/ExpectActualUtils.kt @@ -44,7 +44,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier 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.multiplatform.OptionalAnnotationUtil import org.jetbrains.kotlin.resolve.source.KotlinSourceElement @@ -262,8 +262,8 @@ private fun KtPsiFactory.repairSuperTypeList( private val forbiddenAnnotationFqNames = setOf( OptionalAnnotationUtil.OPTIONAL_EXPECTATION_FQ_NAME, FqName("kotlin.ExperimentalMultiplatform"), - ExperimentalUsageChecker.OPT_IN_FQ_NAME, - ExperimentalUsageChecker.OLD_USE_EXPERIMENTAL_FQ_NAME + OptInNames.OPT_IN_FQ_NAME, + OptInNames.OLD_USE_EXPERIMENTAL_FQ_NAME ) internal fun generateCallable(