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 73d03f49eb7..320d7c416f6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt @@ -56,7 +56,8 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { } override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { - val experimentalities = resolvedCall.resultingDescriptor.loadExperimentalities(moduleAnnotationsResolver) + val experimentalities = + resolvedCall.resultingDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings) reportNotAcceptedExperimentalities(experimentalities, reportOn, context) } @@ -89,7 +90,8 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { } private fun DeclarationDescriptor.loadExperimentalities( - moduleAnnotationsResolver: ModuleAnnotationsResolver + moduleAnnotationsResolver: ModuleAnnotationsResolver, + languageVersionSettings: LanguageVersionSettings ): Set { val result = SmartSet.create() @@ -97,9 +99,16 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation()) } + if (annotations.any { it.fqName == WAS_EXPERIMENTAL_FQ_NAME }) { + val accessibility = checkSinceKotlinVersionAccessibility(languageVersionSettings) + if (accessibility is SinceKotlinAccessibility.NotAccessibleButWasExperimental) { + result.addAll(accessibility.markerClasses.map { it.loadExperimentalityForMarkerAnnotation() }) + } + } + val container = containingDeclaration if (container is ClassDescriptor && this !is ConstructorDescriptor) { - result.addAll(container.loadExperimentalities(moduleAnnotationsResolver)) + result.addAll(container.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings)) } for (moduleAnnotationClassId in moduleAnnotationsResolver.getAnnotationsOnContainingModule(this)) { @@ -169,13 +178,13 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { } } - internal fun Annotated.loadWasExperimentalMarkerFqNames(): List { + internal fun Annotated.loadWasExperimentalMarkerClasses(): List { val wasExperimental = annotations.findAnnotation(WAS_EXPERIMENTAL_FQ_NAME) if (wasExperimental != null) { val annotationClasses = wasExperimental.allValueArguments[WAS_EXPERIMENTAL_ANNOTATION_CLASS] if (annotationClasses is ArrayValue) { return annotationClasses.value.mapNotNull { annotationClass -> - (annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor?.fqNameSafe + (annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor as? ClassDescriptor } } } @@ -243,7 +252,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { } } - val experimentalities = targetDescriptor.loadExperimentalities(moduleAnnotationsResolver) + val experimentalities = targetDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings) reportNotAcceptedExperimentalities(experimentalities, element, context) val targetClass = when (targetDescriptor) { @@ -311,7 +320,8 @@ class ExperimentalUsageChecker(project: Project) : CallChecker { if (descriptor !is CallableMemberDescriptor) return val experimentalOverridden = descriptor.overriddenDescriptors.flatMap { member -> - member.loadExperimentalities(moduleAnnotationsResolver).map { experimentality -> experimentality to member } + member.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings) + .map { experimentality -> experimentality to member } }.toMap() for ((experimentality, member) in experimentalOverridden) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt index ab5c91ae716..e79af8b1aa0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt @@ -227,8 +227,8 @@ class DeprecationResolver( if (sinceKotlinAccessibility is SinceKotlinAccessibility.NotAccessibleButWasExperimental) { if (call != null && bindingContext != null) { return with(ExperimentalUsageChecker) { - sinceKotlinAccessibility.annotationFqNames.any { fqName -> - !call.callElement.isExperimentalityAccepted(fqName, languageVersionSettings, bindingContext) + sinceKotlinAccessibility.markerClasses.any { classDescriptor -> + !call.callElement.isExperimentalityAccepted(classDescriptor.fqNameSafe, languageVersionSettings, bindingContext) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt index 6dfc6075f72..10077bb2787 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker @@ -18,7 +17,7 @@ sealed class SinceKotlinAccessibility { data class NotAccessibleButWasExperimental( val version: ApiVersion, - val annotationFqNames: List + val markerClasses: List ) : SinceKotlinAccessibility() data class NotAccessible( @@ -38,7 +37,7 @@ fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSe // 3) The value as a version is not greater than our API version if (version == null || version <= languageVersionSettings.apiVersion) return SinceKotlinAccessibility.Accessible - val wasExperimentalFqNames = value.wasExperimentalMarkerFqNames + val wasExperimentalFqNames = value.wasExperimentalMarkerClasses if (wasExperimentalFqNames.isNotEmpty()) { return SinceKotlinAccessibility.NotAccessibleButWasExperimental(version, wasExperimentalFqNames) } @@ -48,7 +47,7 @@ fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSe private data class SinceKotlinValue( val apiVersion: ApiVersion, - val wasExperimentalMarkerFqNames: List + val wasExperimentalMarkerClasses: List ) /** @@ -56,7 +55,7 @@ private data class SinceKotlinValue( * or the minimal value of the version from all declarations annotated with [SinceKotlin] otherwise. */ private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): SinceKotlinValue? { - // TODO: combine wasExperimentalMarkerFqNames in case of several members with the same minimal API version + // TODO: combine wasExperimentalMarkerClasses in case of several members with the same minimal API version return DescriptorUtils.getAllOverriddenDeclarations(descriptor).map { it.getOwnSinceKotlinVersion() ?: return null } .minBy { it.apiVersion } } @@ -73,11 +72,11 @@ private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): SinceKotlinValue? val apiVersion = (annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)?.allValueArguments?.values?.singleOrNull()?.value as? String) ?.let(ApiVersion.Companion::parse) if (apiVersion != null) { - // TODO: combine wasExperimentalMarkerFqNames in case of several associated declarations with the same maximal API version + // TODO: combine wasExperimentalMarkerClasses in case of several associated declarations with the same maximal API version if (result == null || apiVersion > result!!.apiVersion) { result = SinceKotlinValue( apiVersion, - with(ExperimentalUsageChecker) { loadWasExperimentalMarkerFqNames() } + with(ExperimentalUsageChecker) { loadWasExperimentalMarkerClasses() } ) } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.kt index b50c25ef9ad..ff0991c2663 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.kt @@ -1,6 +1,6 @@ // !API_VERSION: 1.2 // !USE_EXPERIMENTAL: kotlin.Experimental -// !DIAGNOSTICS: -INVISIBLE_MEMBER -INVISIBLE_REFERENCE -NEWER_VERSION_IN_SINCE_KOTLIN +// !DIAGNOSTICS: -INVISIBLE_MEMBER -INVISIBLE_REFERENCE -NEWER_VERSION_IN_SINCE_KOTLIN -UNUSED_PARAMETER @SinceKotlin("1.3") fun newPublishedFun() {} @@ -21,7 +21,15 @@ val newValExperimentalInThePast = "" @WasExperimental(Marker::class) class NewClassExperimentalInThePast -fun use1() { +@SinceKotlin("1.3") +@WasExperimental(Marker::class) +typealias TypeAliasToNewClass = NewClassExperimentalInThePast + + +fun use1( + c1: NewClassExperimentalInThePast, + t1: TypeAliasToNewClass +) { newPublishedFun() newFunExperimentalInThePast() newValExperimentalInThePast @@ -29,7 +37,10 @@ fun use1() { } @UseExperimental(Marker::class) -fun use2() { +fun use2( + c2: NewClassExperimentalInThePast, + t2: TypeAliasToNewClass +) { newPublishedFun() newFunExperimentalInThePast() newValExperimentalInThePast @@ -37,7 +48,10 @@ fun use2() { } @Marker -fun use3() { +fun use3( + c3: NewClassExperimentalInThePast, + t3: TypeAliasToNewClass +) { newPublishedFun() newFunExperimentalInThePast() newValExperimentalInThePast diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.txt index 57823a54dff..32385bba6b2 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.txt @@ -3,9 +3,9 @@ package @kotlin.SinceKotlin(version = "1.3") @kotlin.WasExperimental(markerClass = {Marker::class}) public val newValExperimentalInThePast: kotlin.String = "" @kotlin.SinceKotlin(version = "1.3") @kotlin.WasExperimental(markerClass = {Marker::class}) public fun newFunExperimentalInThePast(): kotlin.Unit @kotlin.SinceKotlin(version = "1.3") public fun newPublishedFun(): kotlin.Unit -public fun use1(): kotlin.Unit -@kotlin.UseExperimental(markerClass = {Marker::class}) public fun use2(): kotlin.Unit -@Marker public fun use3(): kotlin.Unit +public fun use1(/*0*/ c1: NewClassExperimentalInThePast, /*1*/ t1: TypeAliasToNewClass /* = NewClassExperimentalInThePast */): kotlin.Unit +@kotlin.UseExperimental(markerClass = {Marker::class}) public fun use2(/*0*/ c2: NewClassExperimentalInThePast, /*1*/ t2: TypeAliasToNewClass /* = NewClassExperimentalInThePast */): kotlin.Unit +@Marker public fun use3(/*0*/ c3: NewClassExperimentalInThePast, /*1*/ t3: TypeAliasToNewClass /* = NewClassExperimentalInThePast */): kotlin.Unit @kotlin.Experimental public final annotation class Marker : kotlin.Annotation { public constructor Marker() @@ -20,3 +20,4 @@ public fun use1(): kotlin.Unit public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } +@kotlin.SinceKotlin(version = "1.3") @kotlin.WasExperimental(markerClass = {Marker::class}) public typealias TypeAliasToNewClass = NewClassExperimentalInThePast diff --git a/libraries/stdlib/src/kotlin/annotations/Experimental.kt b/libraries/stdlib/src/kotlin/annotations/Experimental.kt index c59339066f3..772101f281a 100644 --- a/libraries/stdlib/src/kotlin/annotations/Experimental.kt +++ b/libraries/stdlib/src/kotlin/annotations/Experimental.kt @@ -52,7 +52,7 @@ annotation class UseExperimental( ) -@Target(CLASS, PROPERTY, CONSTRUCTOR, FUNCTION) +@Target(CLASS, PROPERTY, CONSTRUCTOR, FUNCTION, TYPEALIAS) @Retention(BINARY) internal annotation class WasExperimental( vararg val markerClass: KClass