diff --git a/java/structuralsearch-java/testSrc/com/intellij/java/structuralsearch/inspection/SSBasedInspectionTest.java b/java/structuralsearch-java/testSrc/com/intellij/java/structuralsearch/inspection/SSBasedInspectionTest.java index 70c4064b0893..44ba428c8cbd 100644 --- a/java/structuralsearch-java/testSrc/com/intellij/java/structuralsearch/inspection/SSBasedInspectionTest.java +++ b/java/structuralsearch-java/testSrc/com/intellij/java/structuralsearch/inspection/SSBasedInspectionTest.java @@ -1,26 +1,43 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.java.structuralsearch.inspection; +import com.intellij.analysis.AnalysisScope; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator; +import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionEngine; +import com.intellij.codeInspection.RedundantSuppressInspection; +import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper; import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; import com.intellij.codeInspection.ex.ToolsImpl; import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.psi.PsiFile; import com.intellij.structuralsearch.MatchOptions; import com.intellij.structuralsearch.inspection.SSBasedInspection; import com.intellij.structuralsearch.inspection.StructuralSearchProfileActionProvider; +import com.intellij.structuralsearch.plugin.replace.ui.ReplaceConfiguration; +import com.intellij.structuralsearch.plugin.ui.Configuration; import com.intellij.structuralsearch.plugin.ui.SearchConfiguration; +import com.intellij.testFramework.InspectionTestUtil; +import com.intellij.testFramework.InspectionsKt; import com.intellij.testFramework.PerformanceUnitTest; import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.fixtures.impl.GlobalInspectionContextForTests; import com.intellij.tools.ide.metrics.benchmark.Benchmark; import com.intellij.util.PairProcessor; import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertNotNull; public class SSBasedInspectionTest extends SSBasedInspectionTestCase { @@ -42,6 +59,41 @@ public class SSBasedInspectionTest extends SSBasedInspectionTestCase { inspectionTest(configuration, HighlightDisplayLevel.ERROR); quickFixTest("Suppress for statement"); } + + public void testRedundantSuppress() { + final Configuration configuration = new ReplaceConfiguration(); + configuration.setName("println"); + configuration.setSuppressId("println"); + + final MatchOptions matchOptions = configuration.getMatchOptions(); + matchOptions.setFileType(JavaFileType.INSTANCE); + matchOptions.fillSearchCriteria("System.out.println();"); + configuration.getReplaceOptions().setReplacement("System.out.print('\\n');"); + + Project project = myFixture.getProject(); + InspectionProfileImpl profile = InspectionProfileManager.getInstance(project).getCurrentProfile(); + StructuralSearchProfileActionProvider.createNewInspection(configuration, project, profile); + @NotNull InspectionToolWrapper redundantSuppressInspection = new GlobalInspectionToolWrapper(new RedundantSuppressInspection()); + + VirtualFile sourceDir = myFixture.copyDirectoryToProject(new File("RedundantSuppress/", "src").getPath(), ""); + AnalysisScope scope = new AnalysisScope(myFixture.getPsiManager().findDirectory(sourceDir)); + + SSBasedInspection inspection = SSBasedInspection.getStructuralSearchInspection(profile); + List> wrappers = new ArrayList<>(); + wrappers.add(redundantSuppressInspection); + wrappers.add(new LocalInspectionToolWrapper(inspection)); + wrappers.addAll(inspection.getChildren()); + + GlobalInspectionContextForTests globalContext = InspectionsKt.createGlobalContextForTool(scope, project, wrappers); + InspectionTestUtil.runTool(redundantSuppressInspection, scope, globalContext); + InspectionTestUtil.compareToolResults(globalContext, redundantSuppressInspection, false, getTestDataPath() + "/RedundantSuppress/"); + + myFixture.testHighlighting(true, false, false, "FixAll.java"); + final IntentionAction intention = myFixture.getAvailableIntention("Fix all 'println' problems in file"); + assertNotNull("Intention not found. Did you forget a ?", intention); + myFixture.checkPreviewAndLaunchAction(intention); + myFixture.checkResultByFile("FixAll.after.java"); + } public void testBrokenPattern() { // check broken pattern does not throw exceptions diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java b/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java index 6b69ac70d311..a3c2fbaa0c5e 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java @@ -376,8 +376,8 @@ public final class InspectionEngine { if (holder.hasResults()) { for (ProblemDescriptor descriptor : holder.getResults()) { PsiElement element = descriptor.getPsiElement(); - if (element == null || !ignoreSuppressedElements || !SuppressionUtil.inspectionResultSuppressed(element, tool)) { - LocalInspectionToolWrapper wrapper = getRedirectedToolWrapper(toolWrapper, descriptor); + LocalInspectionToolWrapper wrapper = getRedirectedToolWrapper(toolWrapper, descriptor); + if (element == null || !ignoreSuppressedElements || !SuppressionUtil.inspectionResultSuppressed(element, wrapper.getTool())) { resultDescriptors.computeIfAbsent(wrapper, x -> new ArrayList<>()).add(descriptor); } } @@ -391,8 +391,7 @@ public final class InspectionEngine { return resultDescriptors; } - private static @Nullable LocalInspectionToolWrapper getRedirectedToolWrapper(LocalInspectionToolWrapper toolWrapper, - ProblemDescriptor descriptor) { + private static LocalInspectionToolWrapper getRedirectedToolWrapper(LocalInspectionToolWrapper toolWrapper, ProblemDescriptor descriptor) { if (descriptor instanceof ProblemDescriptorWithReporterName name && toolWrapper.getTool() instanceof DynamicGroupTool groupTool) { String reportingToolName = name.getReportingToolShortName(); for (LocalInspectionToolWrapper child : groupTool.getChildren()) { @@ -416,8 +415,7 @@ public final class InspectionEngine { if (toolWrapper instanceof LocalInspectionToolWrapper local) { Map> problemDescriptors = inspectEx(Collections.singletonList(local), psiFile, psiFile.getTextRange(), psiFile.getTextRange(), - false, - false, true, new EmptyProgressIndicator(), PairProcessor.alwaysTrue()); + false, false, true, new EmptyProgressIndicator(), PairProcessor.alwaysTrue()); for (List group : problemDescriptors.values()) { result.addAll(group); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java b/platform/analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java index 6df9181a897d..d857be30d2fd 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java @@ -1,4 +1,4 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInspection; import com.intellij.analysis.AnalysisScope; @@ -138,18 +138,24 @@ public abstract class RedundantSuppressInspectionBase extends GlobalSimpleInspec InspectionToolWrapper toolWrapper = entry.getKey(); String toolId = entry.getValue(); toolWrapper.initialize(globalContext); - Collection descriptors; + List descriptors; if (toolWrapper instanceof LocalInspectionToolWrapper local) { if (local.isUnfair()) { continue; // can't work with passes other than LocalInspectionPass } - LocalInspectionTool tool = local.getTool(); + String id = toolWrapper.getMainToolId(); + List wrappers; + if (id != null) { + LocalInspectionToolWrapper mainTool = (LocalInspectionToolWrapper)((InspectionProfileImpl)profile).getToolById(id, psiFile); + wrappers = mainTool != null ? List.of(local, mainTool) : List.of(local); + } + else { + wrappers = List.of(local); + } List found = Collections.synchronizedList(new ArrayList<>()); // shouldn't use standard ProblemsHolder because it filters out suppressed elements by default - InspectionEngine.inspectEx(Collections.singletonList(new LocalInspectionToolWrapper(tool)), psiFile, psiFile.getTextRange(), - psiFile.getTextRange(), false, - true, false, ProgressIndicatorProvider.getGlobalProgressIndicator(), - (wrapper, descriptor) -> found.add(descriptor)); + InspectionEngine.inspectEx(wrappers, psiFile, psiFile.getTextRange(), psiFile.getTextRange(), false, true, false, + ProgressIndicatorProvider.getGlobalProgressIndicator(), (__, descriptor) -> found.add(descriptor)); descriptors = new ArrayList<>(found); } else if (toolWrapper instanceof GlobalInspectionToolWrapper global) { @@ -166,7 +172,7 @@ public abstract class RedundantSuppressInspectionBase extends GlobalSimpleInspec Collection suppressedIds = e.getValue(); PsiElement suppressedScope = e.getKey(); if (!suppressedIds.contains(toolId)) continue; - for (CommonProblemDescriptor descriptor : descriptors) { + for (ProblemDescriptor descriptor : descriptors) { if (!(descriptor instanceof ProblemDescriptor problemDescriptor)) continue; PsiElement element = problemDescriptor.getPsiElement(); if (element == null) continue; diff --git a/platform/structuralsearch/testData/ssBased/FixAll.after.java b/platform/structuralsearch/testData/ssBased/FixAll.after.java new file mode 100644 index 000000000000..3ccd9ff14e6e --- /dev/null +++ b/platform/structuralsearch/testData/ssBased/FixAll.after.java @@ -0,0 +1,11 @@ +class Y { + + void x() { + System.out.print('\n'); + System.out.print('\n'); + System.out.print('\n'); + + // noinspection println + System.out.println(); + } +} \ No newline at end of file diff --git a/platform/structuralsearch/testData/ssBased/FixAll.java b/platform/structuralsearch/testData/ssBased/FixAll.java new file mode 100644 index 000000000000..c598fc1d1aeb --- /dev/null +++ b/platform/structuralsearch/testData/ssBased/FixAll.java @@ -0,0 +1,11 @@ +class Y { + + void x() { + System.out.println(); + System.out.println(); + System.out.println(); + + // noinspection println + System.out.println(); + } +} \ No newline at end of file diff --git a/platform/structuralsearch/testData/ssBased/RedundantSuppress/expected.xml b/platform/structuralsearch/testData/ssBased/RedundantSuppress/expected.xml new file mode 100644 index 000000000000..5e933496b9cf --- /dev/null +++ b/platform/structuralsearch/testData/ssBased/RedundantSuppress/expected.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/platform/structuralsearch/testData/ssBased/RedundantSuppress/src/RedundantSuppress.java b/platform/structuralsearch/testData/ssBased/RedundantSuppress/src/RedundantSuppress.java new file mode 100644 index 000000000000..01788da4b63f --- /dev/null +++ b/platform/structuralsearch/testData/ssBased/RedundantSuppress/src/RedundantSuppress.java @@ -0,0 +1,11 @@ +class X { + + void x() { + System.out.println(); + System.out.println(); + System.out.println(); + + // noinspection println + System.out.println(); + } +} \ No newline at end of file