mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
add a dev kit inspection: try to write stateless EP
This commit is contained in:
@@ -129,6 +129,10 @@
|
||||
<localInspection language="JAVA" shortName="UnsafeReturnStatementVisitor" displayName="Unsafe return statements visitor"
|
||||
groupKey="inspections.group.name" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.internal.UnsafeReturnStatementVisitorInspection" />
|
||||
<localInspection language="JAVA" shortName="StatefulEp" displayName="Stateful EP"
|
||||
groupKey="inspections.group.name"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.StatefulEpInspection"/>
|
||||
|
||||
<moduleConfigurationEditorProvider implementation="org.jetbrains.idea.devkit.module.PluginModuleEditorsProvider"/>
|
||||
<implicitUsageProvider implementation="org.jetbrains.idea.devkit.inspections.DevKitImplicitUsageProvider"/>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Are you sure about holding heavy objects in Extension Point implementation?
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.
|
||||
*/
|
||||
package org.jetbrains.idea.devkit.inspections;
|
||||
|
||||
import com.intellij.codeInspection.InspectionManager;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.devkit.util.ExtensionPointCandidate;
|
||||
import org.jetbrains.idea.devkit.util.ExtensionPointLocator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StatefulEpInspection extends DevKitInspectionBase {
|
||||
@Nullable
|
||||
@Override
|
||||
public ProblemDescriptor[] checkClass(@NotNull PsiClass psiClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
|
||||
PsiField[] fields = psiClass.getFields();
|
||||
if (fields.length == 0) return super.checkClass(psiClass, manager, isOnTheFly);
|
||||
final boolean isQuickFix = InheritanceUtil.isInheritor(psiClass, LocalQuickFix.class.getCanonicalName());
|
||||
if (isQuickFix || shouldCheck(psiClass)) {
|
||||
List<ProblemDescriptor> result = ContainerUtil.newArrayList();
|
||||
for (final PsiField field : fields) {
|
||||
Checker projectChecker = new Checker(Project.class) {
|
||||
@Override
|
||||
boolean predicate() {
|
||||
return !field.hasModifierProperty(PsiModifier.FINAL);
|
||||
}
|
||||
};
|
||||
Checker psiChecker = new Checker(PsiElement.class) {
|
||||
@NotNull
|
||||
@Override
|
||||
String getMessage() {
|
||||
return "Potential memory leak: don't hold PsiElement, use SmartPsiElementPointer instead of" +
|
||||
(isQuickFix ? "; also see LocalQuickFixOnPsiElement" : "");
|
||||
}
|
||||
};
|
||||
Checker refChecker = new Checker(PsiReference.class);
|
||||
for (Checker checker : new Checker[]{projectChecker, psiChecker, refChecker}) {
|
||||
checker.check(field, manager, isOnTheFly, result);
|
||||
}
|
||||
}
|
||||
return result.toArray(new ProblemDescriptor[result.size()]);
|
||||
}
|
||||
return super.checkClass(psiClass, manager, isOnTheFly);
|
||||
}
|
||||
|
||||
boolean shouldCheck(@NotNull PsiClass psiClass) {
|
||||
for (ExtensionPointCandidate candidate : new ExtensionPointLocator(psiClass).findSuperCandidates()) {
|
||||
if (ExtensionPointLocator.isImplementedEp(psiClass, candidate)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class Checker {
|
||||
@NotNull Class myClass;
|
||||
|
||||
private Checker(@NotNull Class psiClass) {
|
||||
myClass = psiClass;
|
||||
}
|
||||
|
||||
private void check(@NotNull PsiField field, @NotNull InspectionManager manager, boolean isOnTheFly, @NotNull List<ProblemDescriptor> result) {
|
||||
if (predicate() && InheritanceUtil.isInheritor(field.getType(), myClass.getCanonicalName())) {
|
||||
String message = getMessage();
|
||||
String actual = message.isEmpty() ? "Don't use " + myClass.getSimpleName() + " as a field in extension points" : message;
|
||||
result.add(manager.createProblemDescriptor(field, actual, true, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String getMessage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
boolean predicate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import com.intellij.util.SmartList;
|
||||
import com.intellij.util.xml.DomElement;
|
||||
import com.intellij.util.xml.DomService;
|
||||
import com.intellij.util.xml.DomUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.devkit.dom.ExtensionPoint;
|
||||
import org.jetbrains.idea.devkit.dom.IdeaPlugin;
|
||||
@@ -70,13 +71,10 @@ public class ExtensionPointLocator {
|
||||
|
||||
private static void findExtensionPointCandidates(PsiClass psiClass, final List<ExtensionPointCandidate> list) {
|
||||
String name = psiClass.getQualifiedName();
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
if (name == null) return;
|
||||
|
||||
final Project project = psiClass.getProject();
|
||||
final Collection<VirtualFile> candidates = DomService.getInstance().getDomFileCandidates(IdeaPlugin.class, project, GlobalSearchScope.allScope(project));
|
||||
GlobalSearchScope scope = GlobalSearchScope.filesScope(project, candidates);
|
||||
Project project = psiClass.getProject();
|
||||
GlobalSearchScope scope = getCandidatesScope(project);
|
||||
PsiSearchHelper.SERVICE.getInstance(project).processUsagesInNonJavaFiles(name, new PsiNonJavaFileReferenceProcessor() {
|
||||
@Override
|
||||
public boolean process(PsiFile file, int startOffset, int endOffset) {
|
||||
@@ -87,6 +85,27 @@ public class ExtensionPointLocator {
|
||||
}, scope);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GlobalSearchScope getCandidatesScope(@NotNull Project project) {
|
||||
Collection<VirtualFile> candidates = DomService.getInstance().getDomFileCandidates(IdeaPlugin.class, project, GlobalSearchScope.allScope(project));
|
||||
return GlobalSearchScope.filesScope(project, candidates);
|
||||
}
|
||||
|
||||
public static boolean isImplementedEp(@NotNull PsiClass psiClass, @NotNull final ExtensionPointCandidate candidate) {
|
||||
String name = psiClass.getQualifiedName();
|
||||
if (name == null) return false;
|
||||
|
||||
Project project = psiClass.getProject();
|
||||
GlobalSearchScope scope = getCandidatesScope(project);
|
||||
return !PsiSearchHelper.SERVICE.getInstance(project).processUsagesInNonJavaFiles(name, new PsiNonJavaFileReferenceProcessor() {
|
||||
@Override
|
||||
public boolean process(PsiFile file, int startOffset, int endOffset) {
|
||||
XmlTag tag = PsiTreeUtil.getParentOfType(file.findElementAt(startOffset), XmlTag.class);
|
||||
return tag == null || !candidate.epName.endsWith(tag.getName());
|
||||
}
|
||||
}, scope);
|
||||
}
|
||||
|
||||
private static void processExtensionPointCandidate(PsiElement element, List<ExtensionPointCandidate> list) {
|
||||
XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
|
||||
if (tag == null) return;
|
||||
|
||||
Reference in New Issue
Block a user