diff --git a/java/java-analysis-impl/src/META-INF/JavaAnalysisPlugin.xml b/java/java-analysis-impl/src/META-INF/JavaAnalysisPlugin.xml index 540b98060310..21e9f308c395 100644 --- a/java/java-analysis-impl/src/META-INF/JavaAnalysisPlugin.xml +++ b/java/java-analysis-impl/src/META-INF/JavaAnalysisPlugin.xml @@ -155,9 +155,6 @@ - -1) { myID = id.substring(0, idx); - myProblemLine = id.substring(idx); } else { myID = id; - myProblemLine = null; } } @@ -72,7 +63,7 @@ public class RemoveSuppressWarningAction implements LocalQuickFix { PsiElement element = descriptor.getPsiElement(); try { if (element != null) { - final PsiJavaDocumentedElement commentOwner = PsiTreeUtil.getParentOfType(element, PsiJavaDocumentedElement.class); + final PsiJavaDocumentedElement commentOwner = PsiTreeUtil.getParentOfType(element, PsiJavaDocumentedElement.class, false); if (commentOwner != null) { final PsiElement psiElement = JavaSuppressionUtil.getElementMemberSuppressedIn(commentOwner, myID); if (psiElement instanceof PsiAnnotation) { @@ -113,10 +104,6 @@ public class RemoveSuppressWarningAction implements LocalQuickFix { } private void removeFromComment(final PsiComment comment, final boolean checkLine) throws IncorrectOperationException { - if (checkLine) { - final PsiStatement statement = PsiTreeUtil.getNextSiblingOfType(comment, PsiStatement.class); - if (statement != null && !Comparing.strEqual(statement.getText(), myProblemLine)) return; - } String newText = removeFromElementText(comment); if (newText != null) { if (newText.isEmpty()) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SuppressManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SuppressManagerImpl.java index 3cf304bb107a..b03e2a67ff90 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/SuppressManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SuppressManagerImpl.java @@ -3,13 +3,15 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInsight.daemon.impl.RemoveSuppressWarningAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiDocCommentOwner; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class SuppressManagerImpl extends SuppressManager { +public class SuppressManagerImpl extends SuppressManager implements RedundantSuppressionDetector { private static final Logger LOG = Logger.getInstance(SuppressManager.class); @Override @@ -53,4 +55,20 @@ public class SuppressManagerImpl extends SuppressManager { public boolean alreadyHas14Suppressions(@NotNull final PsiDocCommentOwner commentOwner) { return JavaSuppressionUtil.alreadyHas14Suppressions(commentOwner); } + + @Override + public String getSuppressionIds(@NotNull PsiElement element) { + return JavaSuppressionUtil.getSuppressedInspectionIdsIn(element); + } + + @Override + public boolean isSuppressionFor(@NotNull PsiElement elementWithSuppression, @NotNull PsiElement place, @NotNull String toolId) { + PsiElement suppressionScope = JavaSuppressionUtil.getElementToolSuppressedIn(place, toolId); + return suppressionScope != null && PsiTreeUtil.isAncestor(elementWithSuppression, suppressionScope, false); + } + + @Override + public LocalQuickFix createRemoveRedundantSuppressionFix(@NotNull String toolId) { + return new RemoveSuppressWarningAction(toolId); + } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before1.java index 825706ca2b75..601fcbd95354 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before1.java @@ -7,7 +7,7 @@ public class Test { } void foo() { - //noinspection unchecked + //noinspection unchecked foo(new ArrayList()); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before2.java index d8436646d0dc..ea4f460f44ac 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before2.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before2.java @@ -7,7 +7,7 @@ public class Test { } void foo() { - //noinspection unchecked,blah-blah-toolid + //noinspection unchecked,blah-blah-toolid foo(new ArrayList()); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before3.java index 61a94caa32b5..c6ef8991cc9e 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before3.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before3.java @@ -12,7 +12,7 @@ public class Test { } void foo() { - //noinspection unchecked + //noinspection unchecked foo(new ArrayList()).addAll(foo1(new ArrayList())); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before4.java index f408873bf4dd..d3b9aeef5b4d 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before4.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before4.java @@ -8,7 +8,7 @@ public class Test { } void foo() { - //noinspection unchecked + //noinspection unchecked List> list = foo(new ArrayList()); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before5.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before5.java index b294d2d5a2df..a787de0a33d2 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before5.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before5.java @@ -7,8 +7,8 @@ public class Test { return null; } - @SuppressWarnings("unchecked") - void foo() { + @SuppressWarnings("unchecked") + void foo() { List> list = foo(new ArrayList()); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before6.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before6.java index 60f4268bf047..92c9bdea1298 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before6.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before6.java @@ -1,8 +1,8 @@ // "Remove 'unchecked' suppression" "true" import java.util.*; -@SuppressWarnings("unchecked") -public class Test { +@SuppressWarnings("unchecked") +public class Test { @SafeVarargs static List foo(T... t){ return null; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before9.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before9.java index e34ee0e8c198..0af466c7a84e 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before9.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before9.java @@ -1,8 +1,8 @@ // "Remove 'unchecked' suppression" "true" import java.util.*; -@SuppressWarnings({"unchecked", "bla-blah-toolid"}) -public class Test { +@SuppressWarnings({"unchecked", "bla-blah-toolid"}) +public class Test { @SafeVarargs static List foo(T... t){ return null; diff --git a/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/expected.xml b/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/expected.xml index 9bc83e9c78ed..42e08b9f9adf 100644 --- a/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/expected.xml +++ b/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/expected.xml @@ -4,7 +4,6 @@ module-info.java 5 - Redundant suppression Redundant suppression diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java index 4e3b3a1d46bd..19a899a3eec4 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java @@ -16,84 +16,17 @@ package com.intellij.java.codeInsight.daemon.quickFix; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; -import com.intellij.codeInspection.*; -import com.intellij.codeInspection.ex.InspectionToolWrapper; -import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; +import com.intellij.codeInspection.PossibleHeapPollutionVarargsInspection; +import com.intellij.codeInspection.RedundantSuppressInspection; import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection; -import com.intellij.psi.*; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; public class RemoveRedundantUncheckedSuppressionTest extends LightQuickFixParameterizedTestCase { - @NotNull + @Override - protected LocalInspectionTool[] configureLocalInspectionTools() { - final PossibleHeapPollutionVarargsInspection varargsInspection = new PossibleHeapPollutionVarargsInspection(); - final UncheckedWarningLocalInspection warningLocalInspection = new UncheckedWarningLocalInspection(); - final RedundantSuppressInspection inspection = new RedundantSuppressInspection(){ - @NotNull - @Override - protected InspectionToolWrapper[] getInspectionTools(PsiElement psiElement, @NotNull InspectionManager manager) { - return new InspectionToolWrapper[]{ - new LocalInspectionToolWrapper(varargsInspection), - new LocalInspectionToolWrapper(warningLocalInspection) - }; - } - }; - - return new LocalInspectionTool[] { - new LocalInspectionTool() { - @Nls - @NotNull - @Override - public String getGroupDisplayName() { - return inspection.getGroupDisplayName(); - } - - @Nls - @NotNull - @Override - public String getDisplayName() { - return inspection.getDisplayName(); - } - - @NotNull - @Override - public String getShortName() { - return inspection.getShortName(); - } - - @NotNull - @Override - public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, - boolean isOnTheFly, - @NotNull LocalInspectionToolSession session) { - return new JavaElementVisitor() { - @Override - public void visitClass(PsiClass aClass) { - checkMember(aClass, inspection, holder); - } - - @Override - public void visitMethod(PsiMethod method) { - checkMember(method, inspection, holder); - } - }; - } - - private void checkMember(PsiMember member, RedundantSuppressInspection inspection, ProblemsHolder holder) { - final ProblemDescriptor[] problemDescriptors = inspection.checkElement(member, InspectionManager.getInstance(getProject())); - if (problemDescriptors != null) { - for (ProblemDescriptor problemDescriptor : problemDescriptors) { - holder.registerProblem(problemDescriptor); - } - } - } - }, - varargsInspection, - warningLocalInspection - }; + protected void setUp() throws Exception { + super.setUp(); + enableInspectionTools(new RedundantSuppressInspection(), new PossibleHeapPollutionVarargsInspection(), new UncheckedWarningLocalInspection()); } @Override diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/RedundantSuppressTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/RedundantSuppressTest.java index f95a66380153..ce6871db7105 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/RedundantSuppressTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/RedundantSuppressTest.java @@ -63,7 +63,7 @@ public class RedundantSuppressTest extends InspectionTestCase { } public void testModuleInfo() { - doTest(); + doTest("redundantSuppress/" + getTestName(true), myWrapper,"java 1.5",false); } public void testDefaultFile() { diff --git a/platform/analysis-api/src/com/intellij/codeInspection/RedundantSuppressionDetector.java b/platform/analysis-api/src/com/intellij/codeInspection/RedundantSuppressionDetector.java new file mode 100644 index 000000000000..3d9b22ec200a --- /dev/null +++ b/platform/analysis-api/src/com/intellij/codeInspection/RedundantSuppressionDetector.java @@ -0,0 +1,42 @@ +// 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. +package com.intellij.codeInspection; + + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + + +public interface RedundantSuppressionDetector { + + /** + * @return comma separated list of suppress ids configured in this {@code element} + */ + @Nullable + String getSuppressionIds(@NotNull PsiElement element); + + /** + * @return quickfix to remove {@code toolId} suppression from list of suppressions + */ + @Nullable + LocalQuickFix createRemoveRedundantSuppressionFix(@NotNull String toolId); + + /** + * @param elementWithSuppression e.g. comment or @SuppressWarning annotation + * @param place element with currently suppressed warning + * @return true if {@code place} is suppressed by {@code elementWithSuppression} + */ + boolean isSuppressionFor(@NotNull PsiElement elementWithSuppression, @NotNull PsiElement place, @NotNull String toolId); + + /** + * @return range with {@code toolId} to highlight in the editor + */ + default TextRange getHighlightingRange(PsiElement elementWithSuppression, String toolId) { + String suppressionElementText = elementWithSuppression.getText(); + int idx = suppressionElementText.indexOf(toolId); + return idx > 0 + ? new TextRange(idx, idx + toolId.length()) + : new TextRange(0, suppressionElementText.length()); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java index 926c90a678ae..33dc5223a529 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LocalInspectionsPass.java @@ -58,6 +58,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; +import java.util.stream.Collectors; /** * @author max @@ -74,6 +75,7 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass private final String myShortcutText; private final SeverityRegistrar mySeverityRegistrar; private final InspectionProfileWrapper myProfileWrapper; + private final Map> mySuppressedElements = new HashMap<>(); public LocalInspectionsPass(@NotNull PsiFile file, @Nullable Document document, @@ -224,6 +226,40 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass myInfos = new ArrayList<>(); addHighlightsFromResults(myInfos); + + if (isOnTheFly) highlightRedundantSuppressions(toolWrappers, iManager, inside, outside, elementDialectIds); + } + + private void highlightRedundantSuppressions(@NotNull List toolWrappers, + @NotNull InspectionManager iManager, + List inside, + List outside, + Set elementDialectIds) { + HighlightDisplayKey key = HighlightDisplayKey.find(RedundantSuppressInspection.SHORT_NAME); + final InspectionProfileImpl inspectionProfile = myProfileWrapper.getInspectionProfile(); + if (key != null && inspectionProfile.isToolEnabled(key, getFile())) { + InspectionToolWrapper toolWrapper = inspectionProfile.getToolById(RedundantSuppressInspection.SHORT_NAME, getFile()); + InspectionSuppressor suppressor = LanguageInspectionSuppressors.INSTANCE.forLanguage(getFile().getLanguage()); + if (suppressor instanceof RedundantSuppressionDetector) { + Set activeTools = toolWrappers.stream().map(tool -> tool.getID()).collect(Collectors.toSet()); + LocalInspectionTool + localTool = ((RedundantSuppressInspection)toolWrapper.getTool()).createLocalTool((RedundantSuppressionDetector)suppressor, mySuppressedElements, activeTools); + ProblemsHolder holder = new ProblemsHolder(iManager, getFile(), true); + PsiElementVisitor visitor = localTool.buildVisitor(holder, true); + InspectionEngine.acceptElements(inside, visitor, elementDialectIds, null); + InspectionEngine.acceptElements(outside, visitor, elementDialectIds, null); + + HighlightSeverity severity = myProfileWrapper.getErrorLevel(key, getFile()).getSeverity(); + for (ProblemDescriptor descriptor : holder.getResults()) { + ProgressManager.checkCanceled(); + PsiElement element = descriptor.getPsiElement(); + if (element != null) { + Document thisDocument = documentManager.getDocument(getFile()); + createHighlightsForDescriptor(myInfos, emptyActionRegistered, ilManager, getFile(), thisDocument, new LocalInspectionToolWrapper(localTool), severity, descriptor, element); + } + } + } + } } @NotNull @@ -473,7 +509,10 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass @NotNull ProblemDescriptor descriptor, @NotNull PsiElement element) { LocalInspectionTool tool = toolWrapper.getTool(); - if (myIgnoreSuppressed && SuppressionUtil.inspectionResultSuppressed(element, tool)) return; + if (myIgnoreSuppressed && SuppressionUtil.inspectionResultSuppressed(element, tool)) { + mySuppressedElements.computeIfAbsent(toolWrapper.getID(), shortName -> new HashSet<>()).add(element); + return; + } HighlightInfoType level = ProblemDescriptorUtil.highlightTypeFromDescriptor(descriptor, severity, mySeverityRegistrar); @NonNls String message = ProblemDescriptorUtil.renderDescriptionMessage(descriptor, element); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java b/platform/lang-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java similarity index 63% rename from java/java-analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java rename to platform/lang-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java index 4a760ed6adc6..6b72734c178f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/RedundantSuppressInspection.java @@ -1,37 +1,24 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// 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. package com.intellij.codeInspection; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInsight.daemon.GroupNames; -import com.intellij.codeInsight.daemon.impl.RemoveSuppressWarningAction; import com.intellij.codeInspection.ex.*; import com.intellij.codeInspection.reference.*; import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; +import com.intellij.lang.Language; +import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.BidirectionalMap; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; import org.jdom.Element; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,6 +28,7 @@ import java.util.*; public class RedundantSuppressInspection extends GlobalInspectionTool { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.RedundantSuppressInspection"); + public static final String SHORT_NAME = "RedundantSuppression"; public boolean IGNORE_ALL; private BidirectionalMap myQuickFixes; @@ -60,7 +48,7 @@ public class RedundantSuppressInspection extends GlobalInspectionTool { @NotNull @NonNls public String getShortName() { - return "RedundantSuppression"; + return SHORT_NAME; } @Override @@ -80,25 +68,19 @@ public class RedundantSuppressInspection extends GlobalInspectionTool { @NotNull final InspectionManager manager, @NotNull final GlobalInspectionContext globalContext, @NotNull final ProblemDescriptionsProcessor problemDescriptionsProcessor) { - globalContext.getRefManager().iterate(new RefJavaVisitor() { + globalContext.getRefManager().iterate(new RefVisitor() { @Override - public void visitJavaModule(@NotNull RefJavaModule javaModule) { - visitElement(javaModule); - } - - @Override public void visitClass(@NotNull RefClass refClass) { - visitElement(refClass); - } - - private void visitElement(@NotNull RefElement refElement) { + public void visitFile(@NotNull RefFile refElement) { if (!globalContext.shouldCheck(refElement, RedundantSuppressInspection.this)) return; - final PsiElement element = refElement.getElement(); - if (element == null) return; - final CommonProblemDescriptor[] descriptors = checkElement(element, manager); + final PsiFile file = refElement.getPsiElement(); + if (file == null) return; + InspectionSuppressor extension = LanguageInspectionSuppressors.INSTANCE.forLanguage(file.getLanguage()); + if (!(extension instanceof RedundantSuppressionDetector)) return; + final CommonProblemDescriptor[] descriptors = checkElement(file, (RedundantSuppressionDetector)extension, manager); for (CommonProblemDescriptor descriptor : descriptors) { if (descriptor instanceof ProblemDescriptor) { final PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement(); - final PsiMember member = PsiTreeUtil.getParentOfType(psiElement, PsiMember.class); + final PsiElement member = globalContext.getRefManager().getContainerElement(psiElement); final RefElement reference = globalContext.getRefManager().getReference(member); if (reference != null) { problemDescriptionsProcessor.addProblemElement(reference, descriptor); @@ -112,48 +94,15 @@ public class RedundantSuppressInspection extends GlobalInspectionTool { } @NotNull - public ProblemDescriptor[] checkElement(@NotNull final PsiElement psiElement, @NotNull final InspectionManager manager) { + public ProblemDescriptor[] checkElement(@NotNull final PsiFile psiElement, + RedundantSuppressionDetector extension, + @NotNull final InspectionManager manager) { final Map> suppressedScopes = new THashMap<>(); - psiElement.accept(new JavaRecursiveElementWalkingVisitor() { - @Override public void visitModifierList(PsiModifierList list) { - super.visitModifierList(list); - final PsiElement parent = list.getParent(); - if (parent instanceof PsiModifierListOwner && !(parent instanceof PsiClass)) { - checkElement(parent); - } - } - - @Override public void visitComment(PsiComment comment) { - checkElement(comment); - } - - @Override public void visitClass(PsiClass aClass) { - if (aClass == psiElement) { - super.visitClass(aClass); - checkElement(aClass); - } - } - - - private void checkElement(final PsiElement owner) { - String idsString = JavaSuppressionUtil.getSuppressedInspectionIdsIn(owner); - if (idsString != null && !idsString.isEmpty()) { - List ids = new ArrayList<>(); - StringUtil.tokenize(idsString, "[, ]").forEach(ids::add); - if (IGNORE_ALL && (ids.contains(SuppressionUtil.ALL) || ids.contains(SuppressionUtil.ALL.toLowerCase()))) return; - Collection suppressed = suppressedScopes.get(owner); - if (suppressed == null) { - suppressed = ids; - } - else { - for (String id : ids) { - if (!suppressed.contains(id)) { - suppressed.add(id); - } - } - } - suppressedScopes.put(owner, suppressed); - } + psiElement.accept(new PsiRecursiveElementWalkingVisitor() { + @Override + public void visitElement(PsiElement element) { + super.visitElement(element); + collectSuppressions(element, suppressedScopes, IGNORE_ALL, extension); } }); @@ -236,8 +185,8 @@ public class RedundantSuppressInspection extends GlobalInspectionTool { if (!(descriptor instanceof ProblemDescriptor)) continue; PsiElement element = ((ProblemDescriptor)descriptor).getPsiElement(); if (element == null) continue; - PsiElement annotation = JavaSuppressionUtil.getElementToolSuppressedIn(element, toolId); - if (annotation != null && PsiTreeUtil.isAncestor(suppressedScope, annotation, false) || annotation == null && !PsiTreeUtil.isAncestor(suppressedScope, element, false)) { + PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(element.getProject()).getInjectionHost(element); + if (extension.isSuppressionFor(suppressedScope, ObjectUtils.notNull(host, element), toolId)) { suppressedIds.remove(toolId); break; } @@ -247,37 +196,24 @@ public class RedundantSuppressInspection extends GlobalInspectionTool { for (PsiElement suppressedScope : suppressedScopes.keySet()) { Collection suppressedIds = suppressedScopes.get(suppressedScope); for (String toolId : suppressedIds) { - PsiJavaDocumentedElement documentedElement; - String problemLine = null; - if (suppressedScope instanceof PsiJavaDocumentedElement) { - documentedElement = (PsiJavaDocumentedElement)suppressedScope; - } - else { - documentedElement = PsiTreeUtil.getParentOfType(suppressedScope, PsiJavaDocumentedElement.class); - final PsiStatement statement = PsiTreeUtil.getNextSiblingOfType(suppressedScope, PsiStatement.class); - problemLine = statement != null ? statement.getText() : null; - } + PsiNamedElement documentedElement = globalContext.getRefManager().getContainerElement(suppressedScope); if (documentedElement != null && documentedElement.isValid()) { - String description = InspectionsBundle.message("inspection.redundant.suppression.description"); if (myQuickFixes == null) myQuickFixes = new BidirectionalMap<>(); - final String key = toolId + (problemLine != null ? ";" + problemLine : ""); + String key = toolId + ";" + suppressedScope.getLanguage().getID(); QuickFix fix = myQuickFixes.get(key); if (fix == null) { - fix = new RemoveSuppressWarningAction(toolId, problemLine); + fix = createQuickFix(key); myQuickFixes.put(key, fix); } PsiElement identifier; - if (suppressedScope instanceof PsiNameIdentifierOwner && suppressedScope instanceof PsiJavaDocumentedElement) { - identifier = ((PsiNameIdentifierOwner)suppressedScope).getNameIdentifier(); + if (suppressedScope instanceof PsiNameIdentifierOwner && suppressedScope == documentedElement) { + identifier = ObjectUtils.notNull(((PsiNameIdentifierOwner)suppressedScope).getNameIdentifier(), suppressedScope); } else { identifier = suppressedScope; } - if (identifier == null) { - identifier = documentedElement; - } result.add( - manager.createProblemDescriptor(identifier, description, (LocalQuickFix)fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + manager.createProblemDescriptor(identifier, InspectionsBundle.message("inspection.redundant.suppression.description"), (LocalQuickFix)fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)); } } @@ -290,6 +226,84 @@ public class RedundantSuppressInspection extends GlobalInspectionTool { return result.toArray(ProblemDescriptor.EMPTY_ARRAY); } + private static void collectSuppressions(@NotNull PsiElement element, + Map> suppressedScopes, + boolean ignoreAll, + RedundantSuppressionDetector suppressor) { + String idsString = suppressor.getSuppressionIds(element); + if (idsString != null && !idsString.isEmpty()) { + List ids = new ArrayList<>(); + StringUtil.tokenize(idsString, "[, ]").forEach(ids::add); + if (ignoreAll && (ids.contains(SuppressionUtil.ALL) || ids.contains(SuppressionUtil.ALL.toLowerCase()))) return; + Collection suppressed = suppressedScopes.get(element); + if (suppressed == null) { + suppressed = ids; + } + else { + for (String id : ids) { + if (!suppressed.contains(id)) { + suppressed.add(id); + } + } + } + suppressedScopes.put(element, suppressed); + } + } + + public LocalInspectionTool createLocalTool(RedundantSuppressionDetector suppressor, + Map> toolToSuppressScopes, + Set activeTools) { + return new LocalInspectionTool() { + @NotNull + @Override + public String getShortName() { + return SHORT_NAME; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return RedundantSuppressInspection.this.getDisplayName(); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new PsiElementVisitor() { + + @Override + public void visitElement(PsiElement element) { + super.visitElement(element); + HashMap> scopes = new HashMap<>(); + collectSuppressions(element, scopes, false, suppressor); + Collection suppressIds = scopes.get(element); + if (suppressIds != null) { + for (String suppressId : suppressIds) { + if (!activeTools.contains(suppressId)) continue; + Set suppressedPlaces = toolToSuppressScopes.get(suppressId); + if (suppressedPlaces == null || suppressedPlaces.stream().noneMatch(place -> suppressor.isSuppressionFor(element, place, suppressId))) { + holder.registerProblem(element, suppressor.getHighlightingRange(element, suppressId), + InspectionsBundle.message("inspection.redundant.suppression.description"), + suppressor.createRemoveRedundantSuppressionFix(suppressId)); + } + } + } + } + }; + } + }; + } + + private static QuickFix createQuickFix(String key) { + String[] toolAndLang = key.split(";"); + Language language = Language.findLanguageByID(toolAndLang[1]); + if (language == null) return null; + InspectionSuppressor suppressor = LanguageInspectionSuppressors.INSTANCE.forLanguage(language); + return suppressor instanceof RedundantSuppressionDetector + ? ((RedundantSuppressionDetector)suppressor).createRemoveRedundantSuppressionFix(toolAndLang[0]) : null; + } + @NotNull protected InspectionToolWrapper[] getInspectionTools(PsiElement psiElement, @NotNull InspectionManager manager) { // todo for what we create modifiable model here? @@ -303,7 +317,7 @@ public class RedundantSuppressInspection extends GlobalInspectionTool { @Override @Nullable public QuickFix getQuickFix(final String hint) { - return myQuickFixes != null ? myQuickFixes.get(hint) : new RemoveSuppressWarningAction(hint); + return myQuickFixes != null ? myQuickFixes.get(hint) : createQuickFix(hint); } @Override diff --git a/java/java-impl/src/inspectionDescriptions/RedundantSuppression.html b/platform/platform-resources-en/src/inspectionDescriptions/RedundantSuppression.html similarity index 100% rename from java/java-impl/src/inspectionDescriptions/RedundantSuppression.html rename to platform/platform-resources-en/src/inspectionDescriptions/RedundantSuppression.html diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 90401ca7b9e7..07735b0d4e24 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -893,6 +893,9 @@ +