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.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=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.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 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 package org.jetbrains.idea.devkit.inspections
import com.intellij.codeInspection.LocalQuickFix 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.types.JvmType
import com.intellij.lang.jvm.util.JvmInheritanceUtil.isInheritor import com.intellij.lang.jvm.util.JvmInheritanceUtil.isInheritor
import com.intellij.lang.jvm.util.JvmUtil import com.intellij.lang.jvm.util.JvmUtil
import com.intellij.openapi.components.ProjectComponent
import com.intellij.openapi.project.Project 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.PsiTreeUtil
import com.intellij.psi.util.PsiUtil
import com.intellij.psi.xml.XmlTag import com.intellij.psi.xml.XmlTag
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.annotations.Nls import org.jetbrains.annotations.Nls
import org.jetbrains.idea.devkit.DevKitBundle import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.idea.devkit.util.processExtensionsByClassName import org.jetbrains.idea.devkit.util.processExtensionsByClassName
import org.jetbrains.uast.*
import org.jetbrains.uast.visitor.AbstractUastVisitor
import java.util.* import java.util.*
class StatefulEpInspection : DevKitJvmInspection() { class StatefulEpInspection : DevKitJvmInspection() {
@@ -60,10 +63,11 @@ class StatefulEpInspection : DevKitJvmInspection() {
} }
override fun visitClass(clazz: JvmClass): Boolean? { override fun visitClass(clazz: JvmClass): Boolean? {
if (canCapture(clazz) && isInheritor (clazz, localQuickFixFqn)) { if (canCapture(clazz) && isInheritor(clazz, localQuickFixFqn)) {
for (capturedElement in getCapturedPoints(clazz)) { for (capturedElement in getCapturedPoints(clazz)) {
if (capturedElement.resolved is PsiVariable && isHoldingElement(capturedElement.resolved.type, PsiElement::class.java.canonicalName)) { if (capturedElement.resolved is UVariable && isHoldingElement(capturedElement.resolved.type, PsiElement::class.java.canonicalName)) {
(sink as HighlightSinkImpl).holder.registerProblem(capturedElement.reference, getMessage(true)) val capturedElementPsi = capturedElement.reference.sourcePsi ?: continue
(sink as HighlightSinkImpl).holder.registerProblem(capturedElementPsi, getMessage(true))
} }
} }
} }
@@ -81,7 +85,12 @@ class StatefulEpInspection : DevKitJvmInspection() {
return true return true
} }
if (type is JvmReferenceType && type.typeArguments().iterator().hasNext() && holderClasses.any { isInheritor(typeClass, it) }) { 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 return false
} }
@@ -89,39 +98,63 @@ class StatefulEpInspection : DevKitJvmInspection() {
private fun getMessage(isQuickFix: Boolean): @Nls String { private fun getMessage(isQuickFix: Boolean): @Nls String {
var message = DevKitBundle.message("inspections.stateful.extension.point.leak.psi.element") var message = DevKitBundle.message("inspections.stateful.extension.point.leak.psi.element")
if (isQuickFix) { 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 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> { private fun getCapturedPoints(clazz: JvmClass): Collection<CapturedDescriptor> {
val res = mutableListOf<CapturedDescriptor>() val capturedPoints = mutableListOf<CapturedDescriptor>()
if (clazz is PsiClass && canCapture(clazz)) { if (canCapture(clazz)) {
val argumentList = if (clazz is PsiAnonymousClass) clazz.argumentList else null val uClass = (clazz as? PsiElement)?.toUElement() as? UClass ?: return emptyList()
clazz.accept(object : JavaRecursiveElementWalkingVisitor() { val argumentList = if (uClass is UAnonymousClass) (uClass.javaPsi as? PsiAnonymousClass)?.argumentList else null
override fun visitReferenceExpression(expression: PsiReferenceExpression) { uClass.accept(object : AbstractUastVisitor() {
if (expression.qualifierExpression == null && (argumentList == null || !PsiTreeUtil.isAncestor(argumentList, expression, true))) { override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
val refElement = expression.resolve() val expressionPsi = node.sourcePsi ?: return super.visitSimpleNameReferenceExpression(node)
if (refElement is PsiVariable) { if (argumentList == null || !PsiTreeUtil.isAncestor(argumentList, expressionPsi, true)) {
val containingClass = PsiTreeUtil.getParentOfType(refElement, PsiClass::class.java) val refElement = node.resolveToUElement() as? UVariable ?: return super.visitSimpleNameReferenceExpression(node)
if (PsiTreeUtil.isAncestor(containingClass, clazz, true)) { val containingClass = refElement.getUastParentOfType<UClass>() ?: return super.visitSimpleNameReferenceExpression(node)
res.add(CapturedDescriptor(expression, refElement)) 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 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> { private fun findEpCandidates(project: Project, className: String): Collection<XmlTag> {
val result = Collections.synchronizedList(SmartList<XmlTag>()) val result = Collections.synchronizedList(SmartList<XmlTag>())

View File

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

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

View File

@@ -1,8 +1,10 @@
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
public class FakeFile { public class FakeFile {
final com.intellij.psi.PsiElement pe; final PsiElement pe;
final com.intellij.psi.PsiReference r; final PsiReference r;
Project p; Project p;
final Project pf; final Project pf;
@@ -12,4 +14,4 @@ public class FakeFile {
r = null; r = null;
p = pf = project; p = pf = project;
} }
} }

View File

@@ -1,12 +1,17 @@
public class Fix extends com.intellij.codeInspection.LocalQuickFix { import 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>; import com.intellij.psi.PsiElement;
final com.intellij.psi.PsiReference <warning descr="Do not use PsiReference as a field in quick-fix">r</warning>; import com.intellij.psi.PsiReference;
com.intellij.openapi.project.Project <warning descr="Do not use Project as a field in quick-fix">p</warning>; import com.intellij.openapi.project.Project;
final com.intellij.openapi.project.Project pf;
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() { public Fix() {
super(); super();
pe = null; pe = null;
r =null; r =null;
p = pf = null; p = pf = 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,16 +1,20 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.openapi.project.Project;
public class NonFix { public class NonFix {
final com.intellij.psi.PsiElement pe; final PsiElement pe;
final com.intellij.psi.PsiReference r; final PsiReference r;
com.intellij.openapi.project.Project p; Project p;
final com.intellij.openapi.project.Project pf; final Project pf;
public NonFix() { public NonFix() {
super(); super();
pe = null; pe = null;
r =null; r = null;
p = pf = null; p = pf = null;
} }
public static class Ext2 { public static class Ext2 {
} }
} }

View File

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

View File

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

View File

@@ -1,8 +1,10 @@
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
public class ProjectService { public class ProjectService {
final com.intellij.psi.PsiElement <warning descr="Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead">pe</warning>; final 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 PsiReference <warning descr="Do not use PsiReference as a field in extension">r</warning>;
Project p; Project p;
final Project pf; final Project pf;
public ProjectService(Project project) { public ProjectService(Project project) {
@@ -11,4 +13,4 @@ public class ProjectService {
r = null; r = null;
p = pf = project; p = pf = 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; package org.jetbrains.idea.devkit.inspections;
import com.intellij.testFramework.TestDataPath; import com.intellij.testFramework.TestDataPath;
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil; import org.jetbrains.idea.devkit.DevkitJavaTestsUtil;
@TestDataPath("$CONTENT_ROOT/testData/inspections/statefulEp") @TestDataPath("$CONTENT_ROOT/testData/inspections/statefulEp")
public class StatefulEpInspectionTest extends PluginModuleTestCase { public class StatefulEpInspectionTest extends StatefulEpInspectionTestBase {
@Override @Override
protected String getBasePath() { protected String getBasePath() {
return DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/statefulEp"; return DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/statefulEp";
} }
@Override @Override
protected void setUp() throws Exception { protected String getFileExtension() {
super.setUp(); return "java";
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());
} }
public void testLocalQuickFix() { public void testFix() {
myFixture.testHighlighting("Fix.java"); doTest();
} }
public void testNonFix() { public void testNonFix() {
setPluginXml("plugin.xml"); setPluginXml("plugin.xml");
myFixture.testHighlighting("NonFix.java"); doTest();
} }
public void testExt() { public void testExt() {
setPluginXml("plugin.xml"); setPluginXml("plugin.xml");
myFixture.testHighlighting("Ext.java"); doTest();
} }
public void testProjectComp() { public void testProjectComp() {
setPluginXml("plugin.xml"); setPluginXml("plugin.xml");
myFixture.testHighlighting("ProjectComp.java"); doTest();
} }
public void testProjectService() { public void testProjectService() {
setPluginXml("plugin.xml"); setPluginXml("plugin.xml");
myFixture.testHighlighting("ProjectService.java"); doTest();
} }
public void testProjectConfigurable() { public void testProjectConfigurable() {
setPluginXml("plugin.xml"); setPluginXml("plugin.xml");
myFixture.testHighlighting("ProjectConfigurable.java"); doTest();
} }
public void testFakeFile() { public void testFakeFile() {
setPluginXml("plugin.xml"); setPluginXml("plugin.xml");
myFixture.testHighlighting("FakeFile.java"); doTest();
} }
public void testCapturedFromOuterClass() { public void testCapturedFromOuterClass() {
myFixture.testHighlighting("CapturedFromOuterClass.java"); doTest();
} }
public void testCollection() { public void testCollectionTests() {
myFixture.testHighlighting("Collection.java"); doTest();
} }
public void testMap() { public void testMapTests() {
myFixture.testHighlighting("Map.java"); doTest();
} }
public void testRef() { public void testRefTests() {
myFixture.testHighlighting("Ref.java"); 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();
}