diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt index 5191f3ca08a..46aab598253 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression import org.jetbrains.kotlin.fir.java.JavaScopeProvider import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass import org.jetbrains.kotlin.analysis.api.fir.scopes.JavaClassDeclaredMembersEnhancementScope +import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirPsiJavaClassSymbol import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertiesScope import org.jetbrains.kotlin.fir.resolve.scope @@ -68,6 +69,7 @@ internal class KtFirScopeProvider( private inline fun KtSymbolWithMembers.withFirForScope(crossinline body: (FirClass) -> T): T? { return when (this) { is KtFirNamedClassOrObjectSymbol -> body(firSymbol.fir) + is KtFirPsiJavaClassSymbol -> body(firSymbol.fir) is KtFirAnonymousObjectSymbol -> body(firSymbol.fir) is KtFirEnumEntrySymbol -> { firSymbol.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolProviderByJavaPsi.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolProviderByJavaPsi.kt index 70dae899a5f..fe253ab23d4 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolProviderByJavaPsi.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolProviderByJavaPsi.kt @@ -9,11 +9,10 @@ import com.intellij.psi.* import com.intellij.psi.impl.compiled.ClsElementImpl import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession +import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirPsiJavaClassSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolProviderByJavaPsi -import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firClassByPsiClassProvider -import org.jetbrains.kotlin.analysis.project.structure.getKtModule import org.jetbrains.kotlin.asJava.KtLightClassMarker import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.name.Name @@ -28,10 +27,7 @@ internal class KtFirSymbolProviderByJavaPsi( if (psiClass is KtLightClassMarker) return null if (psiClass.isKotlinCompiledClass()) return null - val module = psiClass.getKtModule(analysisSession.project) - val provider = firResolveSession.getSessionFor(module).firClassByPsiClassProvider - val firClass = provider.getFirClass(psiClass) ?: return null - return firSymbolBuilder.classifierBuilder.buildNamedClassOrObjectSymbol(firClass) + return KtFirPsiJavaClassSymbol(psiClass, analysisSession) } private fun PsiClass.isKotlinCompiledClass() = diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirNamedClassOrObjectSymbol.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirNamedClassOrObjectSymbol.kt index 1e258cc6fcf..70157fbee43 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirNamedClassOrObjectSymbol.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirNamedClassOrObjectSymbol.kt @@ -10,9 +10,9 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession +import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration import org.jetbrains.kotlin.analysis.api.fir.findPsi -import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirClassLikeSymbolPointer import org.jetbrains.kotlin.analysis.api.fir.utils.cached import org.jetbrains.kotlin.analysis.api.impl.base.symbols.toKtClassKind import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken @@ -20,15 +20,11 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind -import org.jetbrains.kotlin.analysis.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException -import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer -import org.jetbrains.kotlin.analysis.api.symbols.pointers.UnsupportedSymbolKind import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility -import org.jetbrains.kotlin.fir.declarations.FirTypeParameter import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.name.ClassId @@ -93,12 +89,9 @@ internal class KtFirNamedClassOrObjectSymbol( } override val typeParameters = withValidityAssertion { - firSymbol.fir.typeParameters.filterIsInstance().map { typeParameter -> - builder.classifierBuilder.buildTypeParameterSymbol(typeParameter.symbol) - } + firSymbol.createRegularKtTypeParameters(builder) } - @OptIn(KtAnalysisApiInternals::class) override val classKind: KtClassKind get() = withValidityAssertion { @@ -109,17 +102,7 @@ internal class KtFirNamedClassOrObjectSymbol( context(KtAnalysisSession) override fun createPointer(): KtSymbolPointer = withValidityAssertion { - KtPsiBasedSymbolPointer.createForSymbolFromSource(this)?.let { return it } - - when (val symbolKind = symbolKind) { - KtSymbolKind.LOCAL -> - throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(classIdIfNonLocal?.asString() ?: name.asString()) - - KtSymbolKind.CLASS_MEMBER, KtSymbolKind.TOP_LEVEL -> - KtFirClassLikeSymbolPointer(classIdIfNonLocal!!, KtNamedClassOrObjectSymbol::class) - - else -> throw UnsupportedSymbolKind(this::class, symbolKind) - } + createNamedClassOrObjectSymbolPointer() } override fun equals(other: Any?): Boolean = symbolEquals(other) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirPsiJavaClassSymbol.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirPsiJavaClassSymbol.kt new file mode 100644 index 00000000000..b7a5ee67584 --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirPsiJavaClassSymbol.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.fir.symbols + +import com.intellij.psi.PsiClass +import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList +import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver +import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession +import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration +import org.jetbrains.kotlin.analysis.api.fir.utils.cached +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 +import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin +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.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.load.java.structure.JavaClass +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl + +/** + * Implements [KtNamedClassOrObjectSymbol] for a Java class. The underlying [firSymbol] is built lazily and only when needed. Many simple + * properties are computed from the given [PsiClass] instead of [firSymbol]. This improves performance when "slow" properties don't need to + * be accessed. + */ +internal class KtFirPsiJavaClassSymbol( + override val psi: PsiClass, + override val analysisSession: KtFirAnalysisSession, +) : KtNamedClassOrObjectSymbol(), KtFirPsiSymbol { + /** + * [javaClass] is used to defer some properties to the compiler's view of a Java class. + */ + private val javaClass: JavaClass = JavaClassImpl(psi) + + override val name: Name = withValidityAssertion { javaClass.name } + + override val classIdIfNonLocal: ClassId? by cached { psi.classIdIfNonLocal } + + override val origin: KtSymbolOrigin + get() = withValidityAssertion { KtSymbolOrigin.JAVA } + + override val symbolKind: KtSymbolKind + get() = withValidityAssertion { + val classId = classIdIfNonLocal + when { + classId == null -> KtSymbolKind.LOCAL + classId.outerClassId != null -> KtSymbolKind.CLASS_MEMBER + else -> KtSymbolKind.TOP_LEVEL + } + } + + @OptIn(KtAnalysisApiInternals::class) + override val classKind: KtClassKind + get() = withValidityAssertion { javaClass.classKind.toKtClassKind(isCompanionObject = false) } + + override val modality: Modality + get() = withValidityAssertion { javaClass.modality } + + override val visibility: Visibility + get() = withValidityAssertion { javaClass.visibility } + + override val isInner: Boolean + get() = withValidityAssertion { classIdIfNonLocal?.outerClassId != null && !javaClass.isStatic } + + override val isData: Boolean get() = withValidityAssertion { false } + override val isInline: Boolean get() = withValidityAssertion { false } + override val isFun: Boolean get() = withValidityAssertion { false } + override val isExternal: Boolean get() = withValidityAssertion { false } + override val companionObject: KtNamedClassOrObjectSymbol? get() = withValidityAssertion { null } + + override val contextReceivers: List get() = withValidityAssertion { emptyList() } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Slow Operations (requiring access to the underlying FIR class symbol) + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override val firSymbol: FirRegularClassSymbol by cached { + val module = psi.getKtModule(analysisSession.project) + val provider = analysisSession.firResolveSession.getSessionFor(module).firClassByPsiClassProvider + val firClassSymbol = provider.getFirClass(psi) + + require(firClassSymbol != null) { + "A FIR class symbol should be available for ${KtFirPsiJavaClassSymbol::class.simpleName} `$classIdIfNonLocal`." + } + firClassSymbol + } + + override val superTypes: List by cached { + firSymbol.superTypesAndAnnotationsListForRegularClass(builder) + } + + override val typeParameters: List by cached { + firSymbol.createRegularKtTypeParameters(builder) + } + + override val annotationsList: KtAnnotationsList by cached { + KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token) + } + + context(KtAnalysisSession) + override fun createPointer(): KtSymbolPointer = withValidityAssertion { + createNamedClassOrObjectSymbolPointer() + } + + // TODO (marco): Can we implement equality and hash code without falling back to the symbol? + override fun equals(other: Any?): Boolean = symbolEquals(other) + override fun hashCode(): Int = symbolHashCode() +} diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirPsiSymbol.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirPsiSymbol.kt new file mode 100644 index 00000000000..6c2888f3597 --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirPsiSymbol.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.fir.symbols + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol + +/** + * A [KtFirSymbol] that is backed by some [PsiElement] and builds [firSymbol] lazily (by convention), allowing some properties to be + * calculated without the need to build a [firSymbol]. + */ +internal interface KtFirPsiSymbol

> : KtFirSymbol { + override val psi: P + + /** + * The origin should be provided without using [firSymbol], if possible. + */ + abstract override val origin: KtSymbolOrigin +} diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt index 18810553468..1c80568863e 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt @@ -8,12 +8,20 @@ package org.jetbrains.kotlin.analysis.api.fir.symbols import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtInitializerValue import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder +import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirClassLikeSymbolPointer import org.jetbrains.kotlin.analysis.api.fir.utils.asKtInitializerValue import org.jetbrains.kotlin.analysis.api.impl.base.KtContextReceiverImpl +import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind +import org.jetbrains.kotlin.analysis.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException +import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer +import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer +import org.jetbrains.kotlin.analysis.api.symbols.pointers.UnsupportedSymbolKind import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext @@ -57,6 +65,13 @@ internal fun FirBasedSymbol.createKtTypeParameters( } } +internal fun FirBasedSymbol.createRegularKtTypeParameters( + builder: KtSymbolByFirBuilder, +): List where D : FirTypeParameterRefsOwner, D : FirDeclaration { + return fir.typeParameters.filterIsInstance().map { typeParameter -> + builder.classifierBuilder.buildTypeParameterSymbol(typeParameter.symbol) + } +} internal fun FirCallableSymbol<*>.createContextReceivers( builder: KtSymbolByFirBuilder @@ -109,3 +124,18 @@ internal fun FirVariableSymbol<*>.getKtConstantInitializer(resolveSession: LLFir ?.classKind == ClassKind.ANNOTATION_CLASS return firInitializer.asKtInitializerValue(moduleData.session, parentIsAnnotation) } + +context(KtAnalysisSession) +internal fun KtNamedClassOrObjectSymbol.createNamedClassOrObjectSymbolPointer(): KtSymbolPointer { + KtPsiBasedSymbolPointer.createForSymbolFromSource(this)?.let { return it } + + return when (val symbolKind = symbolKind) { + KtSymbolKind.LOCAL -> + throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(classIdIfNonLocal?.asString() ?: name.asString()) + + KtSymbolKind.CLASS_MEMBER, KtSymbolKind.TOP_LEVEL -> + KtFirClassLikeSymbolPointer(classIdIfNonLocal!!, KtNamedClassOrObjectSymbol::class) + + else -> throw UnsupportedSymbolKind(this::class, symbolKind) + } +} diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt index a00c978ad9b..30ce5f9f4e2 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt @@ -34,7 +34,7 @@ internal val JavaModifierListOwner.modality: Modality else -> Modality.OPEN } -internal val JavaClass.modality: Modality +val JavaClass.modality: Modality get() = when { isAnnotationType || isEnum -> Modality.FINAL isSealed -> Modality.SEALED @@ -43,7 +43,7 @@ internal val JavaClass.modality: Modality else -> Modality.OPEN } -internal val JavaClass.classKind: ClassKind +val JavaClass.classKind: ClassKind get() = when { isAnnotationType -> ClassKind.ANNOTATION_CLASS isInterface -> ClassKind.INTERFACE