mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
[devkit] IDEA-323204 implement new inspection
GitOrigin-RevId: 379d6e0776d132ee356ba62db23660ca388ea137
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4e97148a7f
commit
4a112d1970
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports listener implementations that implement <code>com.intellij.openapi.Disposable</code>.
|
||||
<p>
|
||||
Listener implementations must be stateless and must not implement life-cycle, including <code>com.intellij.openapi.Disposable</code>.
|
||||
</p>
|
||||
<p>
|
||||
See <a href=https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html>documentation</a> for more information.
|
||||
</p>
|
||||
<p><small>New in 2023.3</small>
|
||||
</body>
|
||||
</html>
|
||||
@@ -404,6 +404,12 @@
|
||||
enabledByDefault="false" level="WARNING"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.StaticInitializationInExtensionsInspection"/>
|
||||
|
||||
<localInspection language="UAST" groupPathKey="inspections.group.path"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
key="inspections.listener.implementation.must.not.be.disposable.name" groupKey="inspections.group.code"
|
||||
enabledByDefault="false" level="ERROR"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.ListenerImplementationMustNotBeDisposableInspection"/>
|
||||
|
||||
|
||||
<moduleConfigurationEditorProvider implementation="org.jetbrains.idea.devkit.module.PluginModuleEditorsProvider"/>
|
||||
<implicitUsageProvider implementation="org.jetbrains.idea.devkit.inspections.DevKitImplicitUsageProvider"/>
|
||||
|
||||
@@ -692,4 +692,7 @@ update.ide.from.sources=Update &IDE from sources
|
||||
update.ide.from.sources.option=from sources
|
||||
|
||||
inspections.static.initialization.in.extensions.name=Static initialization in IDE extensions
|
||||
inspections.static.initialization.in.extensions.message=IDE extensions must not use static initialization
|
||||
inspections.static.initialization.in.extensions.message=IDE extensions must not use static initialization
|
||||
|
||||
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'
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2023 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
|
||||
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.codeInspection.registerUProblem
|
||||
import com.intellij.lang.jvm.JvmClassKind
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.util.InheritanceUtil
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.intellij.uast.UastHintedVisitorAdapter
|
||||
import org.jetbrains.idea.devkit.DevKitBundle
|
||||
import org.jetbrains.idea.devkit.dom.index.IdeaPluginRegistrationIndex
|
||||
import org.jetbrains.idea.devkit.util.PluginRelatedLocatorsUtils
|
||||
import org.jetbrains.uast.UClass
|
||||
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
|
||||
|
||||
internal class ListenerImplementationMustNotBeDisposableInspection : DevKitUastInspectionBase() {
|
||||
|
||||
override fun buildInternalVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
return UastHintedVisitorAdapter.create(
|
||||
holder.file.language,
|
||||
object : AbstractUastNonRecursiveVisitor() {
|
||||
|
||||
override fun visitClass(node: UClass): Boolean {
|
||||
val psiClass = node.javaPsi
|
||||
|
||||
if (psiClass.classKind != JvmClassKind.CLASS ||
|
||||
PsiUtil.isAbstractClass(psiClass) ||
|
||||
PsiUtil.isLocalOrAnonymousClass(psiClass) ||
|
||||
PsiUtil.isInnerClass(psiClass)) return true
|
||||
|
||||
if (!InheritanceUtil.isInheritor(psiClass, Disposable::class.java.canonicalName)) return true
|
||||
|
||||
val isRegisteredListener = !IdeaPluginRegistrationIndex.processListener(
|
||||
holder.project,
|
||||
psiClass,
|
||||
PluginRelatedLocatorsUtils.getCandidatesScope(holder.project),
|
||||
) { false }
|
||||
|
||||
if (!isRegisteredListener) return true
|
||||
|
||||
holder.registerUProblem(
|
||||
node,
|
||||
DevKitBundle.message("inspections.listener.implementation.must.not.implement.disposable")
|
||||
)
|
||||
return true
|
||||
}
|
||||
},
|
||||
arrayOf(UClass::class.java)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import com.intellij.openapi.Disposable;
|
||||
|
||||
class <error descr="Listener implementation must not implement 'Disposable'">MyApplicationListener</error> implements BaseListenerInterface, Disposable { }
|
||||
@@ -0,0 +1,3 @@
|
||||
import com.intellij.openapi.Disposable;
|
||||
|
||||
class <error descr="Listener implementation must not implement 'Disposable'">MyProjectListener</error> implements BaseListenerInterface, Disposable { }
|
||||
@@ -0,0 +1,3 @@
|
||||
import com.intellij.openapi.Disposable;
|
||||
|
||||
class MyClass implements BaseListenerInterface, Disposable { }
|
||||
@@ -0,0 +1 @@
|
||||
class MyApplicationListener implements BaseListenerInterface { }
|
||||
@@ -0,0 +1 @@
|
||||
class MyProjectListener implements BaseListenerInterface { }
|
||||
@@ -0,0 +1,15 @@
|
||||
<idea-plugin>
|
||||
<id>com.intellij</id>
|
||||
|
||||
<applicationListeners>
|
||||
<listener
|
||||
class="MyApplicationListener"
|
||||
topic="BaseListenerInterface"/>
|
||||
</applicationListeners>
|
||||
|
||||
<projectListeners>
|
||||
<listener
|
||||
class="MyProjectListener"
|
||||
topic="BaseListenerInterface"/>
|
||||
</projectListeners>
|
||||
</idea-plugin>
|
||||
@@ -27,7 +27,8 @@ public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase
|
||||
"TokenSetInParserDefinition",
|
||||
"CallingMethodShouldBeRequiresBlockingContext",
|
||||
"IncorrectProcessCanceledExceptionHandling",
|
||||
"StaticInitializationInExtensions");
|
||||
"StaticInitializationInExtensions",
|
||||
"ListenerImplementationMustNotBeDisposable");
|
||||
|
||||
/**
|
||||
* Validates all DevKit inspections that are disabled by default match the expected known set.
|
||||
@@ -36,7 +37,7 @@ public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase
|
||||
List<LocalInspectionEP> devkitInspections = ContainerUtil.filter(LocalInspectionEP.LOCAL_INSPECTION.getExtensionList(), ep -> {
|
||||
return "DevKit".equals(ep.getPluginDescriptor().getPluginId().getIdString());
|
||||
});
|
||||
assertEquals("Mismatch in total inspections, check classpath in test run configuration (intellij.devkit.plugin)", 64,
|
||||
assertEquals("Mismatch in total inspections, check classpath in test run configuration (intellij.devkit.plugin)", 65,
|
||||
devkitInspections.size());
|
||||
|
||||
List<LocalInspectionEP> disabledInspections = ContainerUtil.filter(devkitInspections, ep -> !ep.enabledByDefault);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2000-2023 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
|
||||
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil
|
||||
|
||||
@TestDataPath("/inspections/listenerImplementationMustNotBeDisposable")
|
||||
class ListenerImplementationMustNotBeDisposableInspectionTest : ListenerImplementationMustNotBeDisposableInspectionTestBase() {
|
||||
|
||||
override fun getBasePath() = DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/listenerImplementationMustNotBeDisposable"
|
||||
|
||||
override fun getFileExtension(): String = "java"
|
||||
|
||||
fun testDisposableApplicationListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testDisposableProjectListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testDisposableUnregisteredListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNonDisposableApplicationListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNonDisposableProjectListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import com.intellij.openapi.Disposable
|
||||
|
||||
class <error descr="Listener implementation must not implement 'Disposable'">MyApplicationListener</error> : BaseListenerInterface, Disposable
|
||||
@@ -0,0 +1,3 @@
|
||||
import com.intellij.openapi.Disposable
|
||||
|
||||
class <error descr="Listener implementation must not implement 'Disposable'">MyProjectListener</error> : BaseListenerInterface, Disposable
|
||||
@@ -0,0 +1,3 @@
|
||||
import com.intellij.openapi.Disposable
|
||||
|
||||
class MyClass : BaseListenerInterface, Disposable
|
||||
@@ -0,0 +1 @@
|
||||
class MyApplicationListener : BaseListenerInterface
|
||||
@@ -0,0 +1 @@
|
||||
class MyProjectListener : BaseListenerInterface
|
||||
@@ -0,0 +1,15 @@
|
||||
<idea-plugin>
|
||||
<id>com.intellij</id>
|
||||
|
||||
<applicationListeners>
|
||||
<listener
|
||||
class="MyApplicationListener"
|
||||
topic="BaseListenerInterface"/>
|
||||
</applicationListeners>
|
||||
|
||||
<projectListeners>
|
||||
<listener
|
||||
class="MyProjectListener"
|
||||
topic="BaseListenerInterface"/>
|
||||
</projectListeners>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2000-2023 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 com.intellij.testFramework.TestDataPath
|
||||
import org.jetbrains.idea.devkit.inspections.ListenerImplementationMustNotBeDisposableInspectionTestBase
|
||||
import org.jetbrains.idea.devkit.kotlin.DevkitKtTestsUtil
|
||||
|
||||
@TestDataPath("/inspections/listenerImplementationMustNotBeDisposable")
|
||||
class KtListenerImplementationMustNotBeDisposableInspectionTest : ListenerImplementationMustNotBeDisposableInspectionTestBase() {
|
||||
|
||||
override fun getBasePath() = DevkitKtTestsUtil.TESTDATA_PATH + "inspections/listenerImplementationMustNotBeDisposable"
|
||||
|
||||
override fun getFileExtension(): String = "kt"
|
||||
|
||||
fun testDisposableApplicationListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testDisposableProjectListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testDisposableUnregisteredListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNonDisposableApplicationListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNonDisposableProjectListener() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2000-2023 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
|
||||
|
||||
abstract class ListenerImplementationMustNotBeDisposableInspectionTestBase : PluginModuleTestCase() {
|
||||
|
||||
protected abstract fun getFileExtension(): String
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
addPlatformClasses()
|
||||
setPluginXml("plugin.xml")
|
||||
myFixture.enableInspections(ListenerImplementationMustNotBeDisposableInspection())
|
||||
}
|
||||
|
||||
private fun addPlatformClasses() {
|
||||
myFixture.addClass(
|
||||
"""
|
||||
package com.intellij.openapi;
|
||||
|
||||
public interface Disposable { }
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
myFixture.addClass(
|
||||
"""
|
||||
public interface BaseListenerInterface { }
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
protected open fun doTest() {
|
||||
myFixture.testHighlighting(getTestName(false) + '.' + getFileExtension())
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user