FIR: handle 'SinceKotlin' as a special kind of deprecated

#KT-51850 Fixed
This commit is contained in:
Mikhail Glukhikh
2022-04-05 12:42:11 +03:00
committed by teamcity
parent 07b5bd72ae
commit 9bd6a9c069
19 changed files with 122 additions and 82 deletions
@@ -17255,6 +17255,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/fir/CustomThrowableMessage.kt");
}
@Test
@TestMetadata("differentSinceKotlin.kt")
public void testDifferentSinceKotlin() throws Exception {
runTest("compiler/testData/codegen/box/fir/differentSinceKotlin.kt");
}
@Test
@TestMetadata("ExtensionAlias.kt")
public void testExtensionAlias() throws Exception {
@@ -120,6 +120,18 @@ fun List<FirAnnotation>.getAnnotationsByClassId(classId: ClassId): List<FirAnnot
}
}
inline fun <T> List<FirAnnotation>.mapAnnotationsWithClassIdTo(
classId: ClassId,
destination: MutableCollection<T>,
func: (FirAnnotation) -> T
) {
for (annotation in this) {
if (annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId == classId) {
destination.add(func(annotation))
}
}
}
fun FirExpression.unwrapVarargValue(): List<FirExpression> {
return when (this) {
is FirVarargArgumentsExpression -> arguments
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames.dep
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
import org.jetbrains.kotlin.resolve.deprecation.SimpleDeprecationInfo
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
fun FirBasedSymbol<*>.getDeprecation(callSite: FirElement?): DeprecationInfo? {
return when (this) {
@@ -111,20 +111,38 @@ private fun List<FirAnnotation>.extractDeprecationInfoPerUseSite(
currentVersion: ApiVersion,
fromJava: Boolean
): List<Pair<AnnotationUseSiteTarget?, DeprecationInfo>> {
val annotations = getAnnotationsByClassId(StandardClassIds.Annotations.Deprecated).map { it to false } +
getAnnotationsByClassId(StandardClassIds.Annotations.Java.Deprecated).map { it to true }
@Suppress("RemoveExplicitTypeArguments")
val annotations = buildList<Pair<FirAnnotation, Boolean>> {
mapAnnotationsWithClassIdTo(StandardClassIds.Annotations.Deprecated, this) { it to false }
mapAnnotationsWithClassIdTo(StandardClassIds.Annotations.Java.Deprecated, this) { it to true }
mapAnnotationsWithClassIdTo(StandardClassIds.Annotations.SinceKotlin, this) { it to false }
}
return annotations.mapNotNull { (deprecated, fromJavaAnnotation) ->
if (deprecated.classId == StandardClassIds.Annotations.SinceKotlin) {
val sinceKotlinSingleArgument = deprecated.findArgumentByName(ParameterNames.sinceKotlinVersion)
val apiVersion = ((sinceKotlinSingleArgument as? FirConstExpression<*>)?.value as? String)
?.let(ApiVersion.Companion::parse) ?: return@mapNotNull null
if (apiVersion <= currentVersion) return@mapNotNull null
val wasExperimental = this.any { it.classId == StandardClassIds.Annotations.WasExperimental }
return@mapNotNull runUnless(wasExperimental) {
deprecated.useSiteTarget to SimpleDeprecationInfo(
deprecationLevel = DeprecationLevelValue.HIDDEN,
propagatesToOverrides = true,
message = null
)
}
}
val deprecationLevel = deprecated.getDeprecationLevel() ?: DeprecationLevelValue.WARNING
val deprecatedSinceKotlin = getAnnotationsByClassId(StandardClassIds.Annotations.DeprecatedSinceKotlin).firstOrNull()
fun levelApplied(name: Name, level: DeprecationLevelValue): DeprecationLevelValue? {
fun deprecatedLevelApplied(name: Name, level: DeprecationLevelValue): DeprecationLevelValue? {
deprecatedSinceKotlin?.getVersionFromArgument(name)?.takeIf { it <= currentVersion }?.let { return level }
return level.takeIf { deprecatedSinceKotlin == null && level == deprecationLevel }
}
val appliedLevel = (levelApplied(deprecatedSinceKotlinHiddenSince, DeprecationLevelValue.HIDDEN)
?: levelApplied(deprecatedSinceKotlinErrorSince, DeprecationLevelValue.ERROR)
?: levelApplied(deprecatedSinceKotlinWarningSince, DeprecationLevelValue.WARNING))
val appliedLevel = (deprecatedLevelApplied(deprecatedSinceKotlinHiddenSince, DeprecationLevelValue.HIDDEN)
?: deprecatedLevelApplied(deprecatedSinceKotlinErrorSince, DeprecationLevelValue.ERROR)
?: deprecatedLevelApplied(deprecatedSinceKotlinWarningSince, DeprecationLevelValue.WARNING))
appliedLevel?.let {
val inheritable = !fromJavaAnnotation && !fromJava
@@ -27,9 +27,12 @@ import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Deprecated
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.DeprecatedSinceKotlin
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.WasExperimental
class FirCompilerRequiredAnnotationsResolveProcessor(
session: FirSession,
@@ -120,6 +123,14 @@ private class FirAnnotationResolveTransformer(
) : FirAbstractAnnotationResolveTransformer<Multimap<AnnotationFqn, FirRegularClass>, PersistentList<FirDeclaration>>(
session, scopeSession
) {
companion object {
private val REQUIRED_ANNOTATIONS: Set<ClassId> = setOf(
Deprecated, DeprecatedSinceKotlin, WasExperimental
)
private val REQUIRED_ANNOTATION_NAMES: Set<Name> = REQUIRED_ANNOTATIONS.mapTo(mutableSetOf()) { it.shortClassName }
}
private val predicateBasedProvider = session.predicateBasedProvider
var acceptableFqNames: Set<AnnotationFqn> = emptySet()
@@ -156,9 +167,7 @@ private class FirAnnotationResolveTransformer(
val annotationTypeRef = annotation.annotationTypeRef
if (annotationTypeRef !is FirUserTypeRef) return annotation
val name = annotationTypeRef.qualifier.last().name
if (name != Deprecated.shortClassName && name != DeprecatedSinceKotlin.shortClassName &&
acceptableFqNames.none { it.shortName() == name }
) return annotation
if (name !in REQUIRED_ANNOTATION_NAMES && acceptableFqNames.none { it.shortName() == name }) return annotation
val transformedAnnotation = annotation.transformAnnotationTypeRef(
typeResolverTransformer,
@@ -0,0 +1,27 @@
// TARGET_BACKEND: JVM
// !API_VERSION: 1.4
// WITH_STDLIB
// MODULE: m1
// FILE: m1.kt
package kotlin
@SinceKotlin("1.7")
@kotlin.jvm.JvmName("bar")
@Suppress("CONFLICTING_OVERLOADS")
fun <T> List<T>.foo(): T = this[1]
// MODULE: m2
// FILE: m2.kt
package kotlin
@Deprecated("")
@DeprecatedSinceKotlin(warningSince = "1.4", errorSince = "1.5", hiddenSince = "1.6")
@Suppress("CONFLICTING_OVERLOADS")
fun <T> List<T>.foo(): T? = getOrNull(0)
// MODULE: m3(m1, m2)
// FILE: test.kt
fun box(): String = listOf("OK", "FAIL").foo()!!
@@ -1,34 +0,0 @@
// !API_VERSION: 1.0
// MODULE: m1
// FILE: a.kt
package p1
@SinceKotlin("1.1")
fun foo(s: Int): String = s.toString()
// MODULE: m2
// FILE: b.kt
package p2
fun foo(s: Int): Int = s
// MODULE: m3(m1, m2)
// FILE: severalStarImports.kt
import p1.*
import p2.*
fun test1(): Int {
val r = <!OVERLOAD_RESOLUTION_AMBIGUITY!>foo<!>(42)
return r
}
// FILE: explicitlyImportP1.kt
import p1.foo // TODO: consider reporting API_NOT_AVAILABLE here
import p2.*
fun test2(): Int {
val r = foo(42)
return <!RETURN_TYPE_MISMATCH!>r<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !API_VERSION: 1.0
// MODULE: m1
// FILE: a.kt
@@ -7,5 +7,5 @@ annotation class Anno2 @SinceKotlin("1.1") constructor()
@Anno1("")
@Anno2
@<!UNRESOLVED_REFERENCE!>Anno2<!>
fun t1() {}
@@ -16,8 +16,8 @@ fun t1(): Foo = Foo()
// TODO: do not report API_NOT_AVAILABLE twice
fun t2() = object : Foo() {}
fun t3(): Bar? = Bar()
fun t3(): Bar? = <!UNRESOLVED_REFERENCE!>Bar<!>()
fun t4(): Baz = Baz()
fun t4(): Baz = <!UNRESOLVED_REFERENCE!>Baz<!>()
fun t5(): Quux = Quux()
@@ -18,14 +18,14 @@ interface I11 {
}
fun f1(x: I10) = x.foo()
fun f2(x: I11) = x.foo()
fun f2(x: I11) = x.<!UNRESOLVED_REFERENCE!>foo<!>()
fun f3(x: J) = x.foo()
interface BothI1 : I10, I11
fun f4(x: BothI1) = x.foo()
interface BothI2 : I11, I10
fun f5(x: BothI2) = x.foo()
fun f5(x: BothI2) = x.<!UNRESOLVED_REFERENCE!>foo<!>()
interface JAndI10 : J, I10
fun f6(x: JAndI10) = x.foo()
@@ -34,15 +34,15 @@ val v7: String
get() = ""
fun test() {
v1
v2
v3
<!UNRESOLVED_REFERENCE!>v1<!>
<!UNRESOLVED_REFERENCE!>v2<!>
<!UNRESOLVED_REFERENCE!>v3<!>
v3 = ""
v4
v4 = ""
v5
v5 = ""
v6
v6 = ""
v7
<!UNRESOLVED_REFERENCE!>v4<!> = ""
<!UNRESOLVED_REFERENCE!>v5<!>
<!UNRESOLVED_REFERENCE!>v5<!> = ""
<!UNRESOLVED_REFERENCE!>v6<!>
<!UNRESOLVED_REFERENCE!>v6<!> = ""
<!UNRESOLVED_REFERENCE!>v7<!>
}
@@ -1,19 +0,0 @@
// !API_VERSION: 1.0
@SinceKotlin("1.1")
fun f() {}
@SinceKotlin("1.1")
var p = Unit
@SinceKotlin("1.1.2")
fun z() {}
fun t1() = f()
fun t2() = p
fun t3() { p = Unit }
fun t4() { z() }
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !API_VERSION: 1.0
@SinceKotlin("1.1")
@@ -13,8 +13,8 @@ open class C2(val x: Int) {
typealias C2_Alias = C2
val test1 = C1_Alias()
val test2 = C2_Alias()
val test2 = C2_Alias(<!NO_VALUE_FOR_PARAMETER!>)<!>
class Test3 : C1_Alias()
class Test4 : C2_Alias()
class Test4 : <!NO_VALUE_FOR_PARAMETER!>C2_Alias<!>()
@@ -31,7 +31,7 @@ fun use1(
c1: NewClassExperimentalInThePast,
t1: TypeAliasToNewClass
) {
newPublishedFun()
<!UNRESOLVED_REFERENCE!>newPublishedFun<!>()
newFunExperimentalInThePast()
newValExperimentalInThePast
NewClassExperimentalInThePast()
@@ -42,7 +42,7 @@ fun use2(
c2: NewClassExperimentalInThePast,
t2: TypeAliasToNewClass
) {
newPublishedFun()
<!UNRESOLVED_REFERENCE!>newPublishedFun<!>()
newFunExperimentalInThePast()
newValExperimentalInThePast
NewClassExperimentalInThePast()
@@ -53,7 +53,7 @@ fun use3(
c3: NewClassExperimentalInThePast,
t3: TypeAliasToNewClass
) {
newPublishedFun()
<!UNRESOLVED_REFERENCE!>newPublishedFun<!>()
newFunExperimentalInThePast()
newValExperimentalInThePast
NewClassExperimentalInThePast()
@@ -16871,6 +16871,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/fir/ConstValAccess.kt");
}
@Test
@TestMetadata("differentSinceKotlin.kt")
public void testDifferentSinceKotlin() throws Exception {
runTest("compiler/testData/codegen/box/fir/differentSinceKotlin.kt");
}
@Test
@TestMetadata("ExtensionAlias.kt")
public void testExtensionAlias() throws Exception {
@@ -17255,6 +17255,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/fir/CustomThrowableMessage.kt");
}
@Test
@TestMetadata("differentSinceKotlin.kt")
public void testDifferentSinceKotlin() throws Exception {
runTest("compiler/testData/codegen/box/fir/differentSinceKotlin.kt");
}
@Test
@TestMetadata("ExtensionAlias.kt")
public void testExtensionAlias() throws Exception {
@@ -13969,6 +13969,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/fir/ConstValAccess.kt");
}
@TestMetadata("differentSinceKotlin.kt")
public void testDifferentSinceKotlin() throws Exception {
runTest("compiler/testData/codegen/box/fir/differentSinceKotlin.kt");
}
@TestMetadata("ExtensionAlias.kt")
public void testExtensionAlias() throws Exception {
runTest("compiler/testData/codegen/box/fir/ExtensionAlias.kt");
@@ -161,6 +161,8 @@ object StandardClassIds {
val RestrictsSuspension = "RestrictsSuspension".coroutinesId()
val WasExperimental = "WasExperimental".baseId()
object Java {
val Deprecated = "Deprecated".javaLangId()
val Repeatable = "Repeatable".javaAnnotationId()