[AA] KT-56617 Optimize KtFirVisibilityChecker for Java class symbols
- `KtFirVisibilityChecker` accessed `KtFirPsiJavaClassSymbol.firSymbol` a significant number of times during completion, which led to many instances of FIR class construction. - This commit introduces a heuristic to `KtFirVisibilityChecker` specifically for `KtFirPsiJavaClassSymbol` to bypass the compiler's visibility checker. The fact that we're dealing with Java class visibility from Kotlin makes this feasible without reimplementing the full logic of `FirVisibilityChecker`.
This commit is contained in:
committed by
Space Team
parent
26e2c48a46
commit
77e374db77
+49
@@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.analysis.api.components.KtVisibilityChecker
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirFileSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirPsiJavaClassSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||
@@ -17,11 +18,14 @@ import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignation
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirSafe
|
||||
import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.effectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.packageFqName
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.publishedApiEffectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
@@ -44,6 +48,10 @@ internal class KtFirVisibilityChecker(
|
||||
require(candidateSymbol is KtFirSymbol<*>)
|
||||
require(useSiteFile is KtFirFileSymbol)
|
||||
|
||||
if (candidateSymbol is KtFirPsiJavaClassSymbol) {
|
||||
candidateSymbol.isVisibleByPsi(useSiteFile)?.let { return it }
|
||||
}
|
||||
|
||||
val useSiteFirFile = useSiteFile.firSymbol.fir
|
||||
val containers = collectContainingDeclarations(position)
|
||||
|
||||
@@ -65,6 +73,47 @@ internal class KtFirVisibilityChecker(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [isVisibleByPsi] is a heuristic that decides visibility for most [KtFirPsiJavaClassSymbol]s without deferring to its FIR symbol,
|
||||
* thereby avoiding lazy construction of the FIR class. The visibility rules are tailored specifically for Java classes accessed from
|
||||
* Kotlin. They cover the most popular visibilities `private`, `public`, and default (package) visibility for top-level and nested
|
||||
* classes.
|
||||
*
|
||||
* Returns `null` if visibility cannot be decided by the heuristic.
|
||||
*/
|
||||
private fun KtFirPsiJavaClassSymbol.isVisibleByPsi(useSiteFile: KtFirFileSymbol): Boolean? {
|
||||
val classId = classIdIfNonLocal
|
||||
if (classId == null) {
|
||||
// Local classes are only visible in their containing method. Since we're checking the visibility for a Kotlin file, a local
|
||||
// class from Java can never be accessible.
|
||||
return false
|
||||
}
|
||||
|
||||
when (visibility) {
|
||||
Visibilities.Private ->
|
||||
// Private classes from Java cannot be accessed from Kotlin.
|
||||
return false
|
||||
|
||||
Visibilities.Public ->
|
||||
return when (val outerClass = this.outerClass) {
|
||||
null -> true
|
||||
else -> outerClass.isVisibleByPsi(useSiteFile)
|
||||
}
|
||||
|
||||
JavaVisibilities.PackageVisibility -> {
|
||||
val isSamePackage = classId.packageFqName == useSiteFile.firSymbol.fir.packageFqName
|
||||
if (!isSamePackage) return false
|
||||
|
||||
return when (val outerClass = this.outerClass) {
|
||||
null -> true
|
||||
else -> outerClass.isVisibleByPsi(useSiteFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun isPublicApi(symbol: KtSymbolWithVisibility): Boolean {
|
||||
require(symbol is KtFirSymbol<*>)
|
||||
val declaration = symbol.firSymbol.fir as? FirMemberDeclaration ?: return false
|
||||
|
||||
+5
@@ -92,6 +92,11 @@ internal class KtFirPsiJavaClassSymbol(
|
||||
override val isInner: Boolean
|
||||
get() = withValidityAssertion { classIdIfNonLocal?.outerClassId != null && !javaClass.isStatic }
|
||||
|
||||
val outerClass: KtFirPsiJavaClassSymbol?
|
||||
get() = withValidityAssertion {
|
||||
psi.containingClass?.let { KtFirPsiJavaClassSymbol(it, analysisSession) }
|
||||
}
|
||||
|
||||
override val isData: Boolean get() = withValidityAssertion { false }
|
||||
override val isInline: Boolean get() = withValidityAssertion { false }
|
||||
override val isFun: Boolean get() = withValidityAssertion { false }
|
||||
|
||||
Reference in New Issue
Block a user