[java unused declaration] - excluded functional ref node out of the graph

GitOrigin-RevId: 28ca95ff4709b8c7466abee73054d1bbb52ee28a
This commit is contained in:
Ilyas Selimov
2022-01-19 08:40:35 +00:00
committed by intellij-monorepo-bot
parent af5b12d340
commit c7d80fb16a
16 changed files with 123 additions and 320 deletions
@@ -270,9 +270,6 @@ public final class RefJavaManagerImpl extends RefJavaManager {
else if (uElement instanceof UField) {
return new RefFieldImpl((UField)uElement, psi, myRefManager);
}
else if (uElement instanceof ULambdaExpression || uElement instanceof UCallableReferenceExpression) {
return new RefFunctionalExpressionImpl((UExpression)uElement, psi, myRefManager);
}
return null;
}
@@ -248,27 +248,43 @@ public class RefJavaUtilImpl extends RefJavaUtil {
//TODO support kotlin
addClassReferenceForStaticImport(node, (PsiMember)psiResolved, refFrom, decl);
}
}
@Override
public boolean visitLambdaExpression(@NotNull ULambdaExpression lambda) {
processFunctionalExpression(lambda, lambda.getFunctionalInterfaceType());
return true;
}
@Override
public boolean visitCallableReferenceExpression(@NotNull UCallableReferenceExpression methodRef) {
RefElement refMethod = refFrom.getRefManager().getReference(methodRef.getSourcePsi());
if (refFrom == refMethod) {
visitReferenceExpression(methodRef);
return false;
}
else {
processFunctionalExpression(methodRef, getFunctionalInterfaceType(methodRef));
return true;
// todo currently if psiResolved is KtParameter, it doesn't convert to UParameter, that seems wrong
UParameter uParam = UastContextKt.toUElement(psiResolved, UParameter.class);
if (uParam != null) {
addReferenceToLambdaParameter(uParam, psiResolved, decl, refFrom);
}
}
}
@Override
public boolean visitLambdaExpression(@NotNull ULambdaExpression node) {
processFunctionalExpression(node, node.getFunctionalInterfaceType());
return false;
}
@Override
public boolean visitCallableReferenceExpression(@NotNull UCallableReferenceExpression node) {
visitReferenceExpression(node);
// todo doesn't work for kotlin
PsiType interfaceType = getFunctionalInterfaceType(node);
processFunctionalExpression(node, interfaceType);
markParametersReferenced(node, interfaceType);
return false;
}
private void markParametersReferenced(@NotNull UCallableReferenceExpression node, @Nullable PsiType type) {
PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(type);
if (method == null) return;
for (PsiParameter param : method.getParameterList().getParameters()) {
RefElement paramRef = refFrom.getRefManager().getReference(param);
if (paramRef != null) {
paramRef.waitForInitialized();
refFrom.addReference(paramRef, param, decl, false, true, node);
}
}
}
private void processFunctionalExpression(@NotNull UExpression expression, @Nullable PsiType type) {
PsiElement aClass = PsiUtil.resolveClassInType(type);
if (aClass != null) {
@@ -278,11 +294,14 @@ public class RefJavaUtilImpl extends RefJavaUtil {
final RefElement refWhat = refFrom.getRefManager().getReference(aClass);
if (refWhat != null) refWhat.waitForInitialized();
refFrom.addReference(refWhat, aClass, decl, false, true, null);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(type);
if (interfaceMethod != null) {
RefElement interfaceMethodRef = refFrom.getRefManager().getReference(interfaceMethod);
if (interfaceMethodRef != null) interfaceMethodRef.waitForInitialized();
refFrom.addReference(interfaceMethodRef, interfaceMethod, decl, false, true, null);
refFrom.getRefManager().fireNodeMarkedReferenced(interfaceMethod, expression.getSourcePsi());
}
}
PsiElement functionalExpr = expression.getSourcePsi();
RefElement refFunctionalExpr = refFrom.getRefManager().getReference(functionalExpr);
if (refFunctionalExpr != null) refFunctionalExpr.waitForInitialized();
refFrom.addReference(refFunctionalExpr, functionalExpr, decl, false, true, expression);
}
@Nullable
@@ -397,6 +416,27 @@ public class RefJavaUtilImpl extends RefJavaUtil {
}
}
private static void addReferenceToLambdaParameter(@NotNull UParameter uParam, @NotNull PsiElement param, @NotNull UElement decl,
@NotNull RefJavaElementImpl refFrom) {
ULambdaExpression lambda = UastUtils.getParentOfType(uParam, ULambdaExpression.class);
if (lambda == null) return;
int paramIndex = -1;
List<UParameter> lambdaParams = lambda.getParameters();
for (int i = 0; i < lambdaParams.size(); i++) {
if (lambdaParams.get(i).equals(uParam)) {
paramIndex = i;
break;
}
}
if (paramIndex == -1) return;
RefElement method = refFrom.getRefManager().getReference(LambdaUtil.getFunctionalInterfaceMethod(lambda.getFunctionalInterfaceType()));
if (method instanceof RefMethod) {
method.waitForInitialized();
RefParameter[] methodParams = ((RefMethod)method).getParameters();
refFrom.addReference(methodParams[paramIndex], param, decl, false, true, null);
}
}
private static void addClassReferenceForStaticImport(UExpression node,
PsiMember psiResolved,
RefJavaElementImpl refFrom, UElement decl) {
@@ -59,6 +59,30 @@ public final class RedundantThrowsGraphAnnotator extends RefGraphAnnotatorEx {
}
}
@Override
public void onMarkReferenced(PsiElement what, PsiElement from, boolean referencedFromClassInitializer) {
if (from instanceof PsiFunctionalExpression) {
RefElement refResolved = myRefManager.getReference(what);
if (refResolved instanceof RefMethodImpl) {
PsiFunctionalExpression expression = (PsiFunctionalExpression)from;
final Collection<PsiClassType> exceptionTypes;
if (expression instanceof PsiLambdaExpression) {
PsiElement body = ((PsiLambdaExpression)expression).getBody();
exceptionTypes = body != null ? ExceptionUtil.collectUnhandledExceptions(body, expression, false) : Collections.emptyList();
}
else {
final PsiElement resolve = ((PsiMethodReferenceExpression)expression).resolve();
exceptionTypes = resolve instanceof PsiMethod
? Arrays.asList(((PsiMethod)resolve).getThrowsList().getReferencedTypes())
: Collections.emptyList();
}
for (final PsiClassType exceptionType : exceptionTypes) {
((RefMethodImpl)refResolved).updateThrowsList(exceptionType);
}
}
}
}
public static Set<PsiClassType> getUnhandledExceptions(PsiCodeBlock body, PsiMethod method, PsiClass containingClass) {
Collection<PsiClassType> types = ExceptionUtil.collectUnhandledExceptions(body, method, false);
Set<PsiClassType> unhandled = new HashSet<>(types);
@@ -262,18 +262,18 @@ public final class UnusedDeclarationInspection extends UnusedDeclarationInspecti
}
}
}
else if (refElement instanceof RefField) {
UField field = ((RefField)refElement).getUastElement();
if (field != null) {
UExpression initializer = field.getUastInitializer();
if (initializer != null) {
initializer = UastUtils.skipParenthesizedExprDown(initializer);
if (initializer instanceof ULambdaExpression) {
findUnusedLocalVariables(((ULambdaExpression)initializer).getBody(), refElement);
}
}
}
}
//else if (refElement instanceof RefField) {
// UField field = ((RefField)refElement).getUastElement();
// if (field != null) {
// UExpression initializer = field.getUastInitializer();
// if (initializer != null) {
// initializer = UastUtils.skipParenthesizedExprDown(initializer);
// if (initializer instanceof ULambdaExpression) {
// findUnusedLocalVariables(((ULambdaExpression)initializer).getBody(), refElement);
// }
// }
// }
//}
}
private void findUnusedLocalVariables(UExpression body, RefElement refElement) {
@@ -735,7 +735,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
for (PsiMethod method : methods) {
final PsiClass containingClass = method.getContainingClass();
FunctionalExpressionSearch.search(method).forEach(expression -> {
usages.add(new SafeDeleteFunctionalExpressionUsageInfo(expression, containingClass, isMethodUsage));
usages.add(new SafeDeleteFunctionalExpressionUsageInfo(expression, containingClass, false));
return true;
});
}
@@ -1,15 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Lambda_UnusedMethod.java</file>
<line>12</line>
<description>Method is never used.</description>
</problem>
<problem>
<file>MethodRef_UnusedMethod.java</file>
<line>12</line>
<description>Method is never used.</description>
</problem>
<problem>
<file>Lambda_UnusedMethodInHierarchy.java</file>
<line>12</line>
@@ -1,14 +1,10 @@
public class Test {
public static void main(String[] args) {
final Memento m = () -> isPlaying();
final Memento m = () -> isPlaying;
System.out.println(m);
}
private static boolean isPlaying() {
return false;
}
public static interface Memento {
boolean isPlaying();
}
}
}
@@ -2,105 +2,8 @@
<problems>
<problem>
<file>A.java</file>
<line>5</line>
<line>3</line>
<description>Variable &lt;code&gt;i&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>7</line>
<description>Variable &lt;code&gt;k&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>10</line>
<description>Variable &lt;code&gt;unusedStr1&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>13</line>
<description>Variable &lt;code&gt;unusedStr3&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>18</line>
<description>Variable &lt;code&gt;unusedStr5&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>21</line>
<description>Variable &lt;code&gt;unusedStr7&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>26</line>
<description>Variable &lt;code&gt;unusedStr8&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>27</line>
<description>Variable &lt;code&gt;unusedStr9&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>34</line>
<description>Variable &lt;code&gt;unusedStr10&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>A.java</file>
<line>35</line>
<description>Variable &lt;code&gt;unusedStr11&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>Lambda.java</file>
<line>6</line>
<description>Field has no usages.</description>
</problem>
<problem>
<file>Lambda.java</file>
<line>7</line>
<description>Variable &lt;code&gt;i1&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>Lambda.java</file>
<line>12</line>
<description>Variable &lt;code&gt;i3&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>Lambda.java</file>
<line>24</line>
<description>Variable &lt;code&gt;i1&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>Lambda.java</file>
<line>29</line>
<description>Variable &lt;code&gt;i3&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>AnonymousClass.java</file>
<line>6</line>
<description>Field has no usages.</description>
</problem>
<problem>
<file>AnonymousClass.java</file>
<line>10</line>
<description>Variable &lt;code&gt;i1&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>AnonymousClass.java</file>
<line>21</line>
<description>Variable &lt;code&gt;i3&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>AnonymousClass.java</file>
<line>39</line>
<description>Variable &lt;code&gt;i1&lt;/code&gt; is never used</description>
</problem>
<problem>
<file>AnonymousClass.java</file>
<line>50</line>
<description>Variable &lt;code&gt;i3&lt;/code&gt; is never used</description>
</problem>
</problems>
@@ -1,51 +1,7 @@
import java.util.*;
class A {
public static void main(String[] args) {
int i = 0;
@SuppressWarnings("unused") int j = 0;
int k;
initializeCons(value -> {
String unusedStr1 = "s";
@SuppressWarnings("unused") String unusedStr2 = "s";
{
String unusedStr3;
}
String unusedStr4 = "str";
initializeCons(innerValue1 -> {
{
String unusedStr5 = unusedStr4;
}
String unusedStr6 = "s";
String unusedStr7;
initializeSup(() -> unusedStr6);
class Local {
void test() {
String unusedStr8;
String unusedStr9 = "Str";
}
}
new Local().test();
Local o = new Local() {
void test() {
String unusedStr10;
String unusedStr11 = "Str";
}
}
System.out.println(o);
});
});
}
private static void initializeCons(Consumer<String> consumer) {
consumer.accept("String to test");
}
private static void initializeSup(Supplier<String> supplier) {
supplier.get();
}
}
@@ -1,67 +0,0 @@
interface Unused {
boolean test();
}
class Test {
Unused lambda1 = new Unused() {
@java.lang.Override
public boolean test() {
int i1 = 1;
Unused lambda2 = new Unused() {
@java.lang.Override
public boolean test() {
int i2 = 1;
Unused lambda3 = new Unused() {
@java.lang.Override
public boolean test() {
System.out.println(i2);
int i3 = 1;
return true;
}
}
System.out.println(lambda3);
return false;
}
};
System.out.println(lambda2);
return false;
}
};
public static void main(String[] args) {
bar(new Unused() {
@java.lang.Override
public boolean test() {
int i1 = 1;
Unused lambda2 = new Unused() {
@java.lang.Override
public boolean test() {
int i2 = 1;
Unused lambda3 = new Unused() {
@java.lang.Override
public boolean test() {
System.out.println(i2);
int i3 = 1;
return true;
}
}
System.out.println(lambda3);
return false;
}
};
System.out.println(lambda2);
return false;
}
});
}
static void bar(Unused unused) {
unused.test();
}
}
@@ -1,43 +0,0 @@
interface Unused {
boolean test();
}
class Test {
Unused lambda1 = () -> {
int i1 = 1;
Unused lambda2 = () -> {
int i2 = 1;
Unused lambda3 = () -> {
System.out.println(i2);
int i3 = 1;
return true;
}
System.out.println(lambda3);
return false;
};
System.out.println(lambda2);
return false;
};
public static void main(String[] args) {
bar(() -> {
int i1 = 1;
Unused lambda2 = () -> {
int i2 = 1;
Unused lambda3 = () -> {
System.out.println(i2);
int i3 = 1;
return true;
}
System.out.println(lambda3);
return false;
};
System.out.println(lambda2);
return false;
});
}
static void bar(Unused unused) {
unused.test();
}
}
@@ -1,4 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Test.java</file>
<line>2</line>
<description>All implementations of this method are empty</description>
</problem>
</problems>
@@ -12,11 +12,5 @@
<problem_class>Method returns the same value</problem_class>
<description>Method always returns &lt;code&gt;0&lt;/code&gt;</description>
</problem>
<problem>
<file>LambdaWithSameValue.java</file>
<line>2</line>
<problem_class>Method returns the same value</problem_class>
<description>All implementations of this method always return &lt;code&gt;42&lt;/code&gt;</description>
</problem>
</problems>
@@ -3,10 +3,16 @@ interface ILambdaTest {
}
class LambdaTest implements ILambdaTest {
ILambdaTest lambda = this::getResult;
@Override
public int getResult() {
return 42;
}
}
class LamdaTest2 implements ILambdaTest {
@Override
public int getResult() {
return 43;
}
}
@@ -235,8 +235,15 @@ public class SafeDeleteTest extends MultiFileTestCase {
}
public void testFunctionalInterfaceMethod() throws Exception {
LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_8);
doSingleFileTest();
try {
LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_8);
doSingleFileTest();
fail("Conflict was not detected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
String message = e.getMessage();
assertEquals("interface <b><code>SAM</code></b> has 1 usage that is not safe to delete.", message);
}
}
public void testAmbiguityAfterParameterDelete() throws Exception {
@@ -1,7 +1,2 @@
<problems>
<problem>
<file>Lambda.java</file>
<line>2</line>
<description>Method &lt;code&gt;foo()&lt;/code&gt; and all its derivables always return constants</description>
</problem>
</problems>