FIR IDE: split ValidityToken.isValid into isValid & isAccessible

This commit is contained in:
Ilya Kirillov
2020-12-17 14:51:21 +01:00
parent a2befd4635
commit 9c26edbaaa
9 changed files with 48 additions and 28 deletions

View File

@@ -13,21 +13,30 @@ class ReadActionConfinementValidityToken(project: Project) : ValidityToken() {
private val modificationTracker = project.createProjectWideOutOfBlockModificationTracker()
private val onCreatedTimeStamp = modificationTracker.modificationCount
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun isValid(): Boolean {
val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return false
if (!application.isReadAccessAllowed) return false
return onCreatedTimeStamp == modificationTracker.modificationCount
}
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun getInvalidationReason(): String {
if (onCreatedTimeStamp != modificationTracker.modificationCount) return "PSI has changed since creation"
error("Getting invalidation reason for valid validity token")
}
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun isAccessible(): Boolean {
val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return false
if (!application.isReadAccessAllowed) return false
return true
}
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun getInaccessibilityReason(): String {
val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread"
if (!application.isReadAccessAllowed) return "Called outside read action"
if (onCreatedTimeStamp != modificationTracker.modificationCount) return "PSI has changed since creation"
error("Getting invalidation reason for valid validity token")
error("Getting inaccessibility reason for validity token when it is accessible")
}
companion object {

View File

@@ -8,14 +8,23 @@ package org.jetbrains.kotlin.idea.frontend.api
abstract class ValidityToken {
abstract fun isValid(): Boolean
abstract fun getInvalidationReason(): String
abstract fun isAccessible(): Boolean
abstract fun getInaccessibilityReason(): String
}
@Suppress("NOTHING_TO_INLINE")
inline fun ValidityToken.assertIsValid() {
inline fun ValidityToken.assertIsValidAndAccessible() {
if (!isValid()) {
throw InvalidEntityAccessException("Access to invalid $this, invalidation reason is ${getInvalidationReason()}")
throw InvalidEntityAccessException("Access to invalid $this: ${getInvalidationReason()}")
}
if (!isAccessible()) {
throw InaccessibleEntityAccessException("$this is inaccessible: ${getInaccessibilityReason()}")
}
}
class InvalidEntityAccessException(override val message: String): IllegalStateException()
abstract class BadEntityAccessException(): IllegalStateException()
class InvalidEntityAccessException(override val message: String) : BadEntityAccessException()
class InaccessibleEntityAccessException(override val message: String): BadEntityAccessException()

View File

@@ -9,12 +9,14 @@ interface ValidityTokenOwner {
val token: ValidityToken
}
fun ValidityTokenOwner.isValid(): Boolean = token.isValid()
@Suppress("NOTHING_TO_INLINE")
inline fun ValidityTokenOwner.assertIsValid() {
token.assertIsValid()
inline fun ValidityTokenOwner.assertIsValidAndAccessible() {
token.assertIsValidAndAccessible()
}
inline fun <R> ValidityTokenOwner.withValidityAssertion(action: () -> R): R {
assertIsValid()
assertIsValidAndAccessible()
return action()
}

View File

@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.api.getResolveState
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.ReadActionConfinementValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.assertIsValid
import org.jetbrains.kotlin.idea.frontend.api.assertIsValidAndAccessible
import org.jetbrains.kotlin.idea.frontend.api.components.*
import org.jetbrains.kotlin.idea.frontend.api.fir.components.*
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider
@@ -35,7 +35,7 @@ private constructor(
val context: KtFirAnalysisSessionContext,
) : KtAnalysisSession(token) {
init {
assertIsValid()
assertIsValidAndAccessible()
}
internal val towerDataContextProvider: TowerDataContextProvider = TowerDataContextProvider(this)

View File

@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSessionProvider
import org.jetbrains.kotlin.idea.frontend.api.assertIsValid
import org.jetbrains.kotlin.idea.frontend.api.assertIsValidAndAccessible
import org.jetbrains.kotlin.psi.KtElement
import java.util.concurrent.ConcurrentHashMap
@@ -37,7 +37,7 @@ class KtFirAnalysisSessionProvider(project: Project) : KtAnalysisSessionProvider
@Suppress("DEPRECATION")
KtFirAnalysisSession.createForElement(contextElement)
}.apply {
assertIsValid()
assertIsValidAndAccessible()
}
}

View File

@@ -89,10 +89,11 @@ internal class KtFirCallResolver(
val target = when (val calleeReference = calleeReference) {
is FirResolvedNamedReference -> calleeReference.getKtFunctionOrConstructorSymbol()?.let { KtSuccessCallTarget(it) }
is FirErrorNamedReference -> calleeReference.createErrorCallTarget()
is FirSimpleNamedReference -> error(
"Looks like FirFunctionCall was not resolved to BODY_RESOLVE phase, " +
"consider resolving it containing declaration before starting resolve calls"
)
is FirSimpleNamedReference ->
error(
"Looks like FirFunctionCall was not resolved to BODY_RESOLVE phase, " +
"consider resolving it containing declaration before starting resolve calls"
)
else -> error("Unexpected call reference ${calleeReference::class.simpleName}")
} ?: return null
return KtFunctionCall(target)

View File

@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.components
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.assertIsValid
import org.jetbrains.kotlin.idea.frontend.api.assertIsValidAndAccessible
import org.jetbrains.kotlin.idea.frontend.api.components.KtSubtypingComponent
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
@@ -20,7 +20,7 @@ internal class KtFirSubtypingComponent(
override val token: ValidityToken,
) : KtSubtypingComponent(), KtFirAnalysisSessionComponent {
override fun isEqualTo(first: KtType, second: KtType): Boolean = withValidityAssertion {
second.assertIsValid()
second.assertIsValidAndAccessible()
check(first is KtFirType)
check(second is KtFirType)
return AbstractTypeChecker.equalTypes(
@@ -31,7 +31,7 @@ internal class KtFirSubtypingComponent(
}
override fun isSubTypeOf(subType: KtType, superType: KtType): Boolean = withValidityAssertion {
superType.assertIsValid()
superType.assertIsValidAndAccessible()
check(subType is KtFirType)
check(superType is KtFirType)
return AbstractTypeChecker.isSubtypeOf(

View File

@@ -9,11 +9,10 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.api.resolvedFirToPhase
import org.jetbrains.kotlin.idea.fir.low.level.api.api.withFirDeclaration
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
import org.jetbrains.kotlin.idea.frontend.api.assertIsValid
import org.jetbrains.kotlin.idea.frontend.api.assertIsValidAndAccessible
import java.lang.ref.WeakReference
internal class FirRefWithValidityCheck<out D : FirDeclaration>(fir: D, resolveState: FirModuleResolveState, val token: ValidityToken) {
@@ -25,7 +24,7 @@ internal class FirRefWithValidityCheck<out D : FirDeclaration>(fir: D, resolveSt
firWeakRef.get() == null && resolveStateWeakRef.get() == null
inline fun <R> withFir(phase: FirResolvePhase = FirResolvePhase.RAW_FIR, crossinline action: (fir: D) -> R): R {
token.assertIsValid()
token.assertIsValidAndAccessible()
val fir = firWeakRef.get()
?: throw EntityWasGarbageCollectedException("FirElement")
val resolveState = resolveStateWeakRef.get()

View File

@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.utils
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
import org.jetbrains.kotlin.idea.frontend.api.assertIsValid
import org.jetbrains.kotlin.idea.frontend.api.assertIsValidAndAccessible
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
@@ -22,7 +22,7 @@ class ValidityAwareCachedValue<T>(
@Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Any, property: KProperty<*>): T {
token.assertIsValid()
token.assertIsValidAndAccessible()
return lazyValue.value
}
}