IJPL-160728 SuppressDetectingInspection can be non-dumb aware

GitOrigin-RevId: e76b9fb448e6a38d23249684dc77b4e44d8911a9
This commit is contained in:
Max Medvedev
2024-08-21 16:58:49 +02:00
committed by intellij-monorepo-bot
parent 0980161ec9
commit c4476fce2c
5 changed files with 150 additions and 22 deletions

View File

@@ -4,17 +4,14 @@ package com.intellij.codeInsight.daemon.impl
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase.CanChangeDocumentDuringHighlighting
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.codeInspection.LocalInspectionEP
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.*
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.use
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.*
import com.intellij.testFramework.*
import org.intellij.lang.annotations.Language
import java.util.*
@@ -26,10 +23,11 @@ class LocalInspectionsInDumbModeTest : DaemonAnalyzerTestCase() {
override fun setUp() {
super.setUp()
DaemonProgressIndicator.setDebug(true)
enableInspectionTools(project, testRootDisposable, DumbInspection(), SmartInspection())
}
fun testLocalInspectionInDumbMode() {
enableInspectionTools(project, testRootDisposable, DumbInspection(), SmartInspection())
@Language("JAVA")
val text = """
// comment
@@ -57,6 +55,8 @@ class LocalInspectionsInDumbModeTest : DaemonAnalyzerTestCase() {
}
fun testLocalInspectionsInSmartModeThenInDumbMode() {
enableInspectionTools(project, testRootDisposable, DumbInspection(), SmartInspection())
@Language("JAVA")
val text = """
// comment
@@ -78,6 +78,8 @@ class LocalInspectionsInDumbModeTest : DaemonAnalyzerTestCase() {
}
fun testLocalInspectionInDumbModeDontInitializeUnrelatedTools() {
enableInspectionTools(project, testRootDisposable, DumbInspection(), SmartInspection())
val unrelatedToolWrapper = createUnrelatedToolWrapper()
enableInspectionTool(project, unrelatedToolWrapper, testRootDisposable)
LocalInspectionsPass.forceNoDuplicateCheckInTests(testRootDisposable)
@@ -94,6 +96,8 @@ class LocalInspectionsInDumbModeTest : DaemonAnalyzerTestCase() {
}
fun testLocalInspectionDontInitializeUnrelatedTools() {
enableInspectionTools(project, testRootDisposable, DumbInspection(), SmartInspection())
val unrelatedToolWrapper = createUnrelatedToolWrapper()
enableInspectionTool(project, unrelatedToolWrapper, testRootDisposable)
LocalInspectionsPass.forceNoDuplicateCheckInTests(testRootDisposable)
@@ -109,6 +113,86 @@ class LocalInspectionsInDumbModeTest : DaemonAnalyzerTestCase() {
assertFalse(unrelatedToolWrapper.isToolInstantiated())
}
fun testJavaSuppressor() {
enableInspectionTools(project, testRootDisposable, RedundantSuppressInspection(), StringInspection())
val javaRedundantSuppressor = LanguageInspectionSuppressors.INSTANCE.allForLanguage(JavaLanguage.INSTANCE)
.filterIsInstance<RedundantSuppressionDetector>()
.firstOrNull() // Java Redundant Suppressor is expected to exist
requireNotNull(javaRedundantSuppressor) // Java Redundant Suppressor is expected to exist
assertFalse(javaRedundantSuppressor.isDumbAware) // update the test if JavaRedundantSuppressor has become dumb-aware
@Language("JAVA")
val text = """
class A {
void foo() {
//noinspection String
Object s = "abc";
}
}
"""
configureByText(JavaFileType.INSTANCE, text)
// smart infos don't contain String because Suppressor works in smart mode and suppresses it
val smartInfos = doHighlighting().map { it.description }
assertDoesntContain(smartInfos, "String")
// dumb infos contain String because Suppressor does not work in dumb mode
val dumbInfos = doHighlightingInDumbMode().map { it.description }
assertContainsElements(dumbInfos, "String")
}
fun testRedundantJavaSuppression() {
enableInspectionTools(project, testRootDisposable, RedundantSuppressInspection(), StringInspection())
val javaRedundantSuppressor = LanguageInspectionSuppressors.INSTANCE.allForLanguage(JavaLanguage.INSTANCE)
.filterIsInstance<RedundantSuppressionDetector>()
.firstOrNull()
assertNotNull(javaRedundantSuppressor)// Java Redundant Suppressor is expected to exist
@Language("JAVA")
val text = """
class A {
void foo() {
//noinspection String
Object s;
}
}
"""
configureByText(JavaFileType.INSTANCE, text)
// dumb infos contain a redundant suppression because it's not removed as java suppressor does not work in dumb mode
val initialDumbInfos = doHighlightingInDumbMode().map { it.description }
assertDoesntContain(initialDumbInfos, "Redundant suppression")
// smart infos contain a redundant suppression, because suppression is in fact redundant,
// and redundant suppressor for Java works in smart mode
val smartInfos = doHighlighting().map { it.description }
assertContainsElements(smartInfos, "Redundant suppression")
// dumb infos contain a redundant suppression because it's not removed as java suppressor does not work in dumb mode
val dumbInfos = doHighlightingInDumbMode().map { it.description }
assertContainsElements(dumbInfos, "Redundant suppression")
}
fun testSuppressorInDumbMode2() {
enableInspectionTools(project, testRootDisposable, RedundantSuppressInspection(), StringInspection())
@Language("JAVA")
val text = """
class A {
void foo() {
Object s = "abc";
}
}
"""
configureByText(JavaFileType.INSTANCE, text)
val dumbInfos = doHighlightingInDumbMode()
assertDoesntContain(dumbInfos, "String")
}
private fun assertExistsInfo(infos: List<HighlightInfo>, text: String) {
assert(infos.any { it.description == text }) {
"List [${infos.joinToString { it.description }}] does not contain `$text`"
@@ -154,6 +238,17 @@ class LocalInspectionsInDumbModeTest : DaemonAnalyzerTestCase() {
}
}
private class StringInspection : LocalInspectionTool(), DumbAware {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : JavaElementVisitor() {
override fun visitLiteralExpression(expression: PsiLiteralExpression) {
holder.registerProblem(expression, "String")
}
}
}
}
private fun createUnrelatedToolWrapper(): UnrelatedToolWrapper {
val ep = LocalInspectionEP()
ep.dumbAware = true

View File

@@ -1532,6 +1532,7 @@ com.intellij.codeInspection.QuickFix
- a:getFamilyName():java.lang.String
- getName():java.lang.String
com.intellij.codeInspection.RedundantSuppressionDetector
- com.intellij.openapi.project.PossiblyDumbAware
- a:createRemoveRedundantSuppressionFix(java.lang.String):com.intellij.codeInspection.LocalQuickFix
- getHighlightingRange(com.intellij.psi.PsiElement,java.lang.String):com.intellij.openapi.util.TextRange
- a:getSuppressionIds(com.intellij.psi.PsiElement):java.lang.String

View File

@@ -1,7 +1,8 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 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.openapi.project.PossiblyDumbAware;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
@@ -9,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface RedundantSuppressionDetector {
public interface RedundantSuppressionDetector extends PossiblyDumbAware {
/**
* @return comma separated list of suppress ids configured in this {@code element}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 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;
@@ -384,5 +384,10 @@ public abstract class RedundantSuppressInspectionBase extends GlobalSimpleInspec
}
};
}
@Override
public boolean isDumbAware() {
return mySuppressor.isDumbAware();
}
}
}

View File

@@ -145,7 +145,7 @@ class InspectionRunner {
InspectionEngine.withSession(myPsiFile, myRestrictRange, finalPriorityRange, minimumSeverity, myIsOnTheFly, session -> {
for (LocalInspectionToolWrapper toolWrapper : applicableByLanguage) {
if (enabledToolsPredicate == null || enabledToolsPredicate.value(toolWrapper)) {
LocalInspectionTool tool = toolWrapper.getTool();
LocalInspectionTool tool = toolWrapper.getTool();
AtomicInteger toolWasProcessed = new AtomicInteger();
ToolStampInfo toolStamps = new ToolStampInfo();
InspectionProblemHolder holder = new InspectionProblemHolder(myPsiFile, toolWrapper, myIsOnTheFly, myInspectionProfileWrapper,
@@ -207,7 +207,7 @@ class InspectionRunner {
InspectionProfilerDataHolder.saveStats(myPsiFile, init, highlightInfoUpdater);
}
if (myIsOnTheFly && addRedundantSuppressions) {
addRedundantSuppressions(init, toolWrappers, redundantContexts, applyIncrementallyCallback, contextFinishedCallback);
addRedundantSuppressions(init, toolWrappers, redundantContexts, applyIncrementallyCallback, contextFinishedCallback, enabledToolsPredicate);
}
});
return ContainerUtil.concat(init, redundantContexts, injectedContexts);
@@ -294,7 +294,8 @@ class InspectionRunner {
@NotNull List<? extends LocalInspectionToolWrapper> toolWrappers,
@NotNull List<? super InspectionContext> result,
@NotNull ApplyIncrementallyCallback applyIncrementallyCallback,
@NotNull Consumer<? super InspectionContext> contextFinishedCallback) {
@NotNull Consumer<? super InspectionContext> contextFinishedCallback,
@Nullable Condition<? super LocalInspectionToolWrapper> enabledToolsPredicate) {
for (InspectionContext context : init) {
LocalInspectionToolWrapper toolWrapper = context.tool;
LocalInspectionTool tool = toolWrapper.getTool();
@@ -310,20 +311,33 @@ class InspectionRunner {
if (redundantSuppressionKey == null || !inspectionProfile.isToolEnabled(redundantSuppressionKey, myPsiFile)) {
return;
}
InspectionToolWrapper<?, ?> redundantSuppressTool = Objects.requireNonNull(
inspectionProfile.getInspectionTool(RedundantSuppressInspectionBase.SHORT_NAME, myPsiFile),
"inspectionProfile.isToolEnabled(redundantSuppressionKey, myPsiFile) return true, thus an instance must be not-null"
);
Language fileLanguage = myPsiFile.getLanguage();
InspectionSuppressor suppressor = ContainerUtil.find(LanguageInspectionSuppressors.INSTANCE.allForLanguage(fileLanguage), s -> s instanceof RedundantSuppressionDetector);
if (!(suppressor instanceof RedundantSuppressionDetector redundantSuppressionDetector)) {
return;
}
Set<String> activeTools = new HashSet<>();
RedundantSuppressionDetector redundantSuppressionDetector = findSuppressionDetector(fileLanguage);
if (redundantSuppressionDetector == null) return;
// todo do we really need toolWrappers to figure out active tools?
// I believe `init` parameter already has the correct list of active tools???
Set<String> activeTools = new HashSet<>();
for (LocalInspectionToolWrapper tool : toolWrappers) {
if (tool.runForWholeFile()) {
// no redundants for whole file tools pass
continue;
}
if (tool.isUnfair() || !tool.isApplicable(fileLanguage) || myInspectionProfileWrapper.getInspectionTool(tool.getShortName(), myPsiFile) instanceof GlobalInspectionToolWrapper) {
if (tool.isUnfair() ||
!tool.isApplicable(fileLanguage) ||
myInspectionProfileWrapper.getInspectionTool(tool.getShortName(), myPsiFile) instanceof GlobalInspectionToolWrapper ||
!(enabledToolsPredicate != null && enabledToolsPredicate.test(tool))
) {
continue;
}
activeTools.add(tool.getID());
ContainerUtil.addIfNotNull(activeTools, tool.getAlternativeID());
InspectionElementsMerger elementsMerger = InspectionElementsMerger.getMerger(tool.getShortName());
@@ -331,16 +345,28 @@ class InspectionRunner {
activeTools.addAll(Arrays.asList(elementsMerger.getSuppressIds()));
}
}
InspectionToolWrapper<?,?> redundantSuppressTool = inspectionProfile.getInspectionTool(RedundantSuppressInspectionBase.SHORT_NAME, myPsiFile);
RedundantSuppressInspectionBase redundantSuppressGlobalTool = (RedundantSuppressInspectionBase)redundantSuppressTool.getTool();
LocalInspectionTool rsLocalTool = redundantSuppressGlobalTool.createLocalTool(redundantSuppressionDetector, mySuppressedElements, activeTools, myRestrictRange);
List<LocalInspectionToolWrapper> wrappers = Collections.singletonList(new LocalInspectionToolWrapper(rsLocalTool));
LocalInspectionTool rsLocalTool = redundantSuppressGlobalTool.createLocalTool(
redundantSuppressionDetector, mySuppressedElements, activeTools, myRestrictRange
);
LocalInspectionToolWrapper rsWrapper = new LocalInspectionToolWrapper(rsLocalTool);
if (enabledToolsPredicate != null && !enabledToolsPredicate.test(rsWrapper)) {
return;
}
List<LocalInspectionToolWrapper> wrappers = Collections.singletonList(rsWrapper);
InspectionRunner runner = new InspectionRunner(myPsiFile, myRestrictRange, myPriorityRange, myInspectInjected, true,
myDumbMode, myProgress, false, myInspectionProfileWrapper,
mySuppressedElements);
result.addAll(runner.inspect(wrappers, HighlightSeverity.WARNING, false, applyIncrementallyCallback, contextFinishedCallback, null));
}
private static @Nullable RedundantSuppressionDetector findSuppressionDetector(@NotNull Language fileLanguage) {
List<InspectionSuppressor> allSuppressors = LanguageInspectionSuppressors.INSTANCE.allForLanguage(fileLanguage);
return ContainerUtil.findInstance(allSuppressors, RedundantSuppressionDetector.class);
}
private void executeInImpatientReadAction(@NotNull Runnable runnable) {
ApplicationEx application = ApplicationManagerEx.getApplicationEx();
boolean shouldFailFastAcquiringReadAction = application.isInImpatientReader();