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 b34bec3c04b..f86245f7f76 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 @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.analysis.api.fir.symbols import com.intellij.psi.PsiElement 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.annotations.KtFirAnnotationListForDeclaration @@ -17,10 +16,7 @@ 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.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.Visibilities import org.jetbrains.kotlin.descriptors.Visibility @@ -32,7 +28,7 @@ import org.jetbrains.kotlin.name.Name internal class KtFirNamedClassOrObjectSymbol( override val firSymbol: FirRegularClassSymbol, override val analysisSession: KtFirAnalysisSession, -) : KtNamedClassOrObjectSymbol(), KtFirSymbol { +) : KtFirNamedClassOrObjectSymbolBase() { override val token: KtLifetimeToken get() = builder.token override val psi: PsiElement? by cached { firSymbol.findPsi() } @@ -51,7 +47,6 @@ internal class KtFirNamedClassOrObjectSymbol( } } - /* FirRegularClass visibility is not modified by STATUS only for Unknown, so it can be taken from RAW */ override val visibility: Visibility get() = withValidityAssertion { @@ -83,10 +78,6 @@ internal class KtFirNamedClassOrObjectSymbol( } } - override val superTypes: List by cached { - firSymbol.superTypesAndAnnotationsListForRegularClass(builder) - } - override val typeParameters = withValidityAssertion { firSymbol.createRegularKtTypeParameters(builder) } @@ -98,28 +89,4 @@ internal class KtFirNamedClassOrObjectSymbol( } override val symbolKind: KtSymbolKind get() = withValidityAssertion { getSymbolKind() } - - context(KtAnalysisSession) - override fun createPointer(): KtSymbolPointer = withValidityAssertion { - createNamedClassOrObjectSymbolPointer() - } - - /** - * [KtFirNamedClassOrObjectSymbol] must be able to equal [KtFirPsiJavaClassSymbol]. - */ - override fun equals(other: Any?): Boolean { - if (other === this) return true - - return when (other) { - is KtFirNamedClassOrObjectSymbol -> symbolEquals(other) - is KtFirPsiJavaClassSymbol -> other == this - else -> false - } - } - - /** - * A non-local [KtFirNamedClassOrObjectSymbol] must have the same kind of hash code as [KtFirPsiJavaClassSymbol] so that they are - * interchangeable in collections. - */ - override fun hashCode(): Int = classIdIfNonLocal?.hashCode() ?: symbolHashCode() } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirNamedClassOrObjectSymbolBase.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirNamedClassOrObjectSymbolBase.kt new file mode 100644 index 00000000000..0be92f02a8b --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirNamedClassOrObjectSymbolBase.kt @@ -0,0 +1,73 @@ +/* + * 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.codeInsight.PsiEquivalenceUtil +import com.intellij.psi.PsiClass +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +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.lifetime.withValidityAssertion +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.fir.symbols.impl.FirRegularClassSymbol + +/** + * [KtFirNamedClassOrObjectSymbolBase] provides shared equality and hash code implementations for FIR-based named class or object symbols so + * that symbols of different kinds can be compared and remain interchangeable. + */ +internal sealed class KtFirNamedClassOrObjectSymbolBase : KtNamedClassOrObjectSymbol(), KtFirSymbol { + /** + * Whether [firSymbol] is computed lazily. Equality will fall back to PSI-equality if one of the symbols is lazy and both classes have + * an associated [PsiClass]. + */ + open val hasLazyFirSymbol: Boolean get() = false + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other !is KtFirNamedClassOrObjectSymbolBase) return false + + if (hasLazyFirSymbol || other.hasLazyFirSymbol) { + val psiClass = psi as? PsiClass ?: return symbolEquals(other) + val otherPsiClass = other.psi as? PsiClass ?: return symbolEquals(other) + return PsiEquivalenceUtil.areElementsEquivalent(psiClass, otherPsiClass) + } + return symbolEquals(other) + } + + /** + * All kinds of non-local named class or object symbols must have the same kind of hash code. The class ID is the best option, as the + * same class/object may be represented by multiple different symbols. + */ + override fun hashCode(): Int = classIdIfNonLocal?.hashCode() ?: symbolHashCode() + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Shared Operations + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override val superTypes: List by cached { + firSymbol.superTypesAndAnnotationsListForRegularClass(builder) + } + + context(KtAnalysisSession) + override fun createPointer(): KtSymbolPointer = withValidityAssertion { + 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/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 index 1ab5e123eda..88807da2947 100644 --- 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 @@ -5,10 +5,8 @@ package org.jetbrains.kotlin.analysis.api.fir.symbols -import com.intellij.codeInsight.PsiEquivalenceUtil 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 @@ -22,8 +20,6 @@ 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.analysis.low.level.api.fir.providers.firClassByPsiClassProvider import org.jetbrains.kotlin.analysis.project.structure.getKtModule import org.jetbrains.kotlin.analysis.utils.classIdIfNonLocal @@ -45,7 +41,7 @@ import org.jetbrains.kotlin.name.Name internal class KtFirPsiJavaClassSymbol( override val psi: PsiClass, override val analysisSession: KtFirAnalysisSession, -) : KtNamedClassOrObjectSymbol(), KtFirPsiSymbol { +) : KtFirNamedClassOrObjectSymbolBase(), KtFirPsiSymbol { /** * [javaClass] is used to defer some properties to the compiler's view of a Java class. */ @@ -110,24 +106,11 @@ internal class KtFirPsiJavaClassSymbol( override val contextReceivers: List get() = withValidityAssertion { emptyList() } - override fun equals(other: Any?): Boolean { - if (other === this) return true - if (other == null || other !is KtNamedClassOrObjectSymbol) return false - - val otherPsiClass = other.psi as? PsiClass ?: return false - return PsiEquivalenceUtil.areElementsEquivalent(psi, otherPsiClass) - } - - /** - * Since [PsiEquivalenceUtil] doesn't have a hash code function, we use the class ID as a conservative hash code. - * - * Also see [KtFirNamedClassOrObjectSymbol.hashCode], which must generate the same hash code for non-local classes. - */ - override fun hashCode(): Int = classIdIfNonLocal.hashCode() - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Slow Operations (requiring access to the underlying FIR class symbol) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override val hasLazyFirSymbol: Boolean get() = true + override val firSymbol: FirRegularClassSymbol by cached { val module = psi.getKtModule(analysisSession.project) val provider = analysisSession.firResolveSession.getSessionFor(module).firClassByPsiClassProvider @@ -139,10 +122,6 @@ internal class KtFirPsiJavaClassSymbol( firClassSymbol } - override val superTypes: List by cached { - firSymbol.superTypesAndAnnotationsListForRegularClass(builder) - } - override val typeParameters: List by cached { if (hasTypeParameters) firSymbol.createRegularKtTypeParameters(builder) else emptyList() @@ -152,9 +131,4 @@ internal class KtFirPsiJavaClassSymbol( if (hasAnnotations) KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token) else KtEmptyAnnotationsList(token) } - - context(KtAnalysisSession) - override fun createPointer(): KtSymbolPointer = withValidityAssertion { - createNamedClassOrObjectSymbolPointer() - } } 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 1c80568863e..11a4bb26034 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,20 +8,12 @@ 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 @@ -124,18 +116,3 @@ 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) - } -}