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 {
|
fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings: LanguageVersionSettings): SinceKotlinAccessibility {
|
||||||
val version =
|
val value =
|
||||||
if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this)
|
if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this)
|
||||||
else getOwnSinceKotlinVersion()
|
else getOwnSinceKotlinVersion()
|
||||||
|
val version = value?.apiVersion
|
||||||
|
|
||||||
// Allow access in the following cases:
|
// Allow access in the following cases:
|
||||||
// 1) There's no @SinceKotlin annotation for this descriptor
|
// 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
|
// 3) The value as a version is not greater than our API version
|
||||||
if (version == null || version <= languageVersionSettings.apiVersion) return SinceKotlinAccessibility.Accessible
|
if (version == null || version <= languageVersionSettings.apiVersion) return SinceKotlinAccessibility.Accessible
|
||||||
|
|
||||||
val wasExperimentalFqNames = with(ExperimentalUsageChecker) { loadWasExperimentalMarkerFqNames() }
|
val wasExperimentalFqNames = value.wasExperimentalMarkerFqNames
|
||||||
if (wasExperimentalFqNames.isNotEmpty()) {
|
if (wasExperimentalFqNames.isNotEmpty()) {
|
||||||
return SinceKotlinAccessibility.NotAccessibleButWasExperimental(version, wasExperimentalFqNames)
|
return SinceKotlinAccessibility.NotAccessibleButWasExperimental(version, wasExperimentalFqNames)
|
||||||
}
|
}
|
||||||
@@ -45,34 +46,59 @@ fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSe
|
|||||||
return SinceKotlinAccessibility.NotAccessible(version)
|
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],
|
* @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.
|
* or the minimal value of the version from all declarations annotated with [SinceKotlin] otherwise.
|
||||||
*/
|
*/
|
||||||
private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): ApiVersion? {
|
private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): SinceKotlinValue? {
|
||||||
return DescriptorUtils.getAllOverriddenDeclarations(descriptor).map { it.getOwnSinceKotlinVersion() ?: return null }.min()
|
// 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
|
// TODO: use-site targeted annotations
|
||||||
fun DeclarationDescriptor.loadAnnotationValue(): ApiVersion? =
|
fun DeclarationDescriptor.consider() {
|
||||||
(annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)?.allValueArguments?.values?.singleOrNull()?.value as? String)
|
val apiVersion = (annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)?.allValueArguments?.values?.singleOrNull()?.value as? String)
|
||||||
?.let(ApiVersion.Companion::parse)
|
?.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()
|
this.consider()
|
||||||
val ctorClass = (this as? ConstructorDescriptor)?.containingDeclaration?.loadAnnotationValue()
|
|
||||||
val property = (this as? PropertyAccessorDescriptor)?.correspondingProperty?.loadAnnotationValue()
|
|
||||||
|
|
||||||
val typeAliasDescriptor = (this as? TypeAliasDescriptor) ?: (this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor
|
(this as? ConstructorDescriptor)?.containingDeclaration?.consider()
|
||||||
?: (this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor
|
(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.
|
// 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()
|
(this as? TypeAliasConstructorDescriptor)?.underlyingConstructorDescriptor?.consider()
|
||||||
val underlyingObject = (this as? FakeCallableDescriptorForTypeAliasObject)?.getReferencedObject()?.loadAnnotationValue()
|
(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")
|
@SinceKotlin("1.0")
|
||||||
class Baz @SinceKotlin("1.1") constructor()
|
class Baz @SinceKotlin("1.1") constructor()
|
||||||
|
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
class Quux @SinceKotlin("1.0") constructor()
|
||||||
|
|
||||||
fun t1(): <!API_NOT_AVAILABLE!>Foo<!> = <!UNRESOLVED_REFERENCE!>Foo<!>()
|
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 t3(): Bar? = <!UNRESOLVED_REFERENCE!>Bar<!>()
|
||||||
|
|
||||||
fun t4(): Baz = <!UNRESOLVED_REFERENCE!>Baz<!>()
|
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 t2(): Foo
|
||||||
public fun t3(): Bar?
|
public fun t3(): Bar?
|
||||||
public fun t4(): Baz
|
public fun t4(): Baz
|
||||||
|
public fun t5(): Quux
|
||||||
|
|
||||||
public final class Bar {
|
public final class Bar {
|
||||||
@kotlin.SinceKotlin(version = "1.1") public constructor 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 hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
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
|
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") @kotlin.WasExperimental(markerClass = {Marker::class}) public fun newFunExperimentalInThePast(): kotlin.Unit
|
||||||
@kotlin.SinceKotlin(version = "1.3") public fun newPublishedFun(): kotlin.Unit
|
@kotlin.SinceKotlin(version = "1.3") public fun newPublishedFun(): kotlin.Unit
|
||||||
public fun use1(): 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 hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
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