Java: handle @Repeatable annotations better in “Unused declaration” inspection

GitOrigin-RevId: 677f2c52ac069497147398dce1b42709e78ff445
This commit is contained in:
Bas Leijdekkers
2024-05-09 12:47:21 +00:00
committed by intellij-monorepo-bot
parent 33881c73d1
commit 153827d95f
3 changed files with 23 additions and 37 deletions
@@ -4,7 +4,6 @@ package com.intellij.codeInspection.deadCode;
import com.intellij.analysis.AnalysisBundle;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.analysis.AnnotationsHighlightUtil;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.EntryPointsManager;
import com.intellij.codeInspection.ex.EntryPointsManagerBase;
@@ -27,7 +26,6 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiMethodUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.uast.UastMetaLanguage;
import com.intellij.util.containers.Stack;
import org.jdom.Element;
@@ -345,7 +343,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
LOG.assertTrue(processedSuspicious != null, "phase: " + phase);
Collection<Language> uastLanguages = Language.findInstance(UastMetaLanguage.class).getMatchingLanguages();
boolean[] requestAdded = {false};
globalContext.getRefManager().iterate(new RefJavaVisitor() {
@Override
public void visitField(@NotNull RefField refField) {
@@ -362,7 +359,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
getEntryPointsManager(globalContext).addEntryPoint(refField, false);
return false;
});
requestAdded[0] = true;
}
}
@@ -384,15 +380,12 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
}
}
UMethod uMethod = refMethod.getUastElement();
if (uMethod != null && (RefSerializationUtil.isSerializationMethod(uMethod, refMethod.getOwnerClass()) ||
// todo this method potentially leads to INRE. Perhaps, it should be reconsidered/deleted (IJ-CR-5556)
belongsToRepeatableAnnotationContainer(uMethod, refMethod.getOwnerClass()))) {
if (uMethod != null && (RefSerializationUtil.isSerializationMethod(uMethod, refMethod.getOwnerClass()))) {
getEntryPointsManager(globalContext).addEntryPoint(refMethod, false);
}
else if (!refMethod.isExternalOverride() && !PsiModifier.PRIVATE.equals(refMethod.getAccessModifier())) {
processedSuspicious.addAll(refMethod.getDerivedMethods());
enqueueMethodUsages(globalContext, refMethod);
requestAdded[0] = true;
}
}
@@ -419,7 +412,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
});
queryQualifiedNameUsages(refClass);
requestAdded[0] = true;
}
}
@@ -440,7 +432,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
getEntryPointsManager(globalContext).addEntryPoint(refClass, false);
}
}
requestAdded[0] = true;
}
}
});
@@ -449,22 +440,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
return true;
}
private static boolean belongsToRepeatableAnnotationContainer(@NotNull UMethod uMethod, @Nullable RefClass ownerRefClass) {
if (ownerRefClass == null) return false;
if (!PsiUtil.isLanguageLevel8OrHigher(uMethod.getJavaPsi())) return false;
if (!"value".equals(uMethod.getName())) return false;
if (!ownerRefClass.isAnnotationType()) return false;
PsiType returnType = uMethod.getReturnType();
if (!(returnType instanceof PsiArrayType)) return false;
PsiClass returnTypeClass = PsiUtil.resolveClassInType(returnType);
if (returnTypeClass == null || !returnTypeClass.isAnnotationType()) return false;
RefElement repeatableAnn = ownerRefClass.getRefManager().getReference(returnTypeClass);
if (repeatableAnn == null || !repeatableAnn.isReferenced()) return false;
PsiAnnotation repeatableAnnContainer = returnTypeClass.getAnnotation(CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE);
if (repeatableAnnContainer == null) return false;
return AnnotationsHighlightUtil.doCheckRepeatableAnnotation(repeatableAnnContainer) == null;
}
private static void enqueueMethodUsages(@NotNull GlobalInspectionContext globalContext, @NotNull RefMethod refMethod) {
if (refMethod.getSuperMethods().isEmpty()) {
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueMethodUsagesProcessor(refMethod, psiReference -> {
@@ -75,10 +75,32 @@ public final class RefJavaUtilImpl extends RefJavaUtil {
final RefElement refClass = refManager.getReference(javaClass.getOriginalElement());
if (refClass != null) refClass.initializeIfNeeded();
refFrom.addReference(refClass, javaClass.getOriginalElement(), decl, false, true, null);
checkRepeatableAnnotation(javaClass, node);
}
return false;
}
private void checkRepeatableAnnotation(PsiClass annotationClass, UAnnotation annotation) {
if (!CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE.equals(annotationClass.getQualifiedName())) return;
UExpression value = annotation.findAttributeValue("value");
while (value instanceof UParenthesizedExpression parenth) {
value = parenth.getExpression();
}
if (!(value instanceof UClassLiteralExpression classLiteralExpression)) return;
PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(classLiteralExpression.getType());
if (aClass == null) return;
RefClassImpl refClass =
ObjectUtils.tryCast(refManager.getReference(aClass.getOriginalElement()), RefClassImpl.class);
if (refClass == null) return;
refClass.initializeIfNeeded();
for (RefEntity child : refClass.getChildren()) {
if (child instanceof RefMethod method && "value()".equals(method.getName())) {
refFrom.addReference(method, null, decl, false, true, null);
break;
}
}
}
@Override
public boolean visitTypeReferenceExpression(@NotNull UTypeReferenceExpression node) {
PsiType type = node.getType();
@@ -232,7 +254,6 @@ public final class RefJavaUtilImpl extends RefJavaUtil {
boolean writing = isAccessedForWriting(node);
boolean reading = isAccessedForReading(node);
if (refResolved != null) refResolved.initializeIfNeeded();
refFrom.initializeIfNeeded();
refFrom.addReference(refResolved, psiResolved, decl, writing, reading, node);
if (refResolved instanceof RefMethodImpl refMethod) {
@@ -10,16 +10,6 @@
<line>5</line>
<description>Method owner class is never instantiated OR An instantiation is not reachable from entry points.</description>
</problem>
<problem>
<file>RepeatableAnnotation.java</file>
<line>12</line>
<description>Method is never used.</description>
</problem>
<problem>
<file>RepeatableAnnotation.java</file>
<line>21</line>
<description>Method owner class is never instantiated OR An instantiation is not reachable from entry points.</description>
</problem>
<problem>
<file>RepeatableAnnotation.java</file>
<line>25</line>