global inspections: try to get rid of long read lock (IDEA-226784)

GitOrigin-RevId: ed2c7e44b9f037d892379ab96d4732bedd10f51a
This commit is contained in:
Anna.Kozlova
2019-11-14 09:45:37 +01:00
committed by intellij-monorepo-bot
parent 960cc4c1fe
commit 3f5dd8da98
9 changed files with 107 additions and 53 deletions

View File

@@ -16,6 +16,7 @@
package com.intellij.codeInspection; package com.intellij.codeInspection;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.reference.RefManager; import com.intellij.codeInspection.reference.RefManager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -27,6 +28,11 @@ public abstract class GlobalJavaBatchInspectionTool extends GlobalInspectionTool
return queryExternalUsagesRequests(globalContext.getRefManager(), globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT), problemDescriptionsProcessor); return queryExternalUsagesRequests(globalContext.getRefManager(), globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT), problemDescriptionsProcessor);
} }
@Override
public boolean isReadActionNeeded() {
return false;
}
protected boolean queryExternalUsagesRequests(@NotNull RefManager manager, @NotNull GlobalJavaInspectionContext globalContext, @NotNull ProblemDescriptionsProcessor processor) { protected boolean queryExternalUsagesRequests(@NotNull RefManager manager, @NotNull GlobalJavaInspectionContext globalContext, @NotNull ProblemDescriptionsProcessor processor) {
return false; return false;
} }

View File

