mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[devkit] new inspection: "Can be DumbAware" (IJPL-115445), initial
GitOrigin-RevId: bf9dc5ce980b721fece8d1fde9d4f9b726fa92ca
This commit is contained in:
committed by
intellij-monorepo-bot
parent
fb0c01850e
commit
f3e97446ec
@@ -0,0 +1,21 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports API that can be made <code>DumbAware</code>.
|
||||
<p>
|
||||
If the implementation does not access indexes, it can be marked as working during dumb mode.
|
||||
</p>
|
||||
<p>
|
||||
See <a href="https://plugins.jetbrains.com/docs/intellij/indexing-and-psi-stubs.html#DumbAwareAPI">DumbAware API</a>
|
||||
for more information.
|
||||
</p>
|
||||
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2025.1</small>
|
||||
</p>
|
||||
<p>
|
||||
Add classes that have been verified <em>not</em> to be suitable to "Ignore class list".
|
||||
These will be skipped while avoiding adding a suppression comment/annotation.
|
||||
A quick-fix is available to add the highlighted class to this list.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -468,6 +468,13 @@
|
||||
level="ERROR"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.PotentialDeadlockInServiceInitializationInspection"/>
|
||||
|
||||
<localInspection language="JVM"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.code"
|
||||
enabledByDefault="false" level="WEAK WARNING"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.CanBeDumbAwareInspection"
|
||||
key="inspection.can.be.dumb.aware.name"/>
|
||||
|
||||
<moduleConfigurationEditorProvider implementation="org.jetbrains.idea.devkit.module.PluginModuleEditorsProvider"/>
|
||||
<implicitUsageProvider implementation="org.jetbrains.idea.devkit.inspections.DevKitImplicitUsageProvider"/>
|
||||
|
||||
|
||||
@@ -705,3 +705,9 @@ devkit.unstable.api.usage.ignore.declared.inside.this.project=Ignore unstable AP
|
||||
|
||||
inspections.jcomponent.data.provider.display.name=JComponent must use UiDataProvider
|
||||
inspections.jcomponent.data.provider.use.uidataprovider.instead.of.dataprovider=Use UiDataProvider instead of DataProvider
|
||||
|
||||
inspection.can.be.dumb.aware.name=Can be DumbAware
|
||||
inspection.can.be.dumb.aware.settings.ignore.classes.title=Ignore class list:
|
||||
inspection.can.be.dumb.aware.settings.ignore.classes.dialog.title=Specify Class to Ignore
|
||||
inspection.can.be.dumb.aware.message=Can be made DumbAware if it does not access indexes
|
||||
inspection.can.be.dumb.aware.quickfix.add.to.ignore=Ignore ''{0}''
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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
|
||||
|
||||
import com.intellij.codeInsight.options.JavaClassValidator
|
||||
import com.intellij.codeInspection.LocalQuickFix
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.options.OptPane
|
||||
import com.intellij.codeInspection.options.OptPane.pane
|
||||
import com.intellij.codeInspection.options.OptPane.stringList
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName
|
||||
import com.intellij.lang.jvm.JvmModifier
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.project.PossiblyDumbAware
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.PsiTypes
|
||||
import com.intellij.psi.impl.light.LightMethodBuilder
|
||||
import com.intellij.psi.util.InheritanceUtil
|
||||
import com.siyeh.ig.ui.ExternalizableStringSet
|
||||
import org.jetbrains.idea.devkit.DevKitBundle
|
||||
|
||||
internal class CanBeDumbAwareInspection : DevKitJvmInspection.ForClass() {
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
var ignoreClasses: MutableList<String> = ExternalizableStringSet()
|
||||
|
||||
override fun getOptionsPane(): OptPane {
|
||||
return pane(
|
||||
stringList("ignoreClasses", DevKitBundle.message("inspection.can.be.dumb.aware.settings.ignore.classes.title"),
|
||||
JavaClassValidator()
|
||||
.withTitle(DevKitBundle.message("inspection.can.be.dumb.aware.settings.ignore.classes.dialog.title"))
|
||||
.withSuperClass(PossiblyDumbAware::class.java.canonicalName))
|
||||
)
|
||||
}
|
||||
|
||||
override fun checkClass(project: Project, psiClass: PsiClass, sink: HighlightSink) {
|
||||
if (psiClass.hasModifier(JvmModifier.ABSTRACT)) return
|
||||
val qualifiedName = psiClass.qualifiedName
|
||||
if (ignoreClasses.contains(qualifiedName)) return
|
||||
|
||||
val possiblyDumbAwarePsiClass = JavaPsiFacade.getInstance(project)
|
||||
.findClass(PossiblyDumbAware::class.java.canonicalName, psiClass.resolveScope) ?: return
|
||||
if (!psiClass.isInheritor(possiblyDumbAwarePsiClass, true)) return
|
||||
|
||||
if (InheritanceUtil.isInheritor(psiClass, DumbAware::class.java.canonicalName)) return
|
||||
|
||||
val isDumbAwareMethod = LightMethodBuilder(psiClass.manager, "isDumbAware")
|
||||
.setContainingClass(possiblyDumbAwarePsiClass)
|
||||
.setModifiers(PsiModifier.PUBLIC)
|
||||
.setMethodReturnType(PsiTypes.booleanType())
|
||||
val overriddenMethod = psiClass.findMethodBySignature(isDumbAwareMethod, true)
|
||||
if (overriddenMethod != null) {
|
||||
if (overriddenMethod.containingClass != possiblyDumbAwarePsiClass) {
|
||||
return
|
||||
}
|
||||
|
||||
// explicit search for 'default' method in interfaces is necessary
|
||||
for (clazz in psiClass.interfaces) {
|
||||
val overriddenInInterface = clazz.findMethodBySignature(isDumbAwareMethod, false)
|
||||
if (overriddenInInterface != null &&
|
||||
overriddenInInterface.containingClass != possiblyDumbAwarePsiClass) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val fixes =
|
||||
when {
|
||||
qualifiedName != null -> object : LocalQuickFix {
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
ignoreClasses.add(qualifiedName)
|
||||
}
|
||||
|
||||
override fun getFamilyName(): @IntentionFamilyName String {
|
||||
return DevKitBundle.message("inspection.can.be.dumb.aware.quickfix.add.to.ignore", qualifiedName)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
sink.highlight(DevKitBundle.message("inspection.can.be.dumb.aware.message"), fixes)
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public abstract class AbstractClassNotImplementingDumbAware implements PossiblyDumbAware {
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public class ImplementingDumbAware implements PossiblyDumbAware, DumbAware {
|
||||
// explicit override does not matter
|
||||
@Override
|
||||
public boolean isDumbAware() {
|
||||
return PossiblyDumbAware.super.isDumbAware();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public class ImplementingDumbAwareByParent extends ByParent {
|
||||
}
|
||||
|
||||
interface ParentPossiblyDumbAware extends PossiblyDumbAware {}
|
||||
abstract class ByParent implements ParentPossiblyDumbAware, DumbAware {}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public class <weak_warning descr="Can be made DumbAware if it does not access indexes">NotImplementingDumbAware</weak_warning> implements PossiblyDumbAware {
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public class <weak_warning descr="Can be made DumbAware if it does not access indexes">NotImplementingDumbAwareByParent</weak_warning> extends ByParent {
|
||||
}
|
||||
|
||||
abstract class ByParent implements PossiblyDumbAware {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class NotImplementingPossiblyDumbAware {
|
||||
public boolean isDumbAware() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public class OverridingIsDumbAware implements PossiblyDumbAware {
|
||||
@Override
|
||||
public boolean isDumbAware() {
|
||||
return PossiblyDumbAware.super.isDumbAware();
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public class OverridingIsDumbAwareByInterface implements ByInterface {
|
||||
}
|
||||
|
||||
interface ByInterface extends PossiblyDumbAware {
|
||||
|
||||
@Override
|
||||
default boolean isDumbAware() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
|
||||
public class OverridingIsDumbAwareByParent extends ByParent {
|
||||
}
|
||||
|
||||
abstract class ByParent implements PossiblyDumbAware {
|
||||
|
||||
@Override
|
||||
public boolean isDumbAware() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// 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
|
||||
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil
|
||||
import org.jetbrains.idea.devkit.inspections.quickfix.LightDevKitInspectionFixTestBase
|
||||
|
||||
@TestDataPath("/inspections/canBeDumbAware")
|
||||
class CanBeDumbAwareInspectionTest : LightDevKitInspectionFixTestBase() {
|
||||
|
||||
override fun getBasePath() = DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/canBeDumbAware"
|
||||
|
||||
override fun getFileExtension(): String = "java"
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
myFixture.enableInspections(CanBeDumbAwareInspection())
|
||||
|
||||
myFixture.addClass("""
|
||||
package com.intellij.openapi.project;
|
||||
|
||||
public interface DumbAware {}
|
||||
""".trimIndent())
|
||||
myFixture.addClass("""
|
||||
package com.intellij.openapi.project;
|
||||
|
||||
public interface PossiblyDumbAware {
|
||||
default boolean isDumbAware() {
|
||||
//noinspection SSBasedInspection
|
||||
return this instanceof DumbAware;
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun testNotImplementingPossiblyDumbAware() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testImplementingDumbAware() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotImplementingDumbAware() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testAbstractClassNotImplementingDumbAware() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotImplementingDumbAwareByParent() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testImplementingDumbAwareByParent() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testOverridingIsDumbAware() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testOverridingIsDumbAwareByParent() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testOverridingIsDumbAwareByInterface() {
|
||||
doTest()
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -27,6 +27,7 @@ public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase
|
||||
*/
|
||||
private static final List<String> WIP_INSPECTIONS =
|
||||
Stream.of("ExtensionClassShouldBeFinalAndNonPublic",
|
||||
"CanBeDumbAware",
|
||||
"CancellationCheckInLoops",
|
||||
"ThreadingConcurrency",
|
||||
"CallingMethodShouldBeRequiresBlockingContext",
|
||||
@@ -41,7 +42,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)", 73,
|
||||
assertEquals("Mismatch in total inspections, check classpath in test run configuration (intellij.devkit.plugin)", 74,
|
||||
devkitInspections.size());
|
||||
|
||||
List<LocalInspectionEP> disabledInspections = ContainerUtil.filter(devkitInspections, ep -> !ep.enabledByDefault);
|
||||
|
||||
Reference in New Issue
Block a user