From 2c22ee32a740ecd6b54e8359b4c665ea3289f1f5 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 17 Dec 2019 17:51:36 +0700 Subject: [PATCH] IDEA-229320 Heap pollution warning for records GitOrigin-RevId: 1e870db7c171c02af5d01c74ca631ab2dafe43ef --- ...ossibleHeapPollutionVarargsInspection.java | 75 +++++++++++++------ .../quickFix/safeVarargs/afterRecord.java | 5 ++ .../quickFix/safeVarargs/beforeRecord.java | 4 + 3 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/afterRecord.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/beforeRecord.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/PossibleHeapPollutionVarargsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/PossibleHeapPollutionVarargsInspection.java index ce2b92466c72..20b5cb1e5141 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/PossibleHeapPollutionVarargsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/PossibleHeapPollutionVarargsInspection.java @@ -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); + } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/afterRecord.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/afterRecord.java new file mode 100644 index 000000000000..189564d9f1a0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/afterRecord.java @@ -0,0 +1,5 @@ +// "Annotate as @SafeVarargs" "true" +@SafeVarargs +public record Test(java.util.List... args) { +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/beforeRecord.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/beforeRecord.java new file mode 100644 index 000000000000..bd2b66ed45d2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/safeVarargs/beforeRecord.java @@ -0,0 +1,4 @@ +// "Annotate as @SafeVarargs" "true" +public record Test(java.util.List... args) { +} +