[AA] KT-56617 Sync hashCode/equals of Java and regular class symbols

- Some completion tests failed because the `hashCode` of
  `KtFirNamedClassOrObjectSymbol` and `KtFirPsiJavaClassSymbol` was
  different for symbols which should be equal. Both symbols now use the
  `ClassId` hash code.
- `KtFirNamedClassOrObjectSymbol.equals` is now properly symmetric with
  `KtFirPsiJavaClassSymbol`.
- `KtFirPsiJavaClassSymbol` now has a definite class ID, which
  simplifies its implementation a little. `KtFirPsiJavaClassSymbol`
  doesn't support local Java classes by design.
This commit is contained in:
Marco Pennekamp
2023-03-02 13:32:14 +01:00
committed by Space Team
parent f9fb718b37
commit c729435765
3 changed files with 28 additions and 27 deletions
@@ -82,13 +82,6 @@ internal class KtFirVisibilityChecker(
* Returns `null` if visibility cannot be decided by the heuristic. * Returns `null` if visibility cannot be decided by the heuristic.
*/ */
private fun KtFirPsiJavaClassSymbol.isVisibleByPsi(useSiteFile: KtFirFileSymbol): Boolean? { 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) { when (visibility) {
Visibilities.Private -> Visibilities.Private ->
// Private classes from Java cannot be accessed from Kotlin. // Private classes from Java cannot be accessed from Kotlin.
@@ -101,7 +94,7 @@ internal class KtFirVisibilityChecker(
} }
JavaVisibilities.PackageVisibility -> { JavaVisibilities.PackageVisibility -> {
val isSamePackage = classId.packageFqName == useSiteFile.firSymbol.fir.packageFqName val isSamePackage = classIdIfNonLocal.packageFqName == useSiteFile.firSymbol.fir.packageFqName
if (!isSamePackage) return false if (!isSamePackage) return false
return when (val outerClass = this.outerClass) { return when (val outerClass = this.outerClass) {
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession 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.annotations.KtFirAnnotationListForDeclaration
import org.jetbrains.kotlin.analysis.api.fir.findPsi import org.jetbrains.kotlin.analysis.api.fir.findPsi
import org.jetbrains.kotlin.analysis.api.fir.utils.cached import org.jetbrains.kotlin.analysis.api.fir.utils.cached
@@ -105,6 +104,22 @@ internal class KtFirNamedClassOrObjectSymbol(
createNamedClassOrObjectSymbolPointer() createNamedClassOrObjectSymbolPointer()
} }
override fun equals(other: Any?): Boolean = symbolEquals(other) /**
override fun hashCode(): Int = symbolHashCode() * [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()
} }
@@ -46,12 +46,6 @@ internal class KtFirPsiJavaClassSymbol(
override val psi: PsiClass, override val psi: PsiClass,
override val analysisSession: KtFirAnalysisSession, override val analysisSession: KtFirAnalysisSession,
) : KtNamedClassOrObjectSymbol(), KtFirPsiSymbol<PsiClass, FirRegularClassSymbol> { ) : KtNamedClassOrObjectSymbol(), KtFirPsiSymbol<PsiClass, FirRegularClassSymbol> {
init {
require(psi.qualifiedName != null) {
"${KtFirPsiJavaClassSymbol::class.simpleName} requires a PSI class with a qualified name."
}
}
/** /**
* [javaClass] is used to defer some properties to the compiler's view of a Java class. * [javaClass] is used to defer some properties to the compiler's view of a Java class.
*/ */
@@ -59,20 +53,17 @@ internal class KtFirPsiJavaClassSymbol(
override val name: Name = withValidityAssertion { javaClass.name } override val name: Name = withValidityAssertion { javaClass.name }
override val classIdIfNonLocal: ClassId? by cached { psi.classIdIfNonLocal } override val classIdIfNonLocal: ClassId = withValidityAssertion {
psi.classIdIfNonLocal ?: error("${KtFirPsiJavaClassSymbol::class.simpleName} requires a non-local PSI class.")
private val qualifiedName: String }
get() = withValidityAssertion { psi.qualifiedName!! }
override val origin: KtSymbolOrigin override val origin: KtSymbolOrigin
get() = withValidityAssertion { KtSymbolOrigin.JAVA } get() = withValidityAssertion { KtSymbolOrigin.JAVA }
override val symbolKind: KtSymbolKind override val symbolKind: KtSymbolKind
get() = withValidityAssertion { get() = withValidityAssertion {
val classId = classIdIfNonLocal
when { when {
classId == null -> KtSymbolKind.LOCAL classIdIfNonLocal.outerClassId != null -> KtSymbolKind.CLASS_MEMBER
classId.outerClassId != null -> KtSymbolKind.CLASS_MEMBER
else -> KtSymbolKind.TOP_LEVEL else -> KtSymbolKind.TOP_LEVEL
} }
} }
@@ -88,7 +79,7 @@ internal class KtFirPsiJavaClassSymbol(
get() = withValidityAssertion { javaClass.visibility } get() = withValidityAssertion { javaClass.visibility }
override val isInner: Boolean override val isInner: Boolean
get() = withValidityAssertion { classIdIfNonLocal?.outerClassId != null && !javaClass.isStatic } get() = withValidityAssertion { classIdIfNonLocal.outerClassId != null && !javaClass.isStatic }
val outerClass: KtFirPsiJavaClassSymbol? val outerClass: KtFirPsiJavaClassSymbol?
get() = withValidityAssertion { get() = withValidityAssertion {
@@ -128,9 +119,11 @@ internal class KtFirPsiJavaClassSymbol(
} }
/** /**
* Since [PsiEquivalenceUtil] doesn't have a hash code function, we use the class's fully qualified name as a conservative hash code. * 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 = qualifiedName.hashCode() override fun hashCode(): Int = classIdIfNonLocal.hashCode()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Slow Operations (requiring access to the underlying FIR class symbol) // Slow Operations (requiring access to the underlying FIR class symbol)