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
@@ -13,21 +13,30 @@ class ReadActionConfinementValidityToken(project: Project) : ValidityToken() {
private val modificationTracker = project.createProjectWideOutOfBlockModificationTracker() private val modificationTracker = project.createProjectWideOutOfBlockModificationTracker()
private val onCreatedTimeStamp = modificationTracker.modificationCount private val onCreatedTimeStamp = modificationTracker.modificationCount
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun isValid(): Boolean { override fun isValid(): Boolean {
val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return false
if (!application.isReadAccessAllowed) return false
return onCreatedTimeStamp == modificationTracker.modificationCount return onCreatedTimeStamp == modificationTracker.modificationCount
} }
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class) @OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun getInvalidationReason(): String { 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() val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread" if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread"
if (!application.isReadAccessAllowed) return "Called outside read action" if (!application.isReadAccessAllowed) return "Called outside read action"
if (onCreatedTimeStamp != modificationTracker.modificationCount) return "PSI has changed since creation" error("Getting inaccessibility reason for validity token when it is accessible")
error("Getting invalidation reason for valid validity token")
} }
companion object { companion object {
@@ -8,14 +8,23 @@ package org.jetbrains.kotlin.idea.frontend.api
abstract class ValidityToken { abstract class ValidityToken {
abstract fun isValid(): Boolean abstract fun isValid(): Boolean
abstract fun getInvalidationReason(): String abstract fun getInvalidationReason(): String
abstract fun isAccessible(): Boolean
abstract fun getInaccessibilityReason(): String
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun ValidityToken.assertIsValid() { inline fun ValidityToken.assertIsValidAndAccessible() {
if (!isValid()) { 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()
@@ -9,12 +9,14 @@ interface ValidityTokenOwner {
val token: ValidityToken val token: ValidityToken
} }
fun ValidityTokenOwner.isValid(): Boolean = token.isValid()
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun ValidityTokenOwner.assertIsValid() { inline fun ValidityTokenOwner.assertIsValidAndAccessible() {
token.assertIsValid() token.assertIsValidAndAccessible()
} }
inline fun <R> ValidityTokenOwner.withValidityAssertion(action: () -> R): R { inline fun <R> ValidityTokenOwner.withValidityAssertion(action: () -> R): R {
assertIsValid() assertIsValidAndAccessible()
return action() return action()
} }
@@ -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.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.ReadActionConfinementValidityToken import org.jetbrains.kotlin.idea.frontend.api.ReadActionConfinementValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken 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.components.*
import org.jetbrains.kotlin.idea.frontend.api.fir.components.* import org.jetbrains.kotlin.idea.frontend.api.fir.components.*
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider
@@ -35,7 +35,7 @@ private constructor(
val context: KtFirAnalysisSessionContext, val context: KtFirAnalysisSessionContext,
) : KtAnalysisSession(token) { ) : KtAnalysisSession(token) {
init { init {
assertIsValid() assertIsValidAndAccessible()
} }
internal val towerDataContextProvider: TowerDataContextProvider = TowerDataContextProvider(this) internal val towerDataContextProvider: TowerDataContextProvider = TowerDataContextProvider(this)
@@ -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.InvalidWayOfUsingAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSessionProvider 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 org.jetbrains.kotlin.psi.KtElement
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@@ -37,7 +37,7 @@ class KtFirAnalysisSessionProvider(project: Project) : KtAnalysisSessionProvider
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
KtFirAnalysisSession.createForElement(contextElement) KtFirAnalysisSession.createForElement(contextElement)
}.apply { }.apply {
assertIsValid() assertIsValidAndAccessible()
} }
} }
@@ -89,10 +89,11 @@ internal class KtFirCallResolver(
val target = when (val calleeReference = calleeReference) { val target = when (val calleeReference = calleeReference) {
is FirResolvedNamedReference -> calleeReference.getKtFunctionOrConstructorSymbol()?.let { KtSuccessCallTarget(it) } is FirResolvedNamedReference -> calleeReference.getKtFunctionOrConstructorSymbol()?.let { KtSuccessCallTarget(it) }
is FirErrorNamedReference -> calleeReference.createErrorCallTarget() is FirErrorNamedReference -> calleeReference.createErrorCallTarget()
is FirSimpleNamedReference -> error( is FirSimpleNamedReference ->
"Looks like FirFunctionCall was not resolved to BODY_RESOLVE phase, " + error(
"consider resolving it containing declaration before starting resolve calls" "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}") else -> error("Unexpected call reference ${calleeReference::class.simpleName}")
} ?: return null } ?: return null
return KtFunctionCall(target) return KtFunctionCall(target)
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.components package org.jetbrains.kotlin.idea.frontend.api.fir.components
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken 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.components.KtSubtypingComponent
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
@@ -20,7 +20,7 @@ internal class KtFirSubtypingComponent(
override val token: ValidityToken, override val token: ValidityToken,
) : KtSubtypingComponent(), KtFirAnalysisSessionComponent { ) : KtSubtypingComponent(), KtFirAnalysisSessionComponent {
override fun isEqualTo(first: KtType, second: KtType): Boolean = withValidityAssertion { override fun isEqualTo(first: KtType, second: KtType): Boolean = withValidityAssertion {
second.assertIsValid() second.assertIsValidAndAccessible()
check(first is KtFirType) check(first is KtFirType)
check(second is KtFirType) check(second is KtFirType)
return AbstractTypeChecker.equalTypes( return AbstractTypeChecker.equalTypes(
@@ -31,7 +31,7 @@ internal class KtFirSubtypingComponent(
} }
override fun isSubTypeOf(subType: KtType, superType: KtType): Boolean = withValidityAssertion { override fun isSubTypeOf(subType: KtType, superType: KtType): Boolean = withValidityAssertion {
superType.assertIsValid() superType.assertIsValidAndAccessible()
check(subType is KtFirType) check(subType is KtFirType)
check(superType is KtFirType) check(superType is KtFirType)
return AbstractTypeChecker.isSubtypeOf( return AbstractTypeChecker.isSubtypeOf(
@@ -9,11 +9,10 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase 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.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.fir.low.level.api.api.withFirDeclaration
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner 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 import java.lang.ref.WeakReference
internal class FirRefWithValidityCheck<out D : FirDeclaration>(fir: D, resolveState: FirModuleResolveState, val token: ValidityToken) { 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 firWeakRef.get() == null && resolveStateWeakRef.get() == null
inline fun <R> withFir(phase: FirResolvePhase = FirResolvePhase.RAW_FIR, crossinline action: (fir: D) -> R): R { inline fun <R> withFir(phase: FirResolvePhase = FirResolvePhase.RAW_FIR, crossinline action: (fir: D) -> R): R {
token.assertIsValid() token.assertIsValidAndAccessible()
val fir = firWeakRef.get() val fir = firWeakRef.get()
?: throw EntityWasGarbageCollectedException("FirElement") ?: throw EntityWasGarbageCollectedException("FirElement")
val resolveState = resolveStateWeakRef.get() val resolveState = resolveStateWeakRef.get()
@@ -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.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner 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.properties.ReadOnlyProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@@ -22,7 +22,7 @@ class ValidityAwareCachedValue<T>(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Any, property: KProperty<*>): T { override fun getValue(thisRef: Any, property: KProperty<*>): T {
token.assertIsValid() token.assertIsValidAndAccessible()
return lazyValue.value return lazyValue.value
} }
} }