Force resolve check for dispatch thread instead of isWriteAccessAllowed
isWriteAccessAllowed = dispatch thread + write lock
This commit is contained in:
+32
-17
@@ -20,6 +20,18 @@ fun <T> allowResolveInWriteAction(runnable: () -> T): T {
|
|||||||
return ResolveInWriteActionManager.runWithResolveAllowedInWriteAction(runnable)
|
return ResolveInWriteActionManager.runWithResolveAllowedInWriteAction(runnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary allow resolve in write action.
|
||||||
|
*
|
||||||
|
* All resolve should be banned from dispatch thread. This method should be carefully used for the transition
|
||||||
|
* period at the places where developer expects everything is already cached.
|
||||||
|
*
|
||||||
|
* (A better check is needed instead that would assert no resolve result are obtained outside of caches.)
|
||||||
|
*/
|
||||||
|
fun <T> allowResolveExpectedToBeCached(runnable: () -> T): T {
|
||||||
|
return ResolveInWriteActionManager.runWithResolveAllowedInWriteAction(runnable)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force resolve check in tests.
|
* Force resolve check in tests.
|
||||||
*/
|
*/
|
||||||
@@ -35,21 +47,23 @@ class ResolveInWriteActionException(message: String? = null) : IllegalThreadStat
|
|||||||
internal object ResolveInWriteActionManager {
|
internal object ResolveInWriteActionManager {
|
||||||
private val LOG = Logger.getInstance(ResolveInWriteActionManager::class.java)
|
private val LOG = Logger.getInstance(ResolveInWriteActionManager::class.java)
|
||||||
|
|
||||||
private val isResolveInWriteActionAllowed: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
// Should be accessed only from dispatch thread
|
||||||
private val isForceCheckInTests: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
private var isResolveAllowed: Boolean = false
|
||||||
private val errorHandler: ThreadLocal<(() -> Unit)?> = ThreadLocal.withInitial { null }
|
private var isForceCheckInTests: Boolean = false
|
||||||
|
private var errorHandler: (() -> Unit)? = null
|
||||||
|
|
||||||
fun assertNoResolveUnderWriteAction() {
|
fun assertNoResolveUnderWriteAction() {
|
||||||
if (isResolveInWriteActionAllowed.get()) return
|
|
||||||
|
|
||||||
val application = ApplicationManager.getApplication() ?: return
|
val application = ApplicationManager.getApplication() ?: return
|
||||||
|
if (!application.isDispatchThread) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!application.isWriteAccessAllowed) return
|
if (isResolveAllowed) return
|
||||||
|
|
||||||
if (application.isUnitTestMode) {
|
if (application.isUnitTestMode) {
|
||||||
if (!isForceCheckInTests.get()) return
|
if (!isForceCheckInTests) return
|
||||||
|
|
||||||
val handler = errorHandler.get()
|
val handler = errorHandler
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
handler()
|
handler()
|
||||||
return
|
return
|
||||||
@@ -66,26 +80,27 @@ internal object ResolveInWriteActionManager {
|
|||||||
|
|
||||||
internal inline fun <T> runWithResolveAllowedInWriteAction(runnable: () -> T): T {
|
internal inline fun <T> runWithResolveAllowedInWriteAction(runnable: () -> T): T {
|
||||||
val wasSet =
|
val wasSet =
|
||||||
if (ApplicationManager.getApplication()?.isWriteAccessAllowed == true && isResolveInWriteActionAllowed.get() == false) {
|
if (ApplicationManager.getApplication()?.isDispatchThread == true && !isResolveAllowed) {
|
||||||
isResolveInWriteActionAllowed.set(true)
|
isResolveAllowed = true
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return runnable()
|
return runnable()
|
||||||
} finally {
|
} finally {
|
||||||
if (wasSet) {
|
if (wasSet) {
|
||||||
isResolveInWriteActionAllowed.set(false)
|
isResolveAllowed = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T> runWithForceResolveAllowedCheckInTests(errorHandler: (() -> Unit)?, runnable: () -> T): T {
|
internal fun <T> runWithForceResolveAllowedCheckInTests(errorHandler: (() -> Unit)?, runnable: () -> T): T {
|
||||||
val wasSet = if (isForceCheckInTests.get() == false) {
|
ApplicationManager.getApplication()?.assertIsDispatchThread()
|
||||||
isForceCheckInTests.set(true)
|
|
||||||
this.errorHandler.set(errorHandler)
|
val wasSet = if (!isForceCheckInTests) {
|
||||||
|
isForceCheckInTests = true
|
||||||
|
this.errorHandler = errorHandler
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@@ -95,8 +110,8 @@ internal object ResolveInWriteActionManager {
|
|||||||
return runnable()
|
return runnable()
|
||||||
} finally {
|
} finally {
|
||||||
if (wasSet) {
|
if (wasSet) {
|
||||||
isForceCheckInTests.set(false)
|
isForceCheckInTests = false
|
||||||
this.errorHandler.set(null)
|
this.errorHandler = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.allowResolveExpectedToBeCached
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : QuickFixActionBase<T>(element) {
|
abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : QuickFixActionBase<T>(element) {
|
||||||
@@ -17,7 +18,11 @@ abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : QuickFixAc
|
|||||||
|
|
||||||
override fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
override fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
||||||
val ktFile = file as? KtFile ?: return false
|
val ktFile = file as? KtFile ?: return false
|
||||||
return isAvailable(project, editor, ktFile)
|
return allowResolveExpectedToBeCached {
|
||||||
|
// Quick fixes availability is checked in UI thread but only after background highlighting pass is finished
|
||||||
|
// so we are expecting that every relevant results are already cached.
|
||||||
|
isAvailable(project, editor, ktFile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
final override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
||||||
|
|||||||
+14
-5
@@ -1,19 +1,22 @@
|
|||||||
# Actions that are allowed to resolve in write action. Normally this set shouldn't be extended and eventually should be dropped.
|
# Actions that are allowed to resolve in write action. Normally this set shouldn't be extended and eventually should be dropped.
|
||||||
# Please consider rewriting a quick-fix and remove resolve from it before adding a new entry to this list.
|
# Please consider rewriting a quick-fix and remove resolve from it before adding a new entry to this list.
|
||||||
|
|
||||||
|
org.jetbrains.kotlin.idea.core.overrideImplement.ImplementAsConstructorParameter
|
||||||
org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
|
org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
|
||||||
|
org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection$DeleteRedundantExtensionAction
|
||||||
org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection$HighPriorityIntentionBasedQuickFix
|
org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection$HighPriorityIntentionBasedQuickFix
|
||||||
org.jetbrains.kotlin.idea.inspections.KotlinUnusedImportInspection$OptimizeImportsQuickFix
|
org.jetbrains.kotlin.idea.inspections.KotlinUnusedImportInspection$OptimizeImportsQuickFix
|
||||||
|
org.jetbrains.kotlin.idea.inspections.migration.ObsoleteCoroutineUsageFix
|
||||||
org.jetbrains.kotlin.idea.inspections.SafeDeleteFix
|
org.jetbrains.kotlin.idea.inspections.SafeDeleteFix
|
||||||
org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection$ChangeTypeToMutableFix
|
org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection$ChangeTypeToMutableFix
|
||||||
org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection$JoinWithInitializerFix
|
org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection$JoinWithInitializerFix
|
||||||
org.jetbrains.kotlin.idea.inspections.UnusedReceiverParameterInspection$RemoveReceiverFix
|
org.jetbrains.kotlin.idea.inspections.UnusedReceiverParameterInspection$RemoveReceiverFix
|
||||||
org.jetbrains.kotlin.idea.inspections.migration.ObsoleteCoroutineUsageFix
|
|
||||||
org.jetbrains.kotlin.idea.intentions.AddPropertyAccessorsIntention
|
org.jetbrains.kotlin.idea.intentions.AddPropertyAccessorsIntention
|
||||||
org.jetbrains.kotlin.idea.intentions.AddPropertyGetterIntention
|
org.jetbrains.kotlin.idea.intentions.AddPropertyGetterIntention
|
||||||
org.jetbrains.kotlin.idea.intentions.AddPropertySetterIntention
|
org.jetbrains.kotlin.idea.intentions.AddPropertySetterIntention
|
||||||
org.jetbrains.kotlin.idea.intentions.ConvertPropertyInitializerToGetterIntention
|
org.jetbrains.kotlin.idea.intentions.ConvertPropertyInitializerToGetterIntention
|
||||||
org.jetbrains.kotlin.idea.intentions.CreateKotlinSubClassIntention
|
org.jetbrains.kotlin.idea.intentions.CreateKotlinSubClassIntention
|
||||||
|
org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention
|
||||||
org.jetbrains.kotlin.idea.intentions.MovePropertyToConstructorIntention
|
org.jetbrains.kotlin.idea.intentions.MovePropertyToConstructorIntention
|
||||||
org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
|
org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
|
||||||
org.jetbrains.kotlin.idea.quickfix.AddAnnotationTargetFix
|
org.jetbrains.kotlin.idea.quickfix.AddAnnotationTargetFix
|
||||||
@@ -26,9 +29,14 @@ org.jetbrains.kotlin.idea.quickfix.AddStartProjectionsForInnerClass
|
|||||||
org.jetbrains.kotlin.idea.quickfix.AddWhenRemainingBranchesFix
|
org.jetbrains.kotlin.idea.quickfix.AddWhenRemainingBranchesFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.AssignToPropertyFix
|
org.jetbrains.kotlin.idea.quickfix.AssignToPropertyFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ChangeAccessorTypeFix
|
org.jetbrains.kotlin.idea.quickfix.ChangeAccessorTypeFix
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix$ForCalled
|
||||||
org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix$ForEnclosing
|
org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix$ForEnclosing
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix$ForOverridden
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.ChangeFunctionLiteralReturnTypeFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ChangeParameterTypeFix
|
org.jetbrains.kotlin.idea.quickfix.ChangeParameterTypeFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ChangeToMutableCollectionFix
|
org.jetbrains.kotlin.idea.quickfix.ChangeToMutableCollectionFix
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.ChangeVariableTypeFix
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.ChangeVariableTypeFix$ForOverridden
|
||||||
org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToInternalFix
|
org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToInternalFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToPrivateFix
|
org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToPrivateFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToProtectedFix
|
org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToProtectedFix
|
||||||
@@ -36,6 +44,8 @@ org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToPublicFix
|
|||||||
org.jetbrains.kotlin.idea.quickfix.ConvertExtensionPropertyInitializerToGetterFix
|
org.jetbrains.kotlin.idea.quickfix.ConvertExtensionPropertyInitializerToGetterFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ConvertMemberToExtensionFix
|
org.jetbrains.kotlin.idea.quickfix.ConvertMemberToExtensionFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ConvertToAnonymousObjectFix
|
org.jetbrains.kotlin.idea.quickfix.ConvertToAnonymousObjectFix
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateLocalVariableActionFactory$createAction$2
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.DeprecatedJavaAnnotationFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ImportFix
|
org.jetbrains.kotlin.idea.quickfix.ImportFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.InitializePropertyQuickFixFactory$AddInitializerFix
|
org.jetbrains.kotlin.idea.quickfix.InitializePropertyQuickFixFactory$AddInitializerFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.InitializePropertyQuickFixFactory$InitializeWithConstructorParameter
|
org.jetbrains.kotlin.idea.quickfix.InitializePropertyQuickFixFactory$InitializeWithConstructorParameter
|
||||||
@@ -45,6 +55,7 @@ org.jetbrains.kotlin.idea.quickfix.InsertDelegationCallQuickfix
|
|||||||
org.jetbrains.kotlin.idea.quickfix.KotlinSuppressIntentionAction
|
org.jetbrains.kotlin.idea.quickfix.KotlinSuppressIntentionAction
|
||||||
org.jetbrains.kotlin.idea.quickfix.LetImplementInterfaceFix
|
org.jetbrains.kotlin.idea.quickfix.LetImplementInterfaceFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.LowPriorityQuickFixWithDelegateFactory
|
org.jetbrains.kotlin.idea.quickfix.LowPriorityQuickFixWithDelegateFactory
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.migration.MigrateExternalExtensionFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory
|
org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory
|
||||||
org.jetbrains.kotlin.idea.quickfix.RemoveNameFromFunctionExpressionFix
|
org.jetbrains.kotlin.idea.quickfix.RemoveNameFromFunctionExpressionFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.RemovePartsFromPropertyFix
|
org.jetbrains.kotlin.idea.quickfix.RemovePartsFromPropertyFix
|
||||||
@@ -55,6 +66,8 @@ org.jetbrains.kotlin.idea.quickfix.RenameModToRemFix
|
|||||||
org.jetbrains.kotlin.idea.quickfix.RenameParameterToMatchOverriddenMethodFix
|
org.jetbrains.kotlin.idea.quickfix.RenameParameterToMatchOverriddenMethodFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.RenameUnresolvedReferenceFix
|
org.jetbrains.kotlin.idea.quickfix.RenameUnresolvedReferenceFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.ReplaceInfixOrOperatorCallFix
|
org.jetbrains.kotlin.idea.quickfix.ReplaceInfixOrOperatorCallFix
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix
|
||||||
|
org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageInWholeProjectFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.RestrictedRetentionForExpressionAnnotationFactory$AddSourceRetentionFix
|
org.jetbrains.kotlin.idea.quickfix.RestrictedRetentionForExpressionAnnotationFactory$AddSourceRetentionFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.SimplifyComparisonFix
|
org.jetbrains.kotlin.idea.quickfix.SimplifyComparisonFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.SmartCastImpossibleInIfThenFactory$createQuickFix$1
|
org.jetbrains.kotlin.idea.quickfix.SmartCastImpossibleInIfThenFactory$createQuickFix$1
|
||||||
@@ -62,7 +75,3 @@ org.jetbrains.kotlin.idea.quickfix.SpecifyOverrideExplicitlyFix
|
|||||||
org.jetbrains.kotlin.idea.quickfix.SpecifyTypeExplicitlyFix
|
org.jetbrains.kotlin.idea.quickfix.SpecifyTypeExplicitlyFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.SuperClassNotInitialized$AddParenthesisFix
|
org.jetbrains.kotlin.idea.quickfix.SuperClassNotInitialized$AddParenthesisFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.WrapWithSafeLetCallFix
|
org.jetbrains.kotlin.idea.quickfix.WrapWithSafeLetCallFix
|
||||||
org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateLocalVariableActionFactory$createAction$2
|
|
||||||
org.jetbrains.kotlin.idea.quickfix.migration.MigrateExternalExtensionFix
|
|
||||||
org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix
|
|
||||||
org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageInWholeProjectFix
|
|
||||||
Reference in New Issue
Block a user