[devkit] RegistrationCheckerUtil: performance, #3

more fine-grained search for registration
This commit is contained in:
Yann Cébron
2017-06-29 19:46:42 +02:00
parent 596a82d1b9
commit 7e894d6f2e
3 changed files with 78 additions and 61 deletions
@@ -25,7 +25,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NullableLazyValue;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.MethodReferencesSearch;
@@ -41,12 +40,12 @@ import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.inspections.quickfix.RegisterActionFix;
import org.jetbrains.idea.devkit.inspections.quickfix.RegisterComponentFix;
import org.jetbrains.idea.devkit.module.PluginModuleType;
import org.jetbrains.idea.devkit.util.ActionType;
import org.jetbrains.idea.devkit.util.ComponentType;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.util.Map;
import java.util.Set;
public class ComponentNotRegisteredInspection extends DevKitInspectionBase {
@@ -101,15 +100,6 @@ public class ComponentNotRegisteredInspection extends DevKitInspectionBase {
GlobalSearchScope scope = checkedClass.getResolveScope();
final NullableLazyValue<PsiClass> registrationType = new NullableLazyValue<PsiClass>() {
@Nullable
@Override
protected PsiClass compute() {
final Set<PsiClass> types = RegistrationCheckerUtil.getRegistrationTypes(checkedClass, CHECK_ACTIONS);
return ContainerUtil.getFirstItem(types);
}
};
if (CHECK_ACTIONS) {
PsiClass actionClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(AnAction.class.getName(), scope);
if (actionClass == null) {
@@ -120,7 +110,7 @@ public class ComponentNotRegisteredInspection extends DevKitInspectionBase {
if (IGNORE_NON_PUBLIC && !checkedClass.hasModifierProperty(PsiModifier.PUBLIC)) {
return null;
}
if (!isActionRegistered(checkedClass, registrationType.getValue()) && canFix(checkedClass)) {
if (!isActionRegistered(checkedClass) && canFix(checkedClass)) {
LocalQuickFix fix = new RegisterActionFix(org.jetbrains.idea.devkit.util.PsiUtil.createPointer(checkedClass));
ProblemDescriptor problem =
manager.createProblemDescriptor(classIdentifier,
@@ -139,45 +129,50 @@ public class ComponentNotRegisteredInspection extends DevKitInspectionBase {
// stop if component class cannot be found (non-devkit module/project)
return null;
}
if (checkedClass.isInheritor(compClass, true)) {
boolean registeredAsComponent = false;
if (!checkedClass.isInheritor(compClass, true)) {
return null;
}
final PsiClass registrationClass = registrationType.getValue();
if (registrationClass != null) {
final String qualifiedName = registrationClass.getQualifiedName();
registeredAsComponent = !ActionType.ACTION.myClassName.equals(qualifiedName) &&
!ActionType.GROUP.myClassName.equals(qualifiedName);
for (ComponentType componentType : ComponentType.values()) {
if (!InheritanceUtil.isInheritor(checkedClass, componentType.myClassName)) {
continue;
}
if (!registeredAsComponent && canFix(checkedClass)) {
ComponentType type = null;
for (ComponentType componentType : ComponentType.values()) {
if (InheritanceUtil.isInheritor(checkedClass, componentType.myClassName)) {
type = componentType;
break;
}
}
if (type == null) {
return null;
}
LocalQuickFix fix = new RegisterComponentFix(type, org.jetbrains.idea.devkit.util.PsiUtil.createPointer(checkedClass));
ProblemDescriptor problem =
manager.createProblemDescriptor(classIdentifier,
DevKitBundle.message("inspections.component.not.registered.message",
DevKitBundle.message(type.myPropertyKey)),
fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
return new ProblemDescriptor[]{problem};
if (findRegistrationType(checkedClass, COMPONENT_TYPE_TO_REGISTRATION_TYPE.get(componentType)) != null) {
return null;
}
if (!canFix(checkedClass)) {
return null;
}
LocalQuickFix fix = new RegisterComponentFix(componentType, org.jetbrains.idea.devkit.util.PsiUtil.createPointer(checkedClass));
ProblemDescriptor problem =
manager.createProblemDescriptor(classIdentifier,
DevKitBundle.message("inspections.component.not.registered.message",
DevKitBundle.message(componentType.myPropertyKey)),
fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
return new ProblemDescriptor[]{problem};
}
}
return null;
}
private static boolean isActionRegistered(PsiClass actionClass, PsiClass registrationType) {
private static final Map<ComponentType, RegistrationCheckerUtil.RegistrationType> COMPONENT_TYPE_TO_REGISTRATION_TYPE =
ContainerUtil.<ComponentType, RegistrationCheckerUtil.RegistrationType>immutableMapBuilder()
.put(ComponentType.APPLICATION, RegistrationCheckerUtil.RegistrationType.APPLICATION_COMPONENT)
.put(ComponentType.PROJECT, RegistrationCheckerUtil.RegistrationType.PROJECT_COMPONENT)
.put(ComponentType.MODULE, RegistrationCheckerUtil.RegistrationType.MODULE_COMPONENT)
.build();
private static PsiClass findRegistrationType(@Nullable PsiClass checkedClass, RegistrationCheckerUtil.RegistrationType type) {
final Set<PsiClass> types = RegistrationCheckerUtil.getRegistrationTypes(checkedClass, type);
return ContainerUtil.getFirstItem(types);
}
private static boolean isActionRegistered(PsiClass actionClass) {
final PsiClass registrationType = findRegistrationType(actionClass, RegistrationCheckerUtil.RegistrationType.ACTION);
if (registrationType != null) {
if (ActionType.ACTION.myClassName.equals(registrationType.getQualifiedName())) return true;
if (ActionType.GROUP.myClassName.equals(registrationType.getQualifiedName())) return true;
return true;
}
// search code usages: 1) own CTOR calls 2) usage via "new ActionClass()"
@@ -42,8 +42,17 @@ import java.util.Set;
class RegistrationCheckerUtil {
enum RegistrationType {
ALL,
ALL_COMPONENTS,
APPLICATION_COMPONENT,
PROJECT_COMPONENT,
MODULE_COMPONENT,
ACTION
}
@Nullable
static Set<PsiClass> getRegistrationTypes(PsiClass psiClass, boolean includeActions) {
static Set<PsiClass> getRegistrationTypes(PsiClass psiClass, RegistrationType registrationType) {
final Project project = psiClass.getProject();
final PsiFile psiFile = psiClass.getContainingFile();
@@ -56,7 +65,7 @@ class RegistrationCheckerUtil {
final boolean isIdeaProject = PsiUtil.isIdeaProject(project);
final Set<PsiClass> pluginModuleResults = checkModule(module, isIdeaProject, psiClass, includeActions);
final Set<PsiClass> pluginModuleResults = checkModule(module, isIdeaProject, psiClass, registrationType);
if (pluginModuleResults != null) {
return pluginModuleResults;
}
@@ -64,7 +73,7 @@ class RegistrationCheckerUtil {
final List<Module> candidateModules = PluginModuleType.getCandidateModules(module);
candidateModules.remove(module); // already checked
for (Module m : candidateModules) {
Set<PsiClass> types = checkModule(m, isIdeaProject, psiClass, includeActions);
Set<PsiClass> types = checkModule(m, isIdeaProject, psiClass, registrationType);
if (types != null) return types;
}
@@ -75,7 +84,7 @@ class RegistrationCheckerUtil {
private static Set<PsiClass> checkModule(Module module,
boolean isIdeaProject,
PsiClass psiClass,
boolean includeActions) {
RegistrationType registrationType) {
List<DomFileElement<IdeaPlugin>> pluginXmlCandidates = findPluginXmlFilesForModule(module, isIdeaProject);
if (pluginXmlCandidates.isEmpty()) return null;
@@ -88,7 +97,7 @@ class RegistrationCheckerUtil {
for (DomFileElement<IdeaPlugin> pluginXml : pluginXmlCandidates) {
// "main" plugin.xml
if (!processPluginXml(pluginXml, finder, includeActions)) return finder.getTypes();
if (!processPluginXml(pluginXml, finder, registrationType)) return finder.getTypes();
if (isIdeaProject) continue; // pluginXmlCandidates == all candidates in module
@@ -105,7 +114,7 @@ class RegistrationCheckerUtil {
final DomFileElement<IdeaPlugin> dependentIdeaPlugin = DescriptorUtil.getIdeaPlugin(depPluginXml);
if (dependentIdeaPlugin != null) {
if (!processPluginXml(dependentIdeaPlugin, finder, includeActions)) return finder.getTypes();
if (!processPluginXml(dependentIdeaPlugin, finder, registrationType)) return finder.getTypes();
}
}
}
@@ -131,23 +140,34 @@ class RegistrationCheckerUtil {
private static boolean processPluginXml(DomFileElement<IdeaPlugin> pluginXml,
RegistrationTypeFinder finder,
boolean includeActions) {
RegistrationType registrationType) {
final IdeaPlugin rootElement = pluginXml.getRootElement();
if (!ContainerUtil.process(rootElement.getApplicationComponents(), components ->
finder.processComponents(ComponentType.APPLICATION, components.getComponents()))) {
return false;
}
if (!ContainerUtil.process(rootElement.getProjectComponents(), components ->
finder.processComponents(ComponentType.PROJECT, components.getComponents()))) {
return false;
}
if (!ContainerUtil.process(rootElement.getModuleComponents(), components ->
finder.processComponents(ComponentType.MODULE, components.getComponents()))) {
return false;
final boolean findAll = registrationType == RegistrationType.ALL;
final boolean allComponents = findAll || registrationType == RegistrationType.ALL_COMPONENTS;
if (allComponents || registrationType == RegistrationType.APPLICATION_COMPONENT) {
if (!ContainerUtil.process(rootElement.getApplicationComponents(), components ->
finder.processComponents(ComponentType.APPLICATION, components.getComponents()))) {
return false;
}
}
if (includeActions) {
if (allComponents || registrationType == RegistrationType.PROJECT_COMPONENT) {
if (!ContainerUtil.process(rootElement.getProjectComponents(), components ->
finder.processComponents(ComponentType.PROJECT, components.getComponents()))) {
return false;
}
}
if (allComponents || registrationType == RegistrationType.MODULE_COMPONENT) {
if (!ContainerUtil.process(rootElement.getModuleComponents(), components ->
finder.processComponents(ComponentType.MODULE, components.getComponents()))) {
return false;
}
}
if (findAll || registrationType == RegistrationType.ACTION) {
if (!ContainerUtil.process(rootElement.getActions(), actions ->
finder.processActions(actions))) {
return false;
@@ -115,7 +115,9 @@ public class RegistrationProblemsInspection extends DevKitInspectionBase {
nameIdentifier != null &&
checkedClass.getQualifiedName() != null &&
checkedClass.getContainingFile().getVirtualFile() != null) {
final Set<PsiClass> componentClasses = RegistrationCheckerUtil.getRegistrationTypes(checkedClass, CHECK_ACTIONS);
final RegistrationCheckerUtil.RegistrationType registrationType =
CHECK_ACTIONS ? RegistrationCheckerUtil.RegistrationType.ALL : RegistrationCheckerUtil.RegistrationType.ALL_COMPONENTS;
final Set<PsiClass> componentClasses = RegistrationCheckerUtil.getRegistrationTypes(checkedClass, registrationType);
if (componentClasses != null && !componentClasses.isEmpty()) {
List<ProblemDescriptor> problems = new SmartList<>();