Load WasExperimental values correctly for associated declarations
For example, if a class is `@SinceKotlin("X")
@WasExperimental(M::class)`, then its constructor should also be
accessible if API < X, provided that the opt-in to M is given
This commit is contained in:
@@ -27,9 +27,10 @@ sealed class SinceKotlinAccessibility {
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings: LanguageVersionSettings): SinceKotlinAccessibility {
|
||||
val version =
|
||||
val value =
|
||||
if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this)
|
||||
else getOwnSinceKotlinVersion()
|
||||
val version = value?.apiVersion
|
||||
|
||||
// Allow access in the following cases:
|
||||
// 1) There's no @SinceKotlin annotation for this descriptor
|
||||
@@ -37,7 +38,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 = with(ExperimentalUsageChecker) { loadWasExperimentalMarkerFqNames() }
|
||||
val wasExperimentalFqNames = value.wasExperimentalMarkerFqNames
|
||||
if (wasExperimentalFqNames.isNotEmpty()) {
|
||||
return SinceKotlinAccessibility.NotAccessibleButWasExperimental(version, wasExperimentalFqNames)
|
||||
}
|
||||
@@ -45,34 +46,59 @@ fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSe
|
||||
return SinceKotlinAccessibility.NotAccessible(version)
|
||||
}
|
||||
|
||||
private data class SinceKotlinValue(
|
||||
val apiVersion: ApiVersion,
|
||||
val wasExperimentalMarkerFqNames: List<FqName>
|
||||
)
|
||||
|
||||
/**
|
||||
* @return null if there are no overridden members or if there's at least one declaration in the hierarchy not annotated with [SinceKotlin],
|
||||
* or the minimal value of the version from all declarations annotated with [SinceKotlin] otherwise.
|
||||
*/
|
||||
private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): ApiVersion? {
|
||||
return DescriptorUtils.getAllOverriddenDeclarations(descriptor).map { it.getOwnSinceKotlinVersion() ?: return null }.min()
|
||||
private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): SinceKotlinValue? {
|
||||
// TODO: combine wasExperimentalMarkerFqNames in case of several members with the same minimal API version
|
||||
return DescriptorUtils.getAllOverriddenDeclarations(descriptor).map { it.getOwnSinceKotlinVersion() ?: return null }
|
||||
.minBy { it.apiVersion }
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): ApiVersion? {
|
||||
/**
|
||||
* @return the maximal value of API version required by the declaration or any of its "associated" declarations (class for constructor,
|
||||
* property for accessor, underlying class for type alias) along with experimental marker FQ names mentioned in the @WasExperimental
|
||||
*/
|
||||
private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): SinceKotlinValue? {
|
||||
var result: SinceKotlinValue? = null
|
||||
|
||||
// TODO: use-site targeted annotations
|
||||
fun DeclarationDescriptor.loadAnnotationValue(): ApiVersion? =
|
||||
(annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)?.allValueArguments?.values?.singleOrNull()?.value as? String)
|
||||
fun DeclarationDescriptor.consider() {
|
||||
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
|
||||
if (result == null || apiVersion > result!!.apiVersion) {
|
||||
result = SinceKotlinValue(
|
||||
apiVersion,
|
||||
with(ExperimentalUsageChecker) { loadWasExperimentalMarkerFqNames() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ownVersion = loadAnnotationValue()
|
||||
val ctorClass = (this as? ConstructorDescriptor)?.containingDeclaration?.loadAnnotationValue()
|
||||
val property = (this as? PropertyAccessorDescriptor)?.correspondingProperty?.loadAnnotationValue()
|
||||
this.consider()
|
||||
|
||||
val typeAliasDescriptor = (this as? TypeAliasDescriptor) ?: (this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor
|
||||
?: (this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor
|
||||
(this as? ConstructorDescriptor)?.containingDeclaration?.consider()
|
||||
(this as? PropertyAccessorDescriptor)?.correspondingProperty?.consider()
|
||||
|
||||
val typeAlias = typeAliasDescriptor?.loadAnnotationValue()
|
||||
val typeAlias = this as? TypeAliasDescriptor
|
||||
?: (this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor
|
||||
?: (this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor
|
||||
|
||||
typeAlias?.consider()
|
||||
|
||||
// We should check only the upper-most classifier ('A' in 'A<B<C>>') to guarantee binary compatibility.
|
||||
val underlyingClass = typeAliasDescriptor?.classDescriptor?.loadAnnotationValue()
|
||||
typeAlias?.classDescriptor?.consider()
|
||||
|
||||
val underlyingConstructor = (this as? TypeAliasConstructorDescriptor)?.underlyingConstructorDescriptor?.loadAnnotationValue()
|
||||
val underlyingObject = (this as? FakeCallableDescriptorForTypeAliasObject)?.getReferencedObject()?.loadAnnotationValue()
|
||||
(this as? TypeAliasConstructorDescriptor)?.underlyingConstructorDescriptor?.consider()
|
||||
(this as? FakeCallableDescriptorForTypeAliasObject)?.getReferencedObject()?.consider()
|
||||
|
||||
return listOfNotNull(ownVersion, ctorClass, property, typeAlias, underlyingClass, underlyingConstructor, underlyingObject).max()
|
||||
return result
|
||||
}
|
||||
|
||||
Vendored
+4
@@ -8,6 +8,8 @@ class Bar @SinceKotlin("1.1") constructor()
|
||||
@SinceKotlin("1.0")
|
||||
class Baz @SinceKotlin("1.1") constructor()
|
||||
|
||||
@SinceKotlin("1.1")
|
||||
class Quux @SinceKotlin("1.0") constructor()
|
||||
|
||||
fun t1(): <!API_NOT_AVAILABLE!>Foo<!> = <!UNRESOLVED_REFERENCE!>Foo<!>()
|
||||
|
||||
@@ -17,3 +19,5 @@ fun t2() = object : <!UNRESOLVED_REFERENCE, API_NOT_AVAILABLE, API_NOT_AVAILABLE
|
||||
fun t3(): Bar? = <!UNRESOLVED_REFERENCE!>Bar<!>()
|
||||
|
||||
fun t4(): Baz = <!UNRESOLVED_REFERENCE!>Baz<!>()
|
||||
|
||||
fun t5(): <!API_NOT_AVAILABLE!>Quux<!> = <!UNRESOLVED_REFERENCE!>Quux<!>()
|
||||
|
||||
Vendored
+8
@@ -4,6 +4,7 @@ public fun t1(): Foo
|
||||
public fun t2(): Foo
|
||||
public fun t3(): Bar?
|
||||
public fun t4(): Baz
|
||||
public fun t5(): Quux
|
||||
|
||||
public final class Bar {
|
||||
@kotlin.SinceKotlin(version = "1.1") public constructor Bar()
|
||||
@@ -25,3 +26,10 @@ public final class Bar {
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.1") public final class Quux {
|
||||
@kotlin.SinceKotlin(version = "1.0") public constructor Quux()
|
||||
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
|
||||
}
|
||||
|
||||
+8
@@ -1,5 +1,6 @@
|
||||
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
|
||||
@@ -12,3 +13,10 @@ 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 final class NewClassExperimentalInThePast {
|
||||
public constructor NewClassExperimentalInThePast()
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user