IDEA-229320 Heap pollution warning for records

GitOrigin-RevId: 1e870db7c171c02af5d01c74ca631ab2dafe43ef
This commit is contained in:
Tagir Valeev
2019-12-17 11:02:18 +00:00
committed by intellij-monorepo-bot
parent 89d4f6f422
commit 2c22ee32a7
3 changed files with 63 additions and 21 deletions
@@ -14,6 +14,8 @@ import com.intellij.psi.*;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,6 +40,7 @@ public class PossibleHeapPollutionVarargsInspection extends AbstractBaseJavaLoca
return "SafeVarargsDetector";
}
@Pattern(VALID_ID_PATTERN)
@NotNull
@Override
public String getID() {
@@ -47,24 +50,7 @@ public class PossibleHeapPollutionVarargsInspection extends AbstractBaseJavaLoca
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new HeapPollutionVisitor() {
@Override
protected void registerProblem(PsiMethod method, PsiIdentifier nameIdentifier) {
final LocalQuickFix quickFix;
if (GenericsHighlightUtil.isSafeVarargsNoOverridingCondition(method, PsiUtil.getLanguageLevel(method))) {
quickFix = new AnnotateAsSafeVarargsQuickFix();
}
else {
final PsiClass containingClass = method.getContainingClass();
LOG.assertTrue(containingClass != null);
boolean canBeFinal = !method.hasModifierProperty(PsiModifier.ABSTRACT) &&
!containingClass.isInterface() &&
OverridingMethodsSearch.search(method).findFirst() == null;
quickFix = canBeFinal ? new MakeFinalAndAnnotateQuickFix() : null;
}
holder.registerProblem(nameIdentifier, "Possible heap pollution from parameterized vararg type #loc", quickFix);
}
};
return new HeapPollutionVisitor(holder);
}
private static class AnnotateAsSafeVarargsQuickFix implements LocalQuickFix {
@@ -83,7 +69,7 @@ public class PossibleHeapPollutionVarargsInspection extends AbstractBaseJavaLoca
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement psiElement = descriptor.getPsiElement();
if (psiElement instanceof PsiIdentifier) {
final PsiMethod psiMethod = (PsiMethod)psiElement.getParent();
final PsiModifierListOwner psiMethod = (PsiModifierListOwner)psiElement.getParent();
if (psiMethod != null) {
new AddAnnotationPsiFix(CommonClassNames.JAVA_LANG_SAFE_VARARGS, psiMethod, PsiNameValuePair.EMPTY_ARRAY).applyFix(project, descriptor);
}
@@ -120,7 +106,13 @@ public class PossibleHeapPollutionVarargsInspection extends AbstractBaseJavaLoca
}
}
public abstract static class HeapPollutionVisitor extends JavaElementVisitor {
public static class HeapPollutionVisitor extends JavaElementVisitor {
private final ProblemsHolder myHolder;
public HeapPollutionVisitor(ProblemsHolder holder) {
myHolder = holder;
}
@Override
public void visitMethod(PsiMethod method) {
super.visitMethod(method);
@@ -132,6 +124,33 @@ public class PossibleHeapPollutionVarargsInspection extends AbstractBaseJavaLoca
final PsiParameter psiParameter = parameters[parameters.length - 1];
if (!psiParameter.isVarArgs()) return;
checkForHeapPollution(method, psiParameter);
}
@Override
public void visitClass(PsiClass aClass) {
super.visitClass(aClass);
if (!aClass.isRecord()) return;
if (AnnotationUtil.isAnnotated(aClass, CommonClassNames.JAVA_LANG_SAFE_VARARGS, 0)) return;
PsiRecordHeader header = aClass.getRecordHeader();
if (header == null) return;
PsiRecordComponent lastComponent = ArrayUtil.getLastElement(header.getRecordComponents());
if (lastComponent == null || !lastComponent.isVarArgs()) return;
final PsiType type = lastComponent.getType();
LOG.assertTrue(type instanceof PsiEllipsisType, "type: " + type.getCanonicalText() + "; param: " + lastComponent);
final PsiType componentType = ((PsiEllipsisType)type).getComponentType();
if (JavaGenericsUtil.isReifiableType(componentType)) {
return;
}
final PsiElement nameIdentifier = ((PsiNameIdentifierOwner)aClass).getNameIdentifier();
if (nameIdentifier != null) {
final LocalQuickFix quickFix = new AnnotateAsSafeVarargsQuickFix();
myHolder.registerProblem(nameIdentifier, "Possible heap pollution from parameterized vararg type #loc", quickFix);
}
}
private void checkForHeapPollution(PsiMethod method, PsiVariable psiParameter) {
final PsiType type = psiParameter.getType();
LOG.assertTrue(type instanceof PsiEllipsisType, "type: " + type.getCanonicalText() + "; param: " + psiParameter);
@@ -154,6 +173,20 @@ public class PossibleHeapPollutionVarargsInspection extends AbstractBaseJavaLoca
}
}
protected abstract void registerProblem(PsiMethod method, PsiIdentifier nameIdentifier);
protected void registerProblem(PsiMethod method, PsiIdentifier nameIdentifier) {
final LocalQuickFix quickFix;
if (GenericsHighlightUtil.isSafeVarargsNoOverridingCondition(method, PsiUtil.getLanguageLevel(method))) {
quickFix = new AnnotateAsSafeVarargsQuickFix();
}
else {
final PsiClass containingClass = method.getContainingClass();
LOG.assertTrue(containingClass != null);
boolean canBeFinal = !method.hasModifierProperty(PsiModifier.ABSTRACT) &&
!containingClass.isInterface() &&
OverridingMethodsSearch.search(method).findFirst() == null;
quickFix = canBeFinal ? new MakeFinalAndAnnotateQuickFix() : null;
}
myHolder.registerProblem(nameIdentifier, "Possible heap pollution from parameterized vararg type #loc", quickFix);
}
}
}
@@ -0,0 +1,5 @@
// "Annotate as @SafeVarargs" "true"
@SafeVarargs
public record Test(java.util.List<String>... args) {
}
@@ -0,0 +1,4 @@
// "Annotate as @SafeVarargs" "true"
public record T<caret>est(java.util.List<String>... args) {
}