[AA] KT-56617 Introduce shared equals/hashCode for class symbols

- Instead of spreading `equals` and `hashCode` implementations over
  `KtFirNamedClassOrObjectSymbol` and `KtFirPsiJavaClassSymbol`, this
  commit introduces a shared base class.
This commit is contained in:
Marco Pennekamp
2023-03-06 19:15:55 +01:00
committed by Space Team
parent c729435765
commit 1e43e8d975
4 changed files with 77 additions and 86 deletions
@@ -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<FirRegularClassSymbol> {
) : 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<KtType> 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<KtNamedClassOrObjectSymbol> = 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()
}
@@ -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<FirRegularClassSymbol> {
/**
* 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<KtType> by cached {
firSymbol.superTypesAndAnnotationsListForRegularClass(builder)
}
context(KtAnalysisSession)
override fun createPointer(): KtSymbolPointer<KtNamedClassOrObjectSymbol> = withValidityAssertion {
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtNamedClassOrObjectSymbol>(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)
}
}
}
@@ -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<PsiClass, FirRegularClassSymbol> {
) : KtFirNamedClassOrObjectSymbolBase(), KtFirPsiSymbol<PsiClass, FirRegularClassSymbol> {
/**
* [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<KtContextReceiver> 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<KtType> by cached {
firSymbol.superTypesAndAnnotationsListForRegularClass(builder)
}
override val typeParameters: List<KtTypeParameterSymbol> 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<KtNamedClassOrObjectSymbol> = withValidityAssertion {
createNamedClassOrObjectSymbolPointer()
}
}
@@ -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<KtNamedClassOrObjectSymbol> {
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtNamedClassOrObjectSymbol>(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)
}
}