mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
Rename ReadOrWriteActionInServiceInitializationInspection to PotentialDeadlockInServiceInitializationInspection
GitOrigin-RevId: bcf700c2c1a8c7afa9d3d257f010413e6a0e95fb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1897480db8
commit
2beb8da5c2
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports read and write actions run from the scope of service initialization:
|
||||
Reports read/write actions and <code>invokeAndWait</code> called from the scope of service initialization:
|
||||
<ul>
|
||||
<li>service constructors and initialization blocks (including static)</li>
|
||||
<li>service companion object's initialization blocks (Kotlin)</li>
|
||||
@@ -13,7 +13,7 @@ Reports read and write actions run from the scope of service initialization:
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>Running a read or write action during service initialization may cause deadlocks.</p>
|
||||
<p>Running a read/write action or calling <code>invokeAndWait</code> during service initialization may cause deadlocks.</p>
|
||||
|
||||
<p><b>Examples:</b></p>
|
||||
<p>Kotlin:</p>
|
||||
@@ -459,14 +459,14 @@
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.ListenerImplementationMustNotBeDisposableInspection"/>
|
||||
|
||||
<localInspection language="UAST"
|
||||
key="inspection.read.or.write.action.during.service.init.display.name"
|
||||
shortName="ReadOrWriteActionInServiceInitialization"
|
||||
key="inspection.potential.deadlock.during.service.init.display.name"
|
||||
shortName="PotentialDeadlockInServiceInitialization"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupPathKey="inspections.group.path"
|
||||
groupKey="inspections.group.code"
|
||||
enabledByDefault="false"
|
||||
level="ERROR"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.ReadOrWriteActionInServiceInitializationInspection"/>
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.PotentialDeadlockInServiceInitializationInspection"/>
|
||||
|
||||
<moduleConfigurationEditorProvider implementation="org.jetbrains.idea.devkit.module.PluginModuleEditorsProvider"/>
|
||||
<implicitUsageProvider implementation="org.jetbrains.idea.devkit.inspections.DevKitImplicitUsageProvider"/>
|
||||
|
||||
@@ -683,16 +683,16 @@ inspections.static.initialization.in.extensions.message=Extension point implemen
|
||||
inspections.listener.implementation.must.not.be.disposable.name=Listener implementation implements 'Disposable'
|
||||
inspections.listener.implementation.must.not.implement.disposable=Listener implementation must not implement 'Disposable'
|
||||
|
||||
inspection.read.or.write.action.during.service.init.display.name=Read or Write Action run during service initialization
|
||||
inspection.read.or.write.action.during.service.init.message.read=Do not run read actions during service initialization{0}
|
||||
inspection.read.or.write.action.during.service.init.message.write=Do not run write actions during service initialization{0}
|
||||
inspection.read.or.write.action.during.service.init.message.invoke.and.wait=Do not run ''invokeAndWait'' during service initialization{0}
|
||||
inspection.read.or.write.action.during.service.init.message.context=\ ({0} is called in {1})
|
||||
inspection.read.or.write.action.during.service.init.message.context.field=''{0}'' field initializer
|
||||
inspection.read.or.write.action.during.service.init.message.context.method=''{0}'' method
|
||||
inspection.read.or.write.action.during.service.init.message.context.constructor=''{0}'' constructor or init block
|
||||
inspection.read.or.write.action.during.service.init.message.context.static.initializer=static initialization block
|
||||
inspection.read.or.write.action.during.service.init.message.context.instance.initializer=instance initialization block
|
||||
inspection.potential.deadlock.during.service.init.display.name=Read or Write Action run during service initialization
|
||||
inspection.potential.deadlock.during.service.init.message.read=Do not run read actions during service initialization{0}
|
||||
inspection.potential.deadlock.during.service.init.message.write=Do not run write actions during service initialization{0}
|
||||
inspection.potential.deadlock.during.service.init.message.invoke.and.wait=Do not run ''invokeAndWait'' during service initialization{0}
|
||||
inspection.potential.deadlock.during.service.init.message.context=\ ({0} is called in {1})
|
||||
inspection.potential.deadlock.during.service.init.message.context.field=''{0}'' field initializer
|
||||
inspection.potential.deadlock.during.service.init.message.context.method=''{0}'' method
|
||||
inspection.potential.deadlock.during.service.init.message.context.constructor=''{0}'' constructor or init block
|
||||
inspection.potential.deadlock.during.service.init.message.context.static.initializer=static initialization block
|
||||
inspection.potential.deadlock.during.service.init.message.context.instance.initializer=instance initialization block
|
||||
|
||||
inspection.plugin.xml.registration.check.display.name=Plugin.xml registration check
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ private val PERSISTENT_STATE_COMPONENT_INIT_METHOD_NAMES = arrayOf(
|
||||
private const val VISIT_CHILDREN = false
|
||||
private const val SKIP_CHILDREN = true
|
||||
|
||||
internal class ReadOrWriteActionInServiceInitializationInspection : DevKitUastInspectionBase() {
|
||||
internal class PotentialDeadlockInServiceInitializationInspection : DevKitUastInspectionBase() {
|
||||
|
||||
override fun buildInternalVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
return UastHintedVisitorAdapter.create(
|
||||
@@ -71,12 +71,12 @@ internal class ReadOrWriteActionInServiceInitializationInspection : DevKitUastIn
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCalledDuringServiceInitialization(readOrWriteActionCall: UCallExpression, callContextHolder: CallContextHolder): Boolean {
|
||||
val uClass = readOrWriteActionCall.getContainingNonCompanionObjectClass() ?: return false
|
||||
private fun isCalledDuringServiceInitialization(forbiddenCall: UCallExpression, callContextHolder: CallContextHolder): Boolean {
|
||||
val uClass = forbiddenCall.getContainingNonCompanionObjectClass() ?: return false
|
||||
return isService(uClass) &&
|
||||
(isCalledDuringInit(readOrWriteActionCall) ||
|
||||
isInMethodCalledDuringInit(uClass, readOrWriteActionCall, callContextHolder) ||
|
||||
isCalledDuringPersistentStateComponentInit(uClass, readOrWriteActionCall, callContextHolder))
|
||||
(isCalledDuringInit(forbiddenCall) ||
|
||||
isInMethodCalledDuringInit(uClass, forbiddenCall, callContextHolder) ||
|
||||
isCalledDuringPersistentStateComponentInit(uClass, forbiddenCall, callContextHolder))
|
||||
}
|
||||
|
||||
private fun UElement.getContainingNonCompanionObjectClass(): UClass? {
|
||||
@@ -84,41 +84,37 @@ internal class ReadOrWriteActionInServiceInitializationInspection : DevKitUastIn
|
||||
return if (uClass.javaPsi.name == "Companion") uClass.getParentOfType<UClass>() else uClass
|
||||
}
|
||||
|
||||
private fun isCalledDuringInit(readOrWriteActionCall: UCallExpression): Boolean {
|
||||
return !isCalledInAnonymousClassOrLambda(readOrWriteActionCall) &&
|
||||
(isCalledInConstructor(readOrWriteActionCall) ||
|
||||
isCalledInInitBlock(readOrWriteActionCall) ||
|
||||
isCalledInFieldAssignment(readOrWriteActionCall))
|
||||
private fun isCalledDuringInit(forbiddenCall: UCallExpression): Boolean {
|
||||
return !isCalledInAnonymousClassOrLambda(forbiddenCall) &&
|
||||
(isCalledInConstructor(forbiddenCall) ||
|
||||
isCalledInInitBlock(forbiddenCall) ||
|
||||
isCalledInFieldAssignment(forbiddenCall))
|
||||
}
|
||||
|
||||
private fun isCalledInAnonymousClassOrLambda(readOrWriteActionCall: UCallExpression): Boolean {
|
||||
private fun isCalledInAnonymousClassOrLambda(forbiddenActionCall: UCallExpression): Boolean {
|
||||
// do not report calls in listener, alarm, etc. registration
|
||||
return readOrWriteActionCall.getParentOfType<UObjectLiteralExpression>() != null ||
|
||||
readOrWriteActionCall.getParentOfType<ULambdaExpression>() != null
|
||||
return forbiddenActionCall.getParentOfType<UObjectLiteralExpression>() != null ||
|
||||
forbiddenActionCall.getParentOfType<ULambdaExpression>() != null
|
||||
}
|
||||
|
||||
private fun isCalledInConstructor(readOrWriteActionCall: UCallExpression): Boolean {
|
||||
return readOrWriteActionCall.getContainingUMethod()?.isConstructor == true
|
||||
private fun isCalledInConstructor(forbiddenCall: UCallExpression): Boolean {
|
||||
return forbiddenCall.getContainingUMethod()?.isConstructor == true
|
||||
}
|
||||
|
||||
private fun isCalledInInitBlock(readOrWriteActionCall: UCallExpression): Boolean {
|
||||
return readOrWriteActionCall.getParentOfType<UClassInitializer>() != null
|
||||
private fun isCalledInInitBlock(forbiddenCall: UCallExpression): Boolean {
|
||||
return forbiddenCall.getParentOfType<UClassInitializer>() != null
|
||||
}
|
||||
|
||||
private fun isCalledInFieldAssignment(readOrWriteActionCall: UCallExpression): Boolean {
|
||||
return readOrWriteActionCall.getParentOfType<UField>() != null
|
||||
private fun isCalledInFieldAssignment(forbiddenCall: UCallExpression): Boolean {
|
||||
return forbiddenCall.getParentOfType<UField>() != null
|
||||
}
|
||||
|
||||
private fun isInMethodCalledDuringInit(
|
||||
serviceClass: UClass,
|
||||
readOrWriteActionCall: UCallExpression,
|
||||
callContextHolder: CallContextHolder,
|
||||
): Boolean {
|
||||
if (isCalledInAnonymousClassOrLambda(readOrWriteActionCall)) return false
|
||||
private fun isInMethodCalledDuringInit(serviceClass: UClass, forbiddenCall: UCallExpression, callContextHolder: CallContextHolder): Boolean {
|
||||
if (isCalledInAnonymousClassOrLambda(forbiddenCall)) return false
|
||||
val companionObject = serviceClass.innerClasses.firstOrNull { it.javaPsi.name == "Companion" }
|
||||
val initializationElements: List<UElement> = serviceClass.getInitializationElements() +
|
||||
(companionObject?.getInitializationElements() ?: emptyList())
|
||||
val containingMethod = readOrWriteActionCall.getContainingUMethod() ?: return false
|
||||
val containingMethod = forbiddenCall.getContainingUMethod() ?: return false
|
||||
return containingMethod.isCalledInAnyOf(initializationElements, callContextHolder)
|
||||
}
|
||||
|
||||
@@ -154,30 +150,30 @@ internal class ReadOrWriteActionInServiceInitializationInspection : DevKitUastIn
|
||||
private fun getContextText(calledMethod: UMethod, caller: UElement): @Nls String? {
|
||||
val callerName = when (caller) {
|
||||
is UMethod ->
|
||||
if (caller.isConstructor) message("inspection.read.or.write.action.during.service.init.message.context.constructor", caller.name)
|
||||
else message("inspection.read.or.write.action.during.service.init.message.context.method", caller.name)
|
||||
if (caller.isConstructor) message("inspection.potential.deadlock.during.service.init.message.context.constructor", caller.name)
|
||||
else message("inspection.potential.deadlock.during.service.init.message.context.method", caller.name)
|
||||
|
||||
is UField ->
|
||||
message("inspection.read.or.write.action.during.service.init.message.context.field", @Suppress("UElementAsPsi") caller.name)
|
||||
message("inspection.potential.deadlock.during.service.init.message.context.field", @Suppress("UElementAsPsi") caller.name)
|
||||
|
||||
is UClassInitializer ->
|
||||
// there is no API in UAST to distinguish companion object context, but we can live with it
|
||||
if (caller.isStatic) message("inspection.read.or.write.action.during.service.init.message.context.static.initializer")
|
||||
else message("inspection.read.or.write.action.during.service.init.message.context.instance.initializer")
|
||||
if (caller.isStatic) message("inspection.potential.deadlock.during.service.init.message.context.static.initializer")
|
||||
else message("inspection.potential.deadlock.during.service.init.message.context.instance.initializer")
|
||||
|
||||
else -> return null
|
||||
}
|
||||
return message("inspection.read.or.write.action.during.service.init.message.context", "'${calledMethod.name}'", callerName)
|
||||
return message("inspection.potential.deadlock.during.service.init.message.context", "'${calledMethod.name}'", callerName)
|
||||
}
|
||||
|
||||
private fun isCalledDuringPersistentStateComponentInit(
|
||||
serviceClass: UClass,
|
||||
readOrWriteActionCall: UCallExpression,
|
||||
forbiddenCall: UCallExpression,
|
||||
callContextHolder: CallContextHolder,
|
||||
): Boolean {
|
||||
return isPersistentStateComponent(serviceClass) &&
|
||||
(isCalledDuringPersistentStateComponentInitMethods(readOrWriteActionCall) ||
|
||||
isInMethodCalledDuringPersistentStateComponentInit(serviceClass, readOrWriteActionCall, callContextHolder))
|
||||
(isCalledDuringPersistentStateComponentInitMethods(forbiddenCall) ||
|
||||
isInMethodCalledDuringPersistentStateComponentInit(serviceClass, forbiddenCall, callContextHolder))
|
||||
}
|
||||
|
||||
private fun isPersistentStateComponent(serviceClass: UClass): Boolean {
|
||||
@@ -185,19 +181,19 @@ internal class ReadOrWriteActionInServiceInitializationInspection : DevKitUastIn
|
||||
return JvmInheritanceUtil.isInheritor(servicePsiClass, PersistentStateComponent::class.java.canonicalName)
|
||||
}
|
||||
|
||||
private fun isCalledDuringPersistentStateComponentInitMethods(readOrWriteActionCall: UCallExpression): Boolean {
|
||||
if (isCalledInAnonymousClassOrLambda(readOrWriteActionCall)) return false
|
||||
val containingMethod = readOrWriteActionCall.getContainingUMethod() ?: return false
|
||||
private fun isCalledDuringPersistentStateComponentInitMethods(forbiddenCall: UCallExpression): Boolean {
|
||||
if (isCalledInAnonymousClassOrLambda(forbiddenCall)) return false
|
||||
val containingMethod = forbiddenCall.getContainingUMethod() ?: return false
|
||||
return PERSISTENT_STATE_COMPONENT_INIT_METHOD_NAMES.contains(containingMethod.name)
|
||||
}
|
||||
|
||||
private fun isInMethodCalledDuringPersistentStateComponentInit(
|
||||
serviceClass: UClass, readOrWriteActionCall: UCallExpression,
|
||||
serviceClass: UClass, forbiddenCall: UCallExpression,
|
||||
callContextHolder: CallContextHolder,
|
||||
): Boolean {
|
||||
if (isCalledInAnonymousClassOrLambda(readOrWriteActionCall)) return false
|
||||
if (isCalledInAnonymousClassOrLambda(forbiddenCall)) return false
|
||||
val lifecycleMethods: List<UMethod> = serviceClass.methods.filter { PERSISTENT_STATE_COMPONENT_INIT_METHOD_NAMES.contains(it.name) }
|
||||
val containingMethod = readOrWriteActionCall.getContainingUMethod() ?: return false
|
||||
val containingMethod = forbiddenCall.getContainingUMethod() ?: return false
|
||||
return containingMethod.isCalledInAnyOf(lifecycleMethods, callContextHolder)
|
||||
}
|
||||
|
||||
@@ -210,9 +206,9 @@ internal class ReadOrWriteActionInServiceInitializationInspection : DevKitUastIn
|
||||
val anchor = uCallExpression.methodIdentifier?.sourcePsi ?: return
|
||||
val callContext = callContextHolder.value ?: ""
|
||||
val message = when (actionType) {
|
||||
CallType.READ -> message("inspection.read.or.write.action.during.service.init.message.read", callContext)
|
||||
CallType.WRITE -> message("inspection.read.or.write.action.during.service.init.message.write", callContext)
|
||||
CallType.INVOKE_AND_WAIT -> message("inspection.read.or.write.action.during.service.init.message.invoke.and.wait", callContext)
|
||||
CallType.READ -> message("inspection.potential.deadlock.during.service.init.message.read", callContext)
|
||||
CallType.WRITE -> message("inspection.potential.deadlock.during.service.init.message.write", callContext)
|
||||
CallType.INVOKE_AND_WAIT -> message("inspection.potential.deadlock.during.service.init.message.invoke.and.wait", callContext)
|
||||
}
|
||||
holder.registerProblem(anchor, message)
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase
|
||||
"ThreadingConcurrency",
|
||||
"CallingMethodShouldBeRequiresBlockingContext",
|
||||
"IncorrectProcessCanceledExceptionHandling",
|
||||
"ReadOrWriteActionInServiceInitialization"
|
||||
"PotentialDeadlockInServiceInitialization"
|
||||
).sorted().toList();
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.inspections
|
||||
|
||||
class ReadOrWriteActionInServiceInitializationInspectionTest : ReadOrWriteActionInServiceInitializationInspectionTestBase() {
|
||||
class PotentialDeadlockInServiceInitializationInspectionTest : PotentialDeadlockInServiceInitializationInspectionTestBase() {
|
||||
|
||||
fun `test read and write actions are reported in a light service`() {
|
||||
myFixture.configureByText("TestService.java", getServiceWithReadAndWriteActionsCalledDuringInit(true))
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.kotlin.inspections
|
||||
|
||||
import org.jetbrains.idea.devkit.inspections.ReadOrWriteActionInServiceInitializationInspectionTestBase
|
||||
import org.jetbrains.idea.devkit.inspections.PotentialDeadlockInServiceInitializationInspectionTestBase
|
||||
|
||||
class KtReadOrWriteActionInServiceInitializationInspectionTest : ReadOrWriteActionInServiceInitializationInspectionTestBase() {
|
||||
class KtPotentialDeadlockInServiceInitializationInspectionTest : PotentialDeadlockInServiceInitializationInspectionTestBase() {
|
||||
|
||||
fun `test read and write actions are reported in a light service`() {
|
||||
myFixture.configureByText("TestService.kt", getServiceWithReadAndWriteActionsCalledDuringInit(true))
|
||||
@@ -17,7 +17,7 @@ import com.intellij.util.Alarm
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.idea.devkit.inspections.quickfix.DevKitInspectionFixTestBase
|
||||
|
||||
abstract class ReadOrWriteActionInServiceInitializationInspectionTestBase : DevKitInspectionFixTestBase() {
|
||||
abstract class PotentialDeadlockInServiceInitializationInspectionTestBase : DevKitInspectionFixTestBase() {
|
||||
|
||||
override fun tuneFixture(moduleBuilder: JavaModuleFixtureBuilder<*>) {
|
||||
// too many classes to add manually via addClass, so using the slower libraries approach:
|
||||
@@ -39,7 +39,7 @@ abstract class ReadOrWriteActionInServiceInitializationInspectionTestBase : DevK
|
||||
KotlinTester.configureKotlinStdLib(it)
|
||||
}
|
||||
IndexingTestUtil.waitUntilIndexesAreReady(project)
|
||||
myFixture.enableInspections(ReadOrWriteActionInServiceInitializationInspection())
|
||||
myFixture.enableInspections(PotentialDeadlockInServiceInitializationInspection())
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user