Don't allow to resolve anything within write action

This commit is contained in:
Nikolay Krasko
2019-03-15 15:10:38 +03:00
parent 7e4688da93
commit 1060a24221
2 changed files with 91 additions and 3 deletions
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.AnalysisResult
@@ -24,6 +26,7 @@ import org.jetbrains.kotlin.container.getService
import org.jetbrains.kotlin.container.tryGetService
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
@@ -54,20 +57,27 @@ internal class ModuleResolutionFacadeImpl(
}
override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext {
ResolveInWriteActionManager.assertNoResolveUnderWriteAction()
if (elements.isEmpty()) return BindingContext.EMPTY
val resolveElementCache = getFrontendService(elements.first(), ResolveElementCache::class.java)
return resolveElementCache.resolveToElements(elements, bodyResolveMode)
}
override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult =
projectFacade.getAnalysisResultsForElements(elements)
override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
ResolveInWriteActionManager.assertNoResolveUnderWriteAction()
return projectFacade.getAnalysisResultsForElements(elements)
}
override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor {
return if (KtPsiUtil.isLocal(declaration)) {
val bindingContext = analyze(declaration, bodyResolveMode)
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration]
?: getFrontendService(moduleInfo, AbsentDescriptorHandler::class.java).diagnoseDescriptorNotFound(declaration)
?: getFrontendService(moduleInfo, AbsentDescriptorHandler::class.java).diagnoseDescriptorNotFound(declaration)
} else {
ResolveInWriteActionManager.assertNoResolveUnderWriteAction()
val resolveSession = projectFacade.resolverForElement(declaration).componentProvider.get<ResolveSession>()
resolveSession.resolveToDescriptor(declaration)
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.idea.caches.resolve
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.ControlFlowException
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.IndexNotReadyException
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiNamedElement
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
/**
* Temporary allow resolve in write action.
*
* All resolve should be banned from write action. This method is needed for the transition period to document
* places that are not fixed yet.
*/
fun <T> allowResolveInWriteAction(runnable: () -> T): T {
return ResolveInWriteActionManager.runWithResolveAllowedInWriteAction(runnable)
}
internal object ResolveInWriteActionManager {
private val LOG = Logger.getInstance(ResolveInWriteActionManager::class.java)
private val isResolveInWriteActionAllowed: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
fun assertNoResolveUnderWriteAction() {
if (isResolveInWriteActionAllowed.get()) return
val application = ApplicationManager.getApplication() ?: return
if (!application.isWriteAccessAllowed) return
if (application.isUnitTestMode) return
if (application.isInternal && !KotlinPluginUtil.isSnapshotVersion()) return
LOG.error("Resolve is not allowed under the write action!")
}
inline fun <T> runWithResolveAllowedInWriteAction(runnable: () -> T): T {
val wasSet =
if (ApplicationManager.getApplication()?.isWriteAccessAllowed == true && isResolveInWriteActionAllowed.get() == false) {
isResolveInWriteActionAllowed.set(true)
true
} else {
false
}
try {
return runnable()
} finally {
if (wasSet) {
isResolveInWriteActionAllowed.set(false)
}
}
}
}