@@ -250,6 +250,11 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
return false; return false;
} }
@Override
public boolean isReadActionNeeded() {
return false;
}
@Override @Override
public void runInspection(@NotNull final AnalysisScope scope, public void runInspection(@NotNull final AnalysisScope scope,
@NotNull InspectionManager manager, @NotNull InspectionManager manager,

View File

@@ -40,6 +40,11 @@ public class InconsistentLanguageLevelInspection extends GlobalInspectionTool {
return false; return false;
} }
@Override
public boolean isReadActionNeeded() {
return false;
}
@Nullable @Nullable
@Override @Override
public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity,

View File

@@ -19,13 +19,13 @@ package com.intellij.codeInspection.unusedLibraries;
import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInspection.*; import com.intellij.codeInspection.*;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefGraphAnnotator; import com.intellij.codeInspection.reference.RefGraphAnnotator;
import com.intellij.codeInspection.reference.RefManager; import com.intellij.codeInspection.reference.RefManager;
import com.intellij.codeInspection.reference.RefModule; import com.intellij.codeInspection.reference.RefModule;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module; import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.*;
@@ -71,60 +71,75 @@ public class UnusedLibrariesInspection extends GlobalInspectionTool {
return new UnusedLibraryGraphAnnotator(refManager); return new UnusedLibraryGraphAnnotator(refManager);
} }
@Nullable
@Override @Override
public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, public boolean isReadActionNeeded() {
@NotNull AnalysisScope scope, return false;
@NotNull InspectionManager manager, }
@NotNull GlobalInspectionContext globalContext,
@NotNull ProblemDescriptionsProcessor processor) {
if (refEntity instanceof RefModule) {
final RefModule refModule = (RefModule)refEntity;
final Module module = refModule.getModule();
VirtualFile[] givenRoots =
OrderEnumerator.orderEntries(module).withoutSdk()
.withoutModuleSourceEntries()
.withoutDepModules()
.classes()
.getRoots();
if (givenRoots.length == 0) return null; @Override
public void runInspection(@NotNull AnalysisScope scope,
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); @NotNull InspectionManager manager,
final Set<VirtualFile> usedRoots = refModule.getUserData(UnusedLibraryGraphAnnotator.USED_LIBRARY_ROOTS); @NotNull GlobalInspectionContext globalContext,
@NotNull ProblemDescriptionsProcessor problemDescriptionsProcessor) {
if (usedRoots != null) { RefManager refManager = globalContext.getRefManager();
appendUsedRootDependencies(usedRoots, givenRoots); for (Module module : ModuleManager.getInstance(globalContext.getProject()).getModules()) {
} if (scope.containsModule(module)) {
RefModule refModule = refManager.getRefModule(module);
final List<CommonProblemDescriptor> result = new ArrayList<>(); if (refModule != null) {
for (OrderEntry entry : moduleRootManager.getOrderEntries()) { CommonProblemDescriptor[] descriptors = getDescriptors(manager, refModule, module);
if (entry instanceof LibraryOrderEntry && if (descriptors != null) {
!((LibraryOrderEntry)entry).isExported() && problemDescriptionsProcessor.addProblemElement(refModule, descriptors);
((LibraryOrderEntry)entry).getScope() != DependencyScope.RUNTIME) {
final Set<VirtualFile> files = ContainerUtil.set(((LibraryOrderEntry)entry).getRootFiles(OrderRootType.CLASSES));
boolean allRootsUnused = usedRoots == null || !files.removeAll(usedRoots);
if (allRootsUnused) {
String message = InspectionsBundle.message("unused.library.problem.descriptor", entry.getPresentableName());
result.add(manager.createProblemDescriptor(message, module, new RemoveUnusedLibrary(entry.getPresentableName(), null)));
}
else if (!files.isEmpty() && !IGNORE_LIBRARY_PARTS) {
final String unusedLibraryRoots = StringUtil.join(files, file -> file.getPresentableName(), ",");
String message =
InspectionsBundle.message("unused.library.roots.problem.descriptor", unusedLibraryRoots, entry.getPresentableName());
CommonProblemDescriptor descriptor =
((LibraryOrderEntry)entry).isModuleLevel()
? manager.createProblemDescriptor(message, module, new RemoveUnusedLibrary(entry.getPresentableName(), files))
: manager.createProblemDescriptor(message);
result.add(descriptor);
} }
} }
} }
return result.isEmpty() ? null : result.toArray(CommonProblemDescriptor.EMPTY_ARRAY);
} }
return null; }
@Nullable
private CommonProblemDescriptor[] getDescriptors(@NotNull InspectionManager manager,
RefModule refModule,
Module module) {
VirtualFile[] givenRoots =
OrderEnumerator.orderEntries(module).withoutSdk()
.withoutModuleSourceEntries()
.withoutDepModules()
.classes()
.getRoots();
if (givenRoots.length == 0) return null;
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
final Set<VirtualFile> usedRoots = refModule.getUserData(UnusedLibraryGraphAnnotator.USED_LIBRARY_ROOTS);
if (usedRoots != null) {
appendUsedRootDependencies(usedRoots, givenRoots);
}
final List<CommonProblemDescriptor> result = new ArrayList<>();
for (OrderEntry entry : moduleRootManager.getOrderEntries()) {
if (entry instanceof LibraryOrderEntry &&
!((LibraryOrderEntry)entry).isExported() &&
((LibraryOrderEntry)entry).getScope() != DependencyScope.RUNTIME) {
final Set<VirtualFile> files = ContainerUtil.set(((LibraryOrderEntry)entry).getRootFiles(OrderRootType.CLASSES));
boolean allRootsUnused = usedRoots == null || !files.removeAll(usedRoots);
if (allRootsUnused) {
String message = InspectionsBundle.message("unused.library.problem.descriptor", entry.getPresentableName());
result.add(manager.createProblemDescriptor(message, module, new RemoveUnusedLibrary(entry.getPresentableName(), null)));
}
else if (!files.isEmpty() && !IGNORE_LIBRARY_PARTS) {
final String unusedLibraryRoots = StringUtil.join(files, file -> file.getPresentableName(), ",");
String message =
InspectionsBundle.message("unused.library.roots.problem.descriptor", unusedLibraryRoots, entry.getPresentableName());
CommonProblemDescriptor descriptor =
((LibraryOrderEntry)entry).isModuleLevel()
? manager.createProblemDescriptor(message, module, new RemoveUnusedLibrary(entry.getPresentableName(), files))
: manager.createProblemDescriptor(message);
result.add(descriptor);
}
}
}
return result.isEmpty() ? null : result.toArray(CommonProblemDescriptor.EMPTY_ARRAY);
} }
private static void appendUsedRootDependencies(@NotNull Set<VirtualFile> usedRoots, private static void appendUsedRootDependencies(@NotNull Set<VirtualFile> usedRoots,

View File

@@ -10,6 +10,7 @@ import com.intellij.codeInspection.ex.EntryPointsManager;
import com.intellij.codeInspection.ex.EntryPointsManagerBase; import com.intellij.codeInspection.ex.EntryPointsManagerBase;
import com.intellij.codeInspection.reference.*; import com.intellij.codeInspection.reference.*;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
@@ -501,7 +502,7 @@ public final class VisibilityInspection extends GlobalJavaBatchInspectionTool {
final EntryPointsManager entryPointsManager = globalContext.getEntryPointsManager(manager); final EntryPointsManager entryPointsManager = globalContext.getEntryPointsManager(manager);
for (RefElement entryPoint : entryPointsManager.getEntryPoints(manager)) { for (RefElement entryPoint : entryPointsManager.getEntryPoints(manager)) {
//don't ignore entry points with explicit visibility requirements //don't ignore entry points with explicit visibility requirements
if (entryPoint instanceof RefJavaElement && getMinVisibilityLevel((RefJavaElement)entryPoint) > 0) { if (entryPoint instanceof RefJavaElement && ReadAction.compute(() -> getMinVisibilityLevel((RefJavaElement)entryPoint)) > 0) {
continue; continue;
} }
ignoreElement(processor, entryPoint); ignoreElement(processor, entryPoint);

View File

@@ -4,6 +4,7 @@ package com.intellij.codeInspection;
import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.ex.JobDescriptor; import com.intellij.codeInspection.ex.JobDescriptor;
import com.intellij.codeInspection.reference.*; import com.intellij.codeInspection.reference.*;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.SmartPsiElementPointer; import com.intellij.psi.SmartPsiElementPointer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -136,6 +137,16 @@ public abstract class GlobalInspectionTool extends InspectionProfileEntry {
return true; return true;
} }
/**
* True by default to ensure third party plugins are not broken
*
* @return true if inspection should be started ({@link #runInspection(AnalysisScope, InspectionManager, GlobalInspectionContext, ProblemDescriptionsProcessor)}) in ReadAction,
* false if ReadAction is taken by inspection itself
*/
public boolean isReadActionNeeded() {
return true;
}
@Override @Override
public boolean isEnabledByDefault() { public boolean isEnabledByDefault() {

View File

@@ -45,6 +45,11 @@ public abstract class GlobalSimpleInspectionTool extends GlobalInspectionTool {
throw new IncorrectOperationException("You must override checkFile() instead"); throw new IncorrectOperationException("You must override checkFile() instead");
} }
@Override
public boolean isReadActionNeeded() {
return false;
}
@Override @Override
public final boolean isGraphNeeded() { public final boolean isGraphNeeded() {
return false; return false;

View File

@@ -740,14 +740,20 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase {
throw e; throw e;
} }
} }
ApplicationManager.getApplication().runReadAction(() -> { ThrowableRunnable<RuntimeException> runnable = () -> {
tool.runInspection(scopeForState, inspectionManager, this, toolPresentation); tool.runInspection(scopeForState, inspectionManager, this, toolPresentation);
//skip phase when we are sure that scope already contains everything, unused declaration though needs to proceed with its suspicious code //skip phase when we are sure that scope already contains everything, unused declaration though needs to proceed with its suspicious code
if ((canBeExternalUsages || tool.getAdditionalJobs(this) != null) && if ((canBeExternalUsages || tool.getAdditionalJobs(this) != null) &&
tool.queryExternalUsagesRequests(inspectionManager, this, toolPresentation)) { tool.queryExternalUsagesRequests(inspectionManager, this, toolPresentation)) {
needRepeatSearchRequest.add(toolWrapper); needRepeatSearchRequest.add(toolWrapper);
} }
}); };
if (tool.isReadActionNeeded()) {
ReadAction.run(runnable);
}
else {
runnable.run();
}
} }
catch (ProcessCanceledException | IndexNotReadyException e) { catch (ProcessCanceledException | IndexNotReadyException e) {
throw e; throw e;

View File

@@ -68,7 +68,7 @@ public class EmptyDirectoryInspection extends BaseGlobalInspection {
@NotNull final ProblemDescriptionsProcessor processor) { @NotNull final ProblemDescriptionsProcessor processor) {
final Project project = context.getProject(); final Project project = context.getProject();
final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex();
final SearchScope searchScope = scope.toSearchScope(); final SearchScope searchScope = ReadAction.compute(() -> scope.toSearchScope());
if (!(searchScope instanceof GlobalSearchScope)) { if (!(searchScope instanceof GlobalSearchScope)) {
return; return;
} }