From 1060a242213e53c1b397953a4ee35b88a8637689 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 15 Mar 2019 15:10:38 +0300 Subject: [PATCH] Don't allow to resolve anything within write action --- .../resolve/ModuleResolutionFacadeImpl.kt | 16 +++- .../resolve/ResolveInWriteActionManager.kt | 78 +++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolveInWriteActionManager.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleResolutionFacadeImpl.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleResolutionFacadeImpl.kt index 7b919e6a9d3..885d23dbe3f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleResolutionFacadeImpl.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleResolutionFacadeImpl.kt @@ -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, 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): AnalysisResult = - projectFacade.getAnalysisResultsForElements(elements) + override fun analyzeWithAllCompilerChecks(elements: Collection): 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.resolveToDescriptor(declaration) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolveInWriteActionManager.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolveInWriteActionManager.kt new file mode 100644 index 00000000000..e4a7f3b6a31 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolveInWriteActionManager.kt @@ -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 allowResolveInWriteAction(runnable: () -> T): T { + return ResolveInWriteActionManager.runWithResolveAllowedInWriteAction(runnable) +} + +internal object ResolveInWriteActionManager { + private val LOG = Logger.getInstance(ResolveInWriteActionManager::class.java) + + private val isResolveInWriteActionAllowed: ThreadLocal = 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 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) + } + } + } +} \ No newline at end of file