IDEA-316821: Support anonymous/inner class fields captured from context in StatefulEpInspection for Kotlin code

GitOrigin-RevId: 7f211c1b5bca9bdcb1f5281cd6717ad58f3751c3
This commit is contained in:
Karol Lewandowski
2023-04-18 18:14:13 +02:00
committed by intellij-monorepo-bot
parent 5252afcb0e
commit 73412b84ed
31 changed files with 533 additions and 133 deletions

View File

@@ -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

View File

@@ -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() {
@@ -62,8 +65,9 @@ class StatefulEpInspection : DevKitJvmInspection() {
override fun visitClass(clazz: JvmClass): Boolean? {
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<PsiElement> is List <? extends Element>, 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<CapturedDescriptor> {
val res = mutableListOf<CapturedDescriptor>()
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<CapturedDescriptor> {
val capturedPoints = mutableListOf<CapturedDescriptor>()
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<UClass>() ?: 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<XmlTag> {
val result = Collections.synchronizedList(SmartList<XmlTag>())

View File

@@ -1,24 +1,28 @@
public class CapturedFromOuterClass extends com.intellij.codeInspection.LocalQuickFix {
final com.intellij.psi.PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">pe</warning>;
final com.intellij.psi.PsiReference <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
com.intellij.openapi.project.Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>;
final PsiReference <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
final Project pf;
public CapturedFromOuterClass(PsiElement a, String b) {
pe = 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(<warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">a</warning>);
LocalQuickFix fix = new LocalQuickFix() {
private void a(PsiElement a1, String b1) {
System.out.println(<warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">a</warning>);
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(<warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">a</warning>);
private void a(PsiElement a1, String b1) {
System.out.println(<warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">a</warning>);
System.out.println(b);
System.out.println(a1);
System.out.println(b1);

View File

@@ -1,9 +0,0 @@
public class Collection extends com.intellij.codeInspection.LocalQuickFix {
java.util.Collection<com.intellij.psi.PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">pe</warning>;
java.util.Collection<com.intellij.psi.PsiReference> <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
java.util.Collection<java.util.Collection<com.intellij.psi.PsiReference>> <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>;
java.util.Collection r2;
java.util.Collection<Integer> r3;
java.util.Collection<java.util.Collection<com.intellij.psi.PsiElement>> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">r4</warning>;
com.intellij.openapi.project.Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
}

View File

@@ -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<PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>;
Collection<PsiReference> <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
Collection<Collection<PsiReference>> <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>;
Collection r2;
Collection<Integer> r3;
Collection<Collection<PsiElement>> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r4</warning>;
Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
}

View File

@@ -1,8 +1,12 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.openapi.project.Project;
public class Ext {
final com.intellij.psi.PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final com.intellij.psi.PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
com.intellij.openapi.project.Project <warning descr="Do not use Project as a field in extension">p</warning>;
final com.intellij.openapi.project.Project pf;
final PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
Project <warning descr="Do not use Project as a field in extension">p</warning>;
final Project pf;
public Ext() {
super();
pe = null;

View File

@@ -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;

View File

@@ -1,8 +1,13 @@
public class Fix extends com.intellij.codeInspection.LocalQuickFix {
final com.intellij.psi.PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">pe</warning>;
final com.intellij.psi.PsiReference <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
com.intellij.openapi.project.Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>;
final PsiReference <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
final Project pf;
public Fix() {
super();
pe = null;

View File

@@ -1,10 +0,0 @@
public class Map extends com.intellij.codeInspection.LocalQuickFix {
java.util.Map<com.intellij.psi.PsiElement, com.intellij.psi.PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">pe</warning>;
java.util.Map<com.intellij.psi.PsiReference, com.intellij.psi.PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">r</warning>;
java.util.Map<java.util.Map<com.intellij.psi.PsiReference, com.intellij.psi.PsiReference>, com.intellij.psi.PsiReference> <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>;
java.util.Map r2;
java.util.Map<Integer, Long> r3;
java.util.Collection<java.util.Map<com.intellij.psi.PsiElement, com.intellij.psi.PsiElement>> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">r4</warning>;
java.util.Map<com.intellij.psi.PsiElement, Long> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">r7</warning>;
java.util.Map<Integer, com.intellij.psi.PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">r8</warning>;
}

View File

@@ -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<PsiElement, PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>;
Map<PsiReference, PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r</warning>;
Map<Map<PsiReference, PsiReference>, PsiReference> <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>;
Map r2;
Map<Integer, Long> r3;
Collection<Map<PsiElement, PsiElement>> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r4</warning>;
Map<PsiElement, Long> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r7</warning>;
Map<Integer, PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r8</warning>;
}

View File

@@ -1,8 +1,12 @@
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;

View File

@@ -1,8 +1,13 @@
public class ProjectComp implements com.intellij.openapi.components.ProjectComponent {
final com.intellij.psi.PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final com.intellij.psi.PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
Project p;
final Project pf;
public ProjectComp() {
super();
pe = null;

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final com.intellij.psi.PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
final PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
Project p;
final Project pf;
public ProjectConfigurable(Project project) {

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final com.intellij.psi.PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
final PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>;
final PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
Project p;
final Project pf;
public ProjectService(Project project) {

View File

@@ -1,9 +0,0 @@
public class Ref extends com.intellij.codeInspection.LocalQuickFix {
com.intellij.openapi.util.Ref<com.intellij.psi.PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">pe</warning>;
com.intellij.openapi.util.Ref<com.intellij.psi.PsiReference> <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
com.intellij.openapi.util.Ref<com.intellij.openapi.util.Ref<com.intellij.psi.PsiReference>> <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>;
com.intellij.openapi.util.Ref r2;
com.intellij.openapi.util.Ref<Integer> r3;
com.intellij.openapi.util.Ref<com.intellij.openapi.util.Ref<com.intellij.psi.PsiElement>> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead See also LocalQuickFixOnPsiElement">r4</warning>;
com.intellij.openapi.project.Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
}

View File

@@ -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<PsiElement> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>;
Ref<PsiReference> <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>;
Ref<Ref<PsiReference>> <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>;
Ref r2;
Ref<Integer> r3;
Ref<Ref<PsiElement>> <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r4</warning>;
Project <warning descr="Do not use Project as a field in quick-fix">p</warning>;
}

View File

@@ -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<T> {}");
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();
}
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>: PsiElement?
val <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>: PsiReference?
var <warning descr="Do not use Project as a field in quick-fix">p</warning>: 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(<warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">a</warning>)
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(<warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">a</warning>)
any(b)
any(a1)
any(b1)
}
}
@Suppress("UNUSED_VARIABLE")
val b1 = object : B(a) {}
}
}
private fun any(@Suppress("UNUSED_PARAMETER") any: Any?) {
// any
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>: Collection<PsiElement>? = null
var <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>: Collection<PsiReference>? = null
var <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>: Collection<Collection<PsiReference>>? = null
var r2: Collection<*>? = null
var r3: Collection<Int>? = null
var <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r4</warning>: Collection<Collection<PsiElement>>? = null
var <warning descr="Do not use Project as a field in quick-fix">p</warning>: Project? = null
}

View File

@@ -0,0 +1,17 @@
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
class Ext {
val <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>: PsiElement?
val <warning descr="Do not use PsiReference as a field in extension">r</warning>: PsiReference?
var <warning descr="Do not use Project as a field in extension">p</warning>: Project?
val pf: Project?
init {
pe = null
r = null
pf = null
p = pf
}
}

View File

@@ -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
}
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>: PsiElement?
val <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>: PsiReference?
var <warning descr="Do not use Project as a field in quick-fix">p</warning>: Project?
val pf: Project?
init {
pe = null
r = null
pf = null
p = pf
}
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>: Map<PsiElement, PsiElement>? = null
val <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r</warning>: Map<PsiReference, PsiElement>? = null
val <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>: Map<Map<PsiReference, PsiReference>, PsiReference>? = null
var r2: Map<*, *>? = null
var r3: Map<Int, Long>? = null
val <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r4</warning>: Collection<Map<PsiElement, PsiElement>>? = null
val <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r7</warning>: Map<PsiElement, Long>? = null
val <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r8</warning>: Map<Int, PsiElement>? = null
}

View File

@@ -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
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>: PsiElement?
val <warning descr="Do not use PsiReference as a field in extension">r</warning>: PsiReference?
var p: Project?
val pf: Project?
init {
pe = null
r = null
pf = null
p = pf
}
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>: PsiElement?
val <warning descr="Do not use PsiReference as a field in extension">r</warning>: PsiReference?
var p: Project
val pf: Project
init {
pe = null
r = null
pf = project
p = pf
}
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>: PsiElement?
val <warning descr="Do not use PsiReference as a field in extension">r</warning>: PsiReference?
var p: Project?
val pf: Project?
init {
pe = null
r = null
pf = project
p = pf
}
}

View File

@@ -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 <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">pe</warning>: Ref<PsiElement>? = null
var <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>: Ref<PsiReference>? = null
var <warning descr="Do not use PsiReference as a field in quick-fix">r1</warning>: Ref<Ref<PsiReference>>? = null
var r2: Ref<*>? = null
var r3: Ref<Int>? = null
var <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead. See also LocalQuickFixOnPsiElement.">r4</warning>: Ref<Ref<PsiElement>>? = null
var <warning descr="Do not use Project as a field in quick-fix">p</warning>: Project? = null
}

View File

@@ -0,0 +1,16 @@
<idea-plugin>
<extensionPoints>
<extensionPoint name="ep" beanClass="EP"/>
<extensionPoint name="projectService" beanClass="SD"/>
<extensionPoint name="projectConfigurable" beanClass="SD"/>
</extensionPoints>
<extensions>
<ep implementation="Ext"/>
<ep implementation="NonFix$Ext2"/>
<ep implementation="ProjectComp"/>
<ep implementation="ProjectComp"/>
<ep forClass="FakeFile" instance="ProjectConfigurable"/>
<projectService serviceImplementation="ProjectService"/>
<projectConfigurable instance="ProjectConfigurable"/>
</extensions>
</idea-plugin>

View File

@@ -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();
}
}

View File

@@ -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<T> {}");
myFixture.enableInspections(new StatefulEpInspection());
}
protected void doTest() {
myFixture.testHighlighting(getTestName(false) + '.' + getFileExtension());
}
protected abstract String getFileExtension();
}