diff --git a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties index 5f3c2b39c261..58b3a7c616f8 100644 --- a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties +++ b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties @@ -297,7 +297,7 @@ inspections.plugin.xml.dynamic.plugin.usage.of.non.dynamic.extension.point=Usage inspections.plugin.xml.dynamic.plugin.analyze.extension.point={0} for ''{1}'' inspections.stateful.extension.point.leak.psi.element=Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead -inspections.stateful.extension.point.leak.psi.element.quick.fix=See also LocalQuickFixOnPsiElement +inspections.stateful.extension.point.leak.psi.element.quick.fix=See also LocalQuickFixOnPsiElement. inspections.stateful.extension.point.do.not.use.in.quick.fix=Do not use {0} as a field in quick-fix inspections.stateful.extension.point.do.not.use.in.extension=Do not use {0} as a field in extension diff --git a/plugins/devkit/devkit-core/src/inspections/StatefulEpInspection.kt b/plugins/devkit/devkit-core/src/inspections/StatefulEpInspection.kt index de959cc1402c..92646c11e6b8 100644 --- a/plugins/devkit/devkit-core/src/inspections/StatefulEpInspection.kt +++ b/plugins/devkit/devkit-core/src/inspections/StatefulEpInspection.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// 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.LocalQuickFix @@ -10,16 +10,19 @@ import com.intellij.lang.jvm.types.JvmReferenceType import com.intellij.lang.jvm.types.JvmType import com.intellij.lang.jvm.util.JvmInheritanceUtil.isInheritor import com.intellij.lang.jvm.util.JvmUtil -import com.intellij.openapi.components.ProjectComponent import com.intellij.openapi.project.Project -import com.intellij.psi.* +import com.intellij.psi.PsiAnonymousClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.psi.PsiWildcardType import com.intellij.psi.util.PsiTreeUtil -import com.intellij.psi.util.PsiUtil import com.intellij.psi.xml.XmlTag import com.intellij.util.SmartList import org.jetbrains.annotations.Nls import org.jetbrains.idea.devkit.DevKitBundle import org.jetbrains.idea.devkit.util.processExtensionsByClassName +import org.jetbrains.uast.* +import org.jetbrains.uast.visitor.AbstractUastVisitor import java.util.* class StatefulEpInspection : DevKitJvmInspection() { @@ -60,10 +63,11 @@ class StatefulEpInspection : DevKitJvmInspection() { } override fun visitClass(clazz: JvmClass): Boolean? { - if (canCapture(clazz) && isInheritor (clazz, localQuickFixFqn)) { + if (canCapture(clazz) && isInheritor(clazz, localQuickFixFqn)) { for (capturedElement in getCapturedPoints(clazz)) { - if (capturedElement.resolved is PsiVariable && isHoldingElement(capturedElement.resolved.type, PsiElement::class.java.canonicalName)) { - (sink as HighlightSinkImpl).holder.registerProblem(capturedElement.reference, getMessage(true)) + if (capturedElement.resolved is UVariable && isHoldingElement(capturedElement.resolved.type, PsiElement::class.java.canonicalName)) { + val capturedElementPsi = capturedElement.reference.sourcePsi ?: continue + (sink as HighlightSinkImpl).holder.registerProblem(capturedElementPsi, getMessage(true)) } } } @@ -81,7 +85,12 @@ class StatefulEpInspection : DevKitJvmInspection() { return true } if (type is JvmReferenceType && type.typeArguments().iterator().hasNext() && holderClasses.any { isInheritor(typeClass, it) }) { - return type.typeArguments().any { isHoldingElement(it, elementClass) } + return type.typeArguments().any { + // Kotlin's List is List , so we need to use bound if exists: + val typeBound = (it as? PsiWildcardType)?.bound + val actualType = typeBound ?: it + isHoldingElement(actualType, elementClass) + } } return false } @@ -89,39 +98,63 @@ class StatefulEpInspection : DevKitJvmInspection() { private fun getMessage(isQuickFix: Boolean): @Nls String { var message = DevKitBundle.message("inspections.stateful.extension.point.leak.psi.element") if (isQuickFix) { - message += " " + DevKitBundle.message("inspections.stateful.extension.point.leak.psi.element.quick.fix") + message += ". " + DevKitBundle.message("inspections.stateful.extension.point.leak.psi.element.quick.fix") } return message } } -data class CapturedDescriptor(val reference: PsiElement, val resolved: PsiElement) +private data class CapturedDescriptor(val reference: UElement, val resolved: UElement) -fun getCapturedPoints(clazz: JvmClass): Collection { - val res = mutableListOf() - if (clazz is PsiClass && canCapture(clazz)) { - val argumentList = if (clazz is PsiAnonymousClass) clazz.argumentList else null - clazz.accept(object : JavaRecursiveElementWalkingVisitor() { - override fun visitReferenceExpression(expression: PsiReferenceExpression) { - if (expression.qualifierExpression == null && (argumentList == null || !PsiTreeUtil.isAncestor(argumentList, expression, true))) { - val refElement = expression.resolve() - if (refElement is PsiVariable) { - val containingClass = PsiTreeUtil.getParentOfType(refElement, PsiClass::class.java) - if (PsiTreeUtil.isAncestor(containingClass, clazz, true)) { - res.add(CapturedDescriptor(expression, refElement)) - } +private fun getCapturedPoints(clazz: JvmClass): Collection { + val capturedPoints = mutableListOf() + if (canCapture(clazz)) { + val uClass = (clazz as? PsiElement)?.toUElement() as? UClass ?: return emptyList() + val argumentList = if (uClass is UAnonymousClass) (uClass.javaPsi as? PsiAnonymousClass)?.argumentList else null + uClass.accept(object : AbstractUastVisitor() { + override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean { + val expressionPsi = node.sourcePsi ?: return super.visitSimpleNameReferenceExpression(node) + if (argumentList == null || !PsiTreeUtil.isAncestor(argumentList, expressionPsi, true)) { + val refElement = node.resolveToUElement() as? UVariable ?: return super.visitSimpleNameReferenceExpression(node) + val containingClass = refElement.getUastParentOfType() ?: return super.visitSimpleNameReferenceExpression(node) + if (isAncestor(containingClass, uClass)) { + capturedPoints.add(CapturedDescriptor(node, refElement)) } } + return super.visitSimpleNameReferenceExpression(node) } }) } - return res + return capturedPoints } -private fun canCapture(clazz: JvmClass) = clazz is PsiClass && PsiUtil.isLocalOrAnonymousClass(clazz) +private fun isAncestor(ancestor: UElement, child: UElement): Boolean { + val ancestorPsi = ancestor.sourcePsi ?: return false + val childPsi = child.sourcePsi ?: return false + return PsiTreeUtil.isAncestor(ancestorPsi, childPsi, true) +} + +private fun canCapture(clazz: JvmClass): Boolean { + val uClass = (clazz as? PsiElement)?.toUElement() as? UClass ?: return false + return uClass.isLocalOrAnonymousClass() +} + +private fun UClass.isLocalOrAnonymousClass(): Boolean { + return this is UAnonymousClass || this.isLocalClass() +} + +private fun UClass.isLocalClass(): Boolean { + val parent = this.uastParent + if (parent is UDeclarationsExpression && parent.uastParent is UBlockExpression) { + return true + } + return (parent as? UClass)?.isLocalOrAnonymousClass() == true +} private val localQuickFixFqn = LocalQuickFix::class.java.canonicalName -private val projectComponentFqn = ProjectComponent::class.java.canonicalName + +@Suppress("DEPRECATION") +private val projectComponentFqn = com.intellij.openapi.components.ProjectComponent::class.java.canonicalName private fun findEpCandidates(project: Project, className: String): Collection { val result = Collections.synchronizedList(SmartList()) diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/CapturedFromOuterClass.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/CapturedFromOuterClass.java index f47c6b584f45..bf36a3938a26 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/CapturedFromOuterClass.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/CapturedFromOuterClass.java @@ -1,24 +1,28 @@ -public class CapturedFromOuterClass extends com.intellij.codeInspection.LocalQuickFix { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; - com.intellij.openapi.project.Project p; - final com.intellij.openapi.project.Project pf; - public CapturedFromOuterClass(com.intellij.psi.PsiElement a, String b) { - super(); +import com.intellij.psi.PsiElement; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.psi.PsiReference; +import com.intellij.openapi.project.Project; + +public class CapturedFromOuterClass implements LocalQuickFix { + final PsiElement pe; + final PsiReference r; + Project p; + final Project pf; + public CapturedFromOuterClass(PsiElement a, String b) { pe = null; - r =null; + r = null; p = pf = null; - com.intellij.codeInspection.LocalQuickFix fix = new com.intellij.codeInspection.LocalQuickFix() { - private void a(com.intellij.psi.PsiElement a1, String b1) { - System.out.println(a); + LocalQuickFix fix = new LocalQuickFix() { + private void a(PsiElement a1, String b1) { + System.out.println(a); System.out.println(b); System.out.println(a1); System.out.println(b1); } }; - com.intellij.psi.PsiElement notFix = new com.intellij.psi.PsiElement() { - private void a(com.intellij.psi.PsiElement a1, String b1) { + PsiElement notFix = new PsiElement() { + private void a(PsiElement a1, String b1) { System.out.println(a); System.out.println(b); System.out.println(a1); @@ -27,13 +31,13 @@ public class CapturedFromOuterClass extends com.intellij.codeInspection.LocalQui }; } - public void test(com.intellij.psi.PsiElement a, String b) { - class B extends com.intellij.codeInspection.LocalQuickFix { - B(com.intellij.psi.PsiElement aa) { + public void test(PsiElement a, String b) { + class B implements LocalQuickFix { + B(PsiElement aa) { } - private void a(com.intellij.psi.PsiElement a1, String b1) { - System.out.println(a); + private void a(PsiElement a1, String b1) { + System.out.println(a); System.out.println(b); System.out.println(a1); System.out.println(b1); @@ -41,4 +45,4 @@ public class CapturedFromOuterClass extends com.intellij.codeInspection.LocalQui }; B b1 = new B(a) {}; } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Collection.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Collection.java deleted file mode 100644 index 3189194976bd..000000000000 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Collection.java +++ /dev/null @@ -1,9 +0,0 @@ -public class Collection extends com.intellij.codeInspection.LocalQuickFix { - java.util.Collection pe; - java.util.Collection r; - java.util.Collection> r1; - java.util.Collection r2; - java.util.Collection r3; - java.util.Collection> r4; - com.intellij.openapi.project.Project p; -} \ No newline at end of file diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/CollectionTests.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/CollectionTests.java new file mode 100644 index 000000000000..e77807e140e7 --- /dev/null +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/CollectionTests.java @@ -0,0 +1,15 @@ +import com.intellij.codeInspection.LocalQuickFix; +import java.util.Collection; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.openapi.project.Project; + +public class CollectionTests implements LocalQuickFix { + Collection pe; + Collection r; + Collection> r1; + Collection r2; + Collection r3; + Collection> r4; + Project p; +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Ext.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Ext.java index 17402d8c3e16..250de88147ca 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Ext.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Ext.java @@ -1,12 +1,16 @@ +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.openapi.project.Project; + public class Ext { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; - com.intellij.openapi.project.Project p; - final com.intellij.openapi.project.Project pf; + final PsiElement pe; + final PsiReference r; + Project p; + final Project pf; public Ext() { super(); pe = null; r =null; p = pf = null; } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/FakeFile.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/FakeFile.java index 2f09502bdbbd..af4d589f2f29 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/FakeFile.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/FakeFile.java @@ -1,8 +1,10 @@ import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; public class FakeFile { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; + final PsiElement pe; + final PsiReference r; Project p; final Project pf; @@ -12,4 +14,4 @@ public class FakeFile { r = null; p = pf = project; } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Fix.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Fix.java index 7cf71980ee93..aeab9f1c022c 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Fix.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Fix.java @@ -1,12 +1,17 @@ -public class Fix extends com.intellij.codeInspection.LocalQuickFix { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; - com.intellij.openapi.project.Project p; - final com.intellij.openapi.project.Project pf; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.openapi.project.Project; + +public class Fix implements LocalQuickFix { + final PsiElement pe; + final PsiReference r; + Project p; + final Project pf; public Fix() { super(); pe = null; r =null; p = pf = null; } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Map.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Map.java deleted file mode 100644 index dd26500b2def..000000000000 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Map.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Map extends com.intellij.codeInspection.LocalQuickFix { - java.util.Map pe; - java.util.Map r; - java.util.Map, com.intellij.psi.PsiReference> r1; - java.util.Map r2; - java.util.Map r3; - java.util.Collection> r4; - java.util.Map r7; - java.util.Map r8; -} \ No newline at end of file diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/MapTests.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/MapTests.java new file mode 100644 index 000000000000..f508c2b5927c --- /dev/null +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/MapTests.java @@ -0,0 +1,16 @@ +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import java.util.Collection; +import java.util.Map; + +public class MapTests implements LocalQuickFix { + Map pe; + Map r; + Map, PsiReference> r1; + Map r2; + Map r3; + Collection> r4; + Map r7; + Map r8; +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/NonFix.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/NonFix.java index 8c2bd3b464be..0cff38300e14 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/NonFix.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/NonFix.java @@ -1,16 +1,20 @@ +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.openapi.project.Project; + public class NonFix { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; - com.intellij.openapi.project.Project p; - final com.intellij.openapi.project.Project pf; + final PsiElement pe; + final PsiReference r; + Project p; + final Project pf; public NonFix() { super(); pe = null; - r =null; + r = null; p = pf = null; } public static class Ext2 { } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectComp.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectComp.java index 32a57c435ade..8e7e22e42148 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectComp.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectComp.java @@ -1,12 +1,17 @@ -public class ProjectComp implements com.intellij.openapi.components.ProjectComponent { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; - com.intellij.openapi.project.Project p; - final com.intellij.openapi.project.Project pf; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; + +public class ProjectComp implements ProjectComponent { + final PsiElement pe; + final PsiReference r; + Project p; + final Project pf; public ProjectComp() { super(); pe = null; - r =null; + r = null; p = pf = null; } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectConfigurable.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectConfigurable.java index c2ee85600750..c8feb126de8c 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectConfigurable.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectConfigurable.java @@ -1,8 +1,10 @@ import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; public class ProjectConfigurable { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; + final PsiElement pe; + final PsiReference r; Project p; final Project pf; public ProjectConfigurable(Project project) { @@ -11,4 +13,4 @@ public class ProjectConfigurable { r = null; p = pf = project; } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectService.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectService.java index f070389f4dad..28f2e87e9f98 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectService.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/ProjectService.java @@ -1,8 +1,10 @@ import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; public class ProjectService { - final com.intellij.psi.PsiElement pe; - final com.intellij.psi.PsiReference r; + final PsiElement pe; + final PsiReference r; Project p; final Project pf; public ProjectService(Project project) { @@ -11,4 +13,4 @@ public class ProjectService { r = null; p = pf = project; } -} \ No newline at end of file +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Ref.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Ref.java deleted file mode 100644 index 5fab974884c0..000000000000 --- a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/Ref.java +++ /dev/null @@ -1,9 +0,0 @@ -public class Ref extends com.intellij.codeInspection.LocalQuickFix { - com.intellij.openapi.util.Ref pe; - com.intellij.openapi.util.Ref r; - com.intellij.openapi.util.Ref> r1; - com.intellij.openapi.util.Ref r2; - com.intellij.openapi.util.Ref r3; - com.intellij.openapi.util.Ref> r4; - com.intellij.openapi.project.Project p; -} \ No newline at end of file diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/RefTests.java b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/RefTests.java new file mode 100644 index 000000000000..2c83f546ef4b --- /dev/null +++ b/plugins/devkit/devkit-java-tests/testData/inspections/statefulEp/RefTests.java @@ -0,0 +1,15 @@ +import com.intellij.openapi.util.Ref; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.openapi.project.Project; + +public class RefTests implements LocalQuickFix { + Ref pe; + Ref r; + Ref> r1; + Ref r2; + Ref r3; + Ref> r4; + Project p; +} diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/StatefulEpInspectionTest.java b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/StatefulEpInspectionTest.java index e492f792ffce..b15ce3ca7bf0 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/StatefulEpInspectionTest.java +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/StatefulEpInspectionTest.java @@ -1,75 +1,68 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// 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("$CONTENT_ROOT/testData/inspections/statefulEp") -public class StatefulEpInspectionTest extends PluginModuleTestCase { +public class StatefulEpInspectionTest extends StatefulEpInspectionTestBase { @Override protected String getBasePath() { return DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/statefulEp"; } @Override - protected void setUp() throws Exception { - super.setUp(); - myFixture.addClass("package com.intellij.openapi.project; public class Project {}"); - myFixture.addClass("package com.intellij.psi; public class PsiElement {}"); - myFixture.addClass("package com.intellij.psi; public class PsiReference {}"); - myFixture.addClass("package com.intellij.codeInspection; public class LocalQuickFix {}"); - myFixture.addClass("package com.intellij.openapi.components; public interface ProjectComponent {}"); - myFixture.addClass("package com.intellij.openapi.util; public class Ref {}"); - myFixture.enableInspections(new StatefulEpInspection()); + protected String getFileExtension() { + return "java"; } - public void testLocalQuickFix() { - myFixture.testHighlighting("Fix.java"); + public void testFix() { + doTest(); } public void testNonFix() { setPluginXml("plugin.xml"); - myFixture.testHighlighting("NonFix.java"); + doTest(); } public void testExt() { setPluginXml("plugin.xml"); - myFixture.testHighlighting("Ext.java"); + doTest(); } public void testProjectComp() { setPluginXml("plugin.xml"); - myFixture.testHighlighting("ProjectComp.java"); + doTest(); } public void testProjectService() { setPluginXml("plugin.xml"); - myFixture.testHighlighting("ProjectService.java"); + doTest(); } public void testProjectConfigurable() { setPluginXml("plugin.xml"); - myFixture.testHighlighting("ProjectConfigurable.java"); + doTest(); } public void testFakeFile() { setPluginXml("plugin.xml"); - myFixture.testHighlighting("FakeFile.java"); + doTest(); } public void testCapturedFromOuterClass() { - myFixture.testHighlighting("CapturedFromOuterClass.java"); + doTest(); } - public void testCollection() { - myFixture.testHighlighting("Collection.java"); + public void testCollectionTests() { + doTest(); } - public void testMap() { - myFixture.testHighlighting("Map.java"); + public void testMapTests() { + doTest(); } - public void testRef() { - myFixture.testHighlighting("Ref.java"); + public void testRefTests() { + doTest(); } } diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/CapturedFromOuterClass.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/CapturedFromOuterClass.kt new file mode 100644 index 000000000000..25a6e1d6dcd1 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/CapturedFromOuterClass.kt @@ -0,0 +1,57 @@ +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class CapturedFromOuterClass(a: PsiElement?, b: String?) : LocalQuickFix { + val pe: PsiElement? + val r: PsiReference? + var p: Project? + val pf: Project? + + init { + pe = null + r = null + pf = null + p = pf + + @Suppress("UNUSED_VARIABLE") + val fix: LocalQuickFix = object : LocalQuickFix { + private fun a(a1: PsiElement, b1: String) { + any(a) + any(b) + any(a1) + any(b1) + } + } + + @Suppress("UNUSED_VARIABLE") + val notFix: Any = object : Any() { + private fun a(a1: PsiElement, b1: String) { + any(a) + any(b) + any(a1) + any(b1) + } + } + } + + fun test(a: PsiElement?, b: String?) { + open class B(@Suppress("UNUSED_PARAMETER") aa: PsiElement?) : LocalQuickFix { + private fun a(a1: PsiElement, b1: String) { + any(a) + any(b) + any(a1) + any(b1) + } + } + + @Suppress("UNUSED_VARIABLE") + val b1 = object : B(a) {} + } +} + +private fun any(@Suppress("UNUSED_PARAMETER") any: Any?) { + // any +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/CollectionTests.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/CollectionTests.kt new file mode 100644 index 000000000000..c6e833406378 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/CollectionTests.kt @@ -0,0 +1,14 @@ +import com.intellij.openapi.project.Project +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class CollectionTests : LocalQuickFix { + var pe: Collection? = null + var r: Collection? = null + var r1: Collection>? = null + var r2: Collection<*>? = null + var r3: Collection? = null + var r4: Collection>? = null + var p: Project? = null +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/Ext.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/Ext.kt new file mode 100644 index 000000000000..494dd7c78d30 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/Ext.kt @@ -0,0 +1,17 @@ +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class Ext { + val pe: PsiElement? + val r: PsiReference? + var p: Project? + val pf: Project? + + init { + pe = null + r = null + pf = null + p = pf + } +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/FakeFile.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/FakeFile.kt new file mode 100644 index 000000000000..7e3c1643b76a --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/FakeFile.kt @@ -0,0 +1,13 @@ +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class FakeFile(val pf: Project) { + val pe: PsiElement? = null + val r: PsiReference? = null + var p: Project + + init { + p = pf + } +} \ No newline at end of file diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/Fix.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/Fix.kt new file mode 100644 index 000000000000..04a9bce1661f --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/Fix.kt @@ -0,0 +1,18 @@ +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class Fix : LocalQuickFix { + val pe: PsiElement? + val r: PsiReference? + var p: Project? + val pf: Project? + + init { + pe = null + r = null + pf = null + p = pf + } +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/MapTests.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/MapTests.kt new file mode 100644 index 000000000000..544c3cf4174a --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/MapTests.kt @@ -0,0 +1,15 @@ +import com.intellij.codeInspection.LocalQuickFix +import kotlin.collections.Map +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class MapTests : LocalQuickFix { + val pe: Map? = null + val r: Map? = null + val r1: Map, PsiReference>? = null + var r2: Map<*, *>? = null + var r3: Map? = null + val r4: Collection>? = null + val r7: Map? = null + val r8: Map? = null +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/NonFix.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/NonFix.kt new file mode 100644 index 000000000000..8edd22e07dc3 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/NonFix.kt @@ -0,0 +1,16 @@ +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class NonFix { + val pe: PsiElement? = null + val r: PsiReference? = null + var p: Project? + val pf: Project? = null + + init { + p = pf + } + + class Ext2 +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectComp.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectComp.kt new file mode 100644 index 000000000000..d3ecaccf2ef8 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectComp.kt @@ -0,0 +1,18 @@ +import com.intellij.openapi.components.ProjectComponent +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class ProjectComp : ProjectComponent { + val pe: PsiElement? + val r: PsiReference? + var p: Project? + val pf: Project? + + init { + pe = null + r = null + pf = null + p = pf + } +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectConfigurable.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectConfigurable.kt new file mode 100644 index 000000000000..fb4f2b154e0b --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectConfigurable.kt @@ -0,0 +1,17 @@ +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class ProjectConfigurable(project: Project) { + val pe: PsiElement? + val r: PsiReference? + var p: Project + val pf: Project + + init { + pe = null + r = null + pf = project + p = pf + } +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectService.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectService.kt new file mode 100644 index 000000000000..e580787e75ba --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/ProjectService.kt @@ -0,0 +1,17 @@ +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class ProjectService(project: Project) { + val pe: PsiElement? + val r: PsiReference? + var p: Project? + val pf: Project? + + init { + pe = null + r = null + pf = project + p = pf + } +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/RefTests.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/RefTests.kt new file mode 100644 index 000000000000..f0b0bfd90025 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/RefTests.kt @@ -0,0 +1,15 @@ +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.openapi.util.Ref +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.openapi.project.Project + +class RefTests : LocalQuickFix { + var pe: Ref? = null + var r: Ref? = null + var r1: Ref>? = null + var r2: Ref<*>? = null + var r3: Ref? = null + var r4: Ref>? = null + var p: Project? = null +} diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/plugin.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/plugin.xml new file mode 100644 index 000000000000..76e4aafa9deb --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/statefulEp/plugin.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtStatefulEpInspectionTest.java b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtStatefulEpInspectionTest.java new file mode 100644 index 000000000000..b13d3005b0ad --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtStatefulEpInspectionTest.java @@ -0,0 +1,70 @@ +// 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.StatefulEpInspectionTestBase; +import org.jetbrains.idea.devkit.kotlin.DevkitKtTestsUtil; + +@TestDataPath("$CONTENT_ROOT/testData/inspections/statefulEp") +public class KtStatefulEpInspectionTest extends StatefulEpInspectionTestBase { + + @Override + protected String getBasePath() { + return DevkitKtTestsUtil.TESTDATA_PATH + "inspections/statefulEp"; + } + + @Override + protected String getFileExtension() { + return "kt"; + } + + public void testFix() { + doTest(); + } + + public void testNonFix() { + setPluginXml("plugin.xml"); + doTest(); + } + + public void testExt() { + setPluginXml("plugin.xml"); + doTest(); + } + + public void testProjectComp() { + setPluginXml("plugin.xml"); + doTest(); + } + + public void testProjectService() { + setPluginXml("plugin.xml"); + doTest(); + } + + public void testProjectConfigurable() { + setPluginXml("plugin.xml"); + doTest(); + } + + public void testFakeFile() { + setPluginXml("plugin.xml"); + doTest(); + } + + public void testCapturedFromOuterClass() { + doTest(); + } + + public void testCollectionTests() { + doTest(); + } + + public void testMapTests() { + doTest(); + } + + public void testRefTests() { + doTest(); + } +} diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/StatefulEpInspectionTestBase.java b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/StatefulEpInspectionTestBase.java new file mode 100644 index 000000000000..622a0aed8c4e --- /dev/null +++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/StatefulEpInspectionTestBase.java @@ -0,0 +1,25 @@ +// 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; + +public abstract class StatefulEpInspectionTestBase extends PluginModuleTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.addClass("package com.intellij.openapi.project; public interface Project {}"); + myFixture.addClass("package com.intellij.psi; public interface PsiElement {}"); + myFixture.addClass("package com.intellij.psi; public interface PsiReference {}"); + myFixture.addClass("package com.intellij.codeInspection; public interface LocalQuickFix {}"); + myFixture.addClass("package com.intellij.codeInspection; public interface ProblemDescriptor {}"); + myFixture.addClass("package com.intellij.openapi.components; public interface ProjectComponent {}"); + myFixture.addClass("package com.intellij.openapi.util; public class Ref {}"); + myFixture.enableInspections(new StatefulEpInspection()); + } + + protected void doTest() { + myFixture.testHighlighting(getTestName(false) + '.' + getFileExtension()); + } + + protected abstract String getFileExtension(); + +}