[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.
*/
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.
@@ -101,7 +94,7 @@ internal class KtFirVisibilityChecker(
}
JavaVisibilities.PackageVisibility -> {
val isSamePackage = classId.packageFqName == useSiteFile.firSymbol.fir.packageFqName
val isSamePackage = classIdIfNonLocal.packageFqName == useSiteFile.firSymbol.fir.packageFqName
if (!isSamePackage) return false
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.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.utils.cached
@@ -105,6 +104,22 @@ internal class KtFirNamedClassOrObjectSymbol(
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 analysisSession: KtFirAnalysisSession,
) : 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.
*/
@@ -59,20 +53,17 @@ internal class KtFirPsiJavaClassSymbol(
override val name: Name = withValidityAssertion { javaClass.name }
override val classIdIfNonLocal: ClassId? by cached { psi.classIdIfNonLocal }
private val qualifiedName: String
get() = withValidityAssertion { psi.qualifiedName!! }
override val classIdIfNonLocal: ClassId = withValidityAssertion {
psi.classIdIfNonLocal ?: error("${KtFirPsiJavaClassSymbol::class.simpleName} requires a non-local PSI class.")
}
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
classIdIfNonLocal.outerClassId != null -> KtSymbolKind.CLASS_MEMBER
else -> KtSymbolKind.TOP_LEVEL
}
}
@@ -88,7 +79,7 @@ internal class KtFirPsiJavaClassSymbol(
get() = withValidityAssertion { javaClass.visibility }
override val isInner: Boolean
get() = withValidityAssertion { classIdIfNonLocal?.outerClassId != null && !javaClass.isStatic }
get() = withValidityAssertion { classIdIfNonLocal.outerClassId != null && !javaClass.isStatic }
val outerClass: KtFirPsiJavaClassSymbol?
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)