[AA] KT-56617 Optimize deprecation calculation for Java class symbols

- Most Java classes aren't deprecated. To avoid building `firSymbol`
  in such cases, this commit adds a simple heuristic which checks the
  class's annotations for the presence of one of the deprecation
  annotations recognized by the Kotlin compiler.
- Note that annotations are compared via simple names, so there is a
  slight margin of error. However, comparing class IDs is more costly in
  my tests, because getting an annotation's class ID is not as cheap as
  getting its simple name.
This commit is contained in:
Marco Pennekamp
2023-03-01 20:52:40 +01:00
committed by Space Team
parent d5933f28ab
commit f9fb718b37
3 changed files with 39 additions and 20 deletions
@@ -8,23 +8,17 @@ package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.components.KtSymbolInfoProvider
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.symbols.*
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirBackingFieldSymbol
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSyntheticJavaPropertySymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtReceiverParameterSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.fir.analysis.checkers.getAllowedAnnotationTargets
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.getDeprecationForCallSite
import org.jetbrains.kotlin.fir.declarations.getJvmNameFromAnnotation
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.load.java.JvmAbi
@@ -41,6 +35,12 @@ internal class KtFirSymbolInfoProvider(
override fun getDeprecation(symbol: KtSymbol): DeprecationInfo? {
if (symbol is KtFirBackingFieldSymbol || symbol is KtFirPackageSymbol || symbol is KtReceiverParameterSymbol) return null
require(symbol is KtFirSymbol<*>) { "${this::class}" }
// Optimization: Avoid building `firSymbol` of `KtFirPsiJavaClassSymbol` if it definitely isn't deprecated.
if (symbol is KtFirPsiJavaClassSymbol && !symbol.mayHaveDeprecation()) {
return null
}
return when (val firSymbol = symbol.firSymbol) {
is FirPropertySymbol -> {
firSymbol.getDeprecationForCallSite(apiVersion, AnnotationUseSiteTarget.PROPERTY)
@@ -51,6 +51,15 @@ internal class KtFirSymbolInfoProvider(
}
}
private fun KtFirPsiJavaClassSymbol.mayHaveDeprecation(): Boolean {
if (!hasAnnotations) return false
// Check the simple names of the Java annotations. While presence of such an annotation name does not prove deprecation, it is a
// necessary condition for it. Type aliases are not a problem here: Java code cannot access Kotlin type aliases. (Currently,
// deprecation annotation type aliases do not work in Kotlin, either, but this might change in the future.)
return annotationSimpleNames.any { it != null && it in deprecationAnnotationSimpleNames }
}
override fun getDeprecation(symbol: KtSymbol, annotationUseSiteTarget: AnnotationUseSiteTarget?): DeprecationInfo? {
require(symbol is KtFirSymbol<*>)
return if (annotationUseSiteTarget != null) {
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForD
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.impl.base.annotations.KtEmptyAnnotationsList
import org.jetbrains.kotlin.analysis.api.impl.base.symbols.toKtClassKind
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
@@ -25,19 +24,18 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firClassByPsiClassProvider
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.analysis.utils.classIdIfNonLocal
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firClassByPsiClassProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.analysis.utils.classIdIfNonLocal
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.java.classKind
import org.jetbrains.kotlin.fir.java.modality
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
/**
* Implements [KtNamedClassOrObjectSymbol] for a Java class. The underlying [firSymbol] is built lazily and only when needed. Many simple
@@ -105,7 +103,13 @@ internal class KtFirPsiJavaClassSymbol(
}
val hasTypeParameters: Boolean
get() = withValidityAssertion { javaClass.typeParameters.isNotEmpty() }
get() = withValidityAssertion { psi.typeParameters.isNotEmpty() }
val annotationSimpleNames: List<String?>
get() = withValidityAssertion { psi.annotations.map { it.nameReferenceElement?.referenceName } }
val hasAnnotations: Boolean
get() = withValidityAssertion { psi.annotations.isNotEmpty() }
override val isData: Boolean get() = withValidityAssertion { false }
override val isInline: Boolean get() = withValidityAssertion { false }
@@ -152,8 +156,8 @@ internal class KtFirPsiJavaClassSymbol(
}
override val annotationsList: KtAnnotationsList by cached {
if (javaClass.annotations.isEmpty()) KtEmptyAnnotationsList(token)
else KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
if (hasAnnotations) KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
else KtEmptyAnnotationsList(token)
}
context(KtAnalysisSession)
@@ -178,6 +178,12 @@ private fun FirAnnotation.getDeprecationLevel(): DeprecationLevelValue? {
return DeprecationLevelValue.values().find { it.name == targetName }
}
val deprecationAnnotationSimpleNames: Set<String> = setOf(
StandardClassIds.Annotations.Deprecated.shortClassName.asString(),
StandardClassIds.Annotations.Java.Deprecated.shortClassName.asString(),
StandardClassIds.Annotations.SinceKotlin.shortClassName.asString(),
)
private fun List<FirAnnotation>.extractDeprecationAnnotationInfoPerUseSite(
session: FirSession, fromJava: Boolean
): DeprecationAnnotationInfoPerUseSiteStorage {