mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
FillPermitsListInspection: intention is inspection now, compilation error fix added (IDEA-243901)
GitOrigin-RevId: cbab81c1117410e6bb56bd7066ebd074bbe1d015
This commit is contained in:
committed by
intellij-monorepo-bot
parent
33d9645cc5
commit
4eba7180d7
@@ -474,4 +474,6 @@ public abstract class QuickFixFactory {
|
||||
public abstract IntentionAction createCreateFieldFromParameterFix();
|
||||
@NotNull
|
||||
public abstract IntentionAction createAssignFieldFromParameterFix();
|
||||
|
||||
public abstract IntentionAction createFillPermitsListFix(PsiIdentifier classIdentifier);
|
||||
}
|
||||
+19
-3
@@ -31,11 +31,11 @@ import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.JavaPsiConstructorUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -1096,16 +1096,32 @@ public final class HighlightClassUtil {
|
||||
}
|
||||
|
||||
public static HighlightInfo checkSealedNonEnumeratedInheritors(PsiClass psiClass) {
|
||||
if (psiClass.hasModifierProperty(PsiModifier.SEALED) && psiClass.getPermitsListTypes().length == 0) {
|
||||
if (psiClass.hasModifierProperty(PsiModifier.SEALED)) {
|
||||
PsiIdentifier nameIdentifier = psiClass.getNameIdentifier();
|
||||
if (nameIdentifier == null) return null;
|
||||
if (psiClass.isEnum()) return null;
|
||||
if (!DirectClassInheritorsSearch.search(psiClass, new LocalSearchScope(psiClass.getContainingFile())).anyMatch(c -> !PsiUtil.isLocalOrAnonymousClass(c))) {
|
||||
|
||||
Collection<PsiClass> inheritors = DirectClassInheritorsSearch.search(psiClass).findAll();
|
||||
if (inheritors.isEmpty()) {
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
|
||||
.range(nameIdentifier)
|
||||
.descriptionAndTooltip(JavaErrorBundle.message("sealed.must.have.inheritors"))
|
||||
.create();
|
||||
}
|
||||
PsiFile parentFile = psiClass.getContainingFile();
|
||||
boolean hasOutsideClasses = inheritors.stream().anyMatch(inheritor -> inheritor.getContainingFile() != parentFile);
|
||||
if (hasOutsideClasses) {
|
||||
Set<PsiElement> permittedClasses = ContainerUtil.map2Set(psiClass.getPermitsListTypes(), PsiClassType::resolve);
|
||||
boolean hasMissingInheritors = inheritors.stream().anyMatch(inheritor -> !permittedClasses.contains(inheritor));
|
||||
if (hasMissingInheritors) {
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
|
||||
.range(nameIdentifier)
|
||||
.descriptionAndTooltip(JavaErrorBundle.message("permit.list.must.contain.outside.inheritors"))
|
||||
.create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createFillPermitsListFix(nameIdentifier));
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1716,6 +1716,11 @@
|
||||
groupKey="group.names.numeric.issues" groupBundle="messages.InspectionsBundle"
|
||||
enabledByDefault="true" level="INFORMATION"
|
||||
implementationClass="com.intellij.codeInspection.numeric.RemoveLiteralUnderscoresInspection" />
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="FillPermitsList"
|
||||
key="inspection.fill.permits.list.display.name" bundle="messages.JavaBundle"
|
||||
groupKey="group.names.code.style.issues" groupBundle="messages.InspectionsBundle"
|
||||
enabledByDefault="true" level="INFORMATION"
|
||||
implementationClass="com.intellij.codeInspection.FillPermitsListInspection"/>
|
||||
|
||||
<globalInspection groupPath="Java" language="JAVA" shortName="EmptyMethod" groupKey="group.names.declaration.redundancy" enabledByDefault="true" groupBundle="messages.InspectionsBundle"
|
||||
level="WARNING" implementationClass="com.intellij.codeInspection.emptyMethod.EmptyMethodInspection"
|
||||
@@ -1880,10 +1885,6 @@
|
||||
<className>com.intellij.codeInsight.intention.impl.SealClassAction</className>
|
||||
<category>Java/Declaration</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.FillPermitsListAction</className>
|
||||
<category>Java/Declaration</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.UnwrapElseBranchAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.intention.impl;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil;
|
||||
import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Query;
|
||||
import com.intellij.util.SmartList;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class FillPermitsListAction extends BaseElementAtCaretIntentionAction {
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
|
||||
if (!HighlightingFeature.SEALED_CLASSES.isAvailable(element)) return false;
|
||||
PsiIdentifier identifier = tryCast(element, PsiIdentifier.class);
|
||||
if (identifier == null) return false;
|
||||
PsiClass psiClass = tryCast(identifier.getParent(), PsiClass.class);
|
||||
if (psiClass == null || !(psiClass.getContainingFile() instanceof PsiJavaFile)) return false;
|
||||
PsiModifierList modifiers = psiClass.getModifierList();
|
||||
if (modifiers == null || !modifiers.hasExplicitModifier(PsiModifier.SEALED)) return false;
|
||||
PsiJavaCodeReferenceElement[] permittedRefs = getPermittedRefs(psiClass);
|
||||
Collection<PsiClass> sameFileInheritors = sameFileInheritors(psiClass).findAll();
|
||||
if (permittedRefs.length < sameFileInheritors.size()) return true;
|
||||
Set<PsiClass> permittedClasses = getPermittedClasses(permittedRefs);
|
||||
for (PsiClass inheritor : sameFileInheritors) {
|
||||
if (PsiUtil.isLocalOrAnonymousClass(inheritor)) return false;
|
||||
if (!permittedClasses.remove(inheritor)) return true;
|
||||
}
|
||||
for (PsiClass inheritor : outsideInheritors(psiClass)) {
|
||||
if (PsiUtil.isLocalOrAnonymousClass(inheritor)) return false;
|
||||
if (!permittedClasses.remove(inheritor)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
|
||||
PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
|
||||
if (psiClass == null) return;
|
||||
PsiJavaFile psiJavaFile = tryCast(psiClass.getContainingFile(), PsiJavaFile.class);
|
||||
if (psiJavaFile == null) return;
|
||||
Set<PsiClass> permittedClasses = getPermittedClasses(getPermittedRefs(psiClass));
|
||||
Collection<String> missingInheritors = getMissingInheritors(project, editor, psiJavaFile, psiClass, permittedClasses);
|
||||
if (missingInheritors == null) return;
|
||||
fillPermitsList(psiClass, missingInheritors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @IntentionName @NotNull String getText() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @IntentionFamilyName String getFamilyName() {
|
||||
return JavaBundle.message("intention.family.name.fill.permits.list");
|
||||
}
|
||||
|
||||
private static PsiJavaCodeReferenceElement @NotNull [] getPermittedRefs(@NotNull PsiClass psiClass) {
|
||||
PsiReferenceList permitsList = psiClass.getPermitsList();
|
||||
return permitsList == null ? PsiJavaCodeReferenceElement.EMPTY_ARRAY : permitsList.getReferenceElements();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Collection<String> getMissingInheritors(@NotNull Project project,
|
||||
Editor editor,
|
||||
@NotNull PsiJavaFile psiJavaFile,
|
||||
@NotNull PsiClass psiClass,
|
||||
@NotNull Set<PsiClass> permittedClasses) {
|
||||
Collection<String> missingInheritors = new SmartList<>();
|
||||
PsiJavaModule module = JavaModuleGraphUtil.findDescriptorByElement(psiClass);
|
||||
for (PsiClass inheritor : ClassInheritorsSearch.search(psiClass, false)) {
|
||||
String errorTitle = SealClassAction.checkInheritor(psiJavaFile, module, inheritor);
|
||||
if (errorTitle != null) {
|
||||
reportError(project, editor, JavaBundle.message(errorTitle));
|
||||
return null;
|
||||
}
|
||||
String qualifiedName = Objects.requireNonNull(inheritor.getQualifiedName());
|
||||
if (!permittedClasses.contains(inheritor)) missingInheritors.add(qualifiedName);
|
||||
}
|
||||
|
||||
if (missingInheritors.isEmpty()) {
|
||||
String message = JavaBundle.message("intention.error.fill.permits.list.no.missing.inheritors");
|
||||
reportError(project, editor, message);
|
||||
return null;
|
||||
}
|
||||
return missingInheritors;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<PsiClass> getPermittedClasses(PsiJavaCodeReferenceElement @NotNull [] permittedRefs) {
|
||||
return Arrays.stream(permittedRefs)
|
||||
.map(ref -> tryCast(ref.resolve(), PsiClass.class))
|
||||
.filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static @NotNull Query<PsiClass> outsideInheritors(@NotNull PsiClass parent) {
|
||||
GlobalSearchScope scope = GlobalSearchScope.notScope(GlobalSearchScope.fileScope(parent.getContainingFile()));
|
||||
return ClassInheritorsSearch.search(parent, scope, false);
|
||||
}
|
||||
|
||||
private static @NotNull Query<PsiClass> sameFileInheritors(@NotNull PsiClass parent) {
|
||||
return ClassInheritorsSearch.search(parent, GlobalSearchScope.fileScope(parent.getContainingFile()), false);
|
||||
}
|
||||
|
||||
private static void fillPermitsList(@NotNull PsiClass parent, @NotNull Collection<String> missingInheritors) {
|
||||
PsiReferenceList permitsList = parent.getPermitsList();
|
||||
PsiFileFactory factory = PsiFileFactory.getInstance(parent.getProject());
|
||||
if (permitsList == null) {
|
||||
PsiReferenceList implementsList = Objects.requireNonNull(parent.getImplementsList());
|
||||
String permitsClause = StreamEx.of(missingInheritors).sorted().joining(",", "permits ", "");
|
||||
parent.addAfter(createPermitsClause(factory, permitsClause), implementsList);
|
||||
}
|
||||
else {
|
||||
Stream<String> curClasses = Arrays.stream(permitsList.getReferenceElements()).map(ref -> ref.getQualifiedName());
|
||||
String permitsClause = StreamEx.of(missingInheritors).append(curClasses).sorted().joining(",", "permits ", "");
|
||||
permitsList.replace(createPermitsClause(factory, permitsClause));
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportError(@NotNull Project project, @NotNull Editor editor, @NotNull String message) {
|
||||
String title = JavaBundle.message("intention.fill.permits.list.hint.title");
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, JavaBundle.message(message), title, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiReferenceList createPermitsClause(@NotNull PsiFileFactory factory, @NotNull String permitsClause) {
|
||||
PsiJavaFile javaFile = (PsiJavaFile)factory.createFileFromText(JavaLanguage.INSTANCE, "class __Dummy " + permitsClause + "{}");
|
||||
PsiClass newClass = javaFile.getClasses()[0];
|
||||
return Objects.requireNonNull(newClass.getPermitsList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.intention.impl;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil;
|
||||
import com.intellij.codeInsight.hint.HintManager;
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class FillPermitsListFix extends LocalQuickFixAndIntentionActionOnPsiElement {
|
||||
|
||||
public FillPermitsListFix(PsiIdentifier classIdentifier) {
|
||||
super(classIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @IntentionName @NotNull String getText() {
|
||||
return JavaBundle.message("inspection.fill.permits.list.fix.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@Nullable Editor editor,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
PsiClass psiClass = PsiTreeUtil.getParentOfType(startElement, PsiClass.class);
|
||||
if (psiClass == null) return;
|
||||
PsiJavaFile psiJavaFile = tryCast(psiClass.getContainingFile(), PsiJavaFile.class);
|
||||
if (psiJavaFile == null) return;
|
||||
Set<PsiClass> permittedClasses = ContainerUtil.map2Set(psiClass.getPermitsListTypes(), PsiClassType::resolve);
|
||||
Collection<String> missingInheritors = getMissingInheritors(project, psiJavaFile, psiClass, permittedClasses);
|
||||
if (missingInheritors == null) return;
|
||||
fillPermitsList(psiClass, missingInheritors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @IntentionFamilyName @NotNull String getFamilyName() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Collection<String> getMissingInheritors(@NotNull Project project,
|
||||
@NotNull PsiJavaFile psiJavaFile,
|
||||
@NotNull PsiClass psiClass,
|
||||
@NotNull Set<PsiClass> permittedClasses) {
|
||||
Collection<String> missingInheritors = new SmartList<>();
|
||||
PsiJavaModule module = JavaModuleGraphUtil.findDescriptorByElement(psiClass);
|
||||
for (PsiClass inheritor : DirectClassInheritorsSearch.search(psiClass)) {
|
||||
String errorTitle = SealClassAction.checkInheritor(psiJavaFile, module, inheritor);
|
||||
if (errorTitle != null) {
|
||||
reportError(project, JavaBundle.message(errorTitle));
|
||||
return null;
|
||||
}
|
||||
String qualifiedName = Objects.requireNonNull(inheritor.getQualifiedName());
|
||||
if (!permittedClasses.contains(inheritor)) missingInheritors.add(qualifiedName);
|
||||
}
|
||||
|
||||
if (missingInheritors.isEmpty()) {
|
||||
String message = JavaBundle.message("inspection.fill.permits.list.no.missing.inheritors");
|
||||
reportError(project, message);
|
||||
return null;
|
||||
}
|
||||
return missingInheritors;
|
||||
}
|
||||
|
||||
private static void fillPermitsList(@NotNull PsiClass parent, @NotNull Collection<String> missingInheritors) {
|
||||
PsiReferenceList permitsList = parent.getPermitsList();
|
||||
PsiFileFactory factory = PsiFileFactory.getInstance(parent.getProject());
|
||||
if (permitsList == null) {
|
||||
PsiReferenceList implementsList = Objects.requireNonNull(parent.getImplementsList());
|
||||
String permitsClause = StreamEx.of(missingInheritors).sorted().joining(",", "permits ", "");
|
||||
parent.addAfter(createPermitsClause(factory, permitsClause), implementsList);
|
||||
}
|
||||
else {
|
||||
Stream<String> curClasses = Arrays.stream(permitsList.getReferenceElements()).map(ref -> ref.getQualifiedName());
|
||||
String permitsClause = StreamEx.of(missingInheritors).append(curClasses).sorted().joining(",", "permits ", "");
|
||||
permitsList.replace(createPermitsClause(factory, permitsClause));
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportError(@NotNull Project project, @NotNull String message) {
|
||||
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor == null) return;
|
||||
HintManager.getInstance().showErrorHint(editor, message);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiReferenceList createPermitsClause(@NotNull PsiFileFactory factory, @NotNull String permitsClause) {
|
||||
PsiJavaFile javaFile = (PsiJavaFile)factory.createFileFromText(JavaLanguage.INSTANCE, "class __Dummy " + permitsClause + "{}");
|
||||
PsiClass newClass = javaFile.getClasses()[0];
|
||||
return Objects.requireNonNull(newClass.getPermitsList());
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class SealClassAction extends BaseElementAtCaretIntentionAction {
|
||||
WriteCommandAction.runWriteCommandAction(project, JavaBundle.message("intention.make.sealed.class.hint.title"), null, runnable);
|
||||
}
|
||||
|
||||
static @Nullable String checkInheritor(@NotNull PsiJavaFile parentFile, @Nullable PsiJavaModule module, @NotNull PsiClass inheritor) {
|
||||
public static @Nullable String checkInheritor(@NotNull PsiJavaFile parentFile, @Nullable PsiJavaModule module, @NotNull PsiClass inheritor) {
|
||||
if (PsiUtil.isLocalOrAnonymousClass(inheritor)) {
|
||||
return "intention.error.make.sealed.class.has.anonymous.or.local.inheritors";
|
||||
}
|
||||
|
||||
+5
@@ -930,4 +930,9 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
|
||||
public @NotNull IntentionAction createAssignFieldFromParameterFix() {
|
||||
return new AssignFieldFromParameterAction(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntentionAction createFillPermitsListFix(PsiIdentifier classIdentifier) {
|
||||
return new FillPermitsListFix(classIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
|
||||
import com.intellij.codeInsight.intention.impl.FillPermitsListFix;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class FillPermitsListInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!HighlightingFeature.SEALED_CLASSES.isAvailable(holder.getFile())) return PsiElementVisitor.EMPTY_VISITOR;
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitClass(PsiClass psiClass) {
|
||||
PsiIdentifier identifier = psiClass.getNameIdentifier();
|
||||
if (identifier == null) return;
|
||||
PsiFile containingFile = tryCast(psiClass.getContainingFile(), PsiJavaFile.class);
|
||||
if (containingFile == null) return;
|
||||
PsiModifierList modifiers = psiClass.getModifierList();
|
||||
if (modifiers == null || !modifiers.hasExplicitModifier(PsiModifier.SEALED)) return;
|
||||
Set<PsiClass> permittedClasses = ContainerUtil.map2Set(psiClass.getPermitsListTypes(), PsiClassType::resolve);
|
||||
boolean hasMissingInheritors = false;
|
||||
for (PsiClass inheritor : ClassInheritorsSearch.search(psiClass, false)) {
|
||||
if (PsiUtil.isLocalOrAnonymousClass(inheritor)) return;
|
||||
// handled in highlighter
|
||||
if (inheritor.getContainingFile() != containingFile) return;
|
||||
hasMissingInheritors |= !permittedClasses.remove(inheritor);
|
||||
}
|
||||
if (hasMissingInheritors) {
|
||||
holder.registerProblem(identifier, JavaBundle.message("inspection.fill.permits.list.display.name"),
|
||||
new FillPermitsListFix(identifier));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -481,6 +481,7 @@ not.allowed.in.sealed.hierarchy={0} is not allowed in the sealed hierarchy
|
||||
invalid.permits.clause=Invalid permits clause: ''{0}'' must be sealed
|
||||
invalid.permits.clause.direct.implementation=Invalid permits clause: ''{0}'' must directly {1, choice, 1#extend|2#implement} ''{2}''
|
||||
sealed.must.have.inheritors=Sealed class must have subclasses
|
||||
permit.list.must.contain.outside.inheritors=Sealed class permit list must contain all inheritors
|
||||
local.classes.must.not.extend.sealed.classes=Local classes must not extend sealed classes
|
||||
anonymous.classes.must.not.extend.sealed.classes=Anonymous classes must not extend sealed classes
|
||||
class.not.allowed.to.extend.sealed.class.from.another.package=Class is not allowed to extend sealed class from another package
|
||||
|
||||
+3
-1
@@ -6,4 +6,6 @@ final class D extends A {}
|
||||
|
||||
non-sealed class C extends A {}
|
||||
|
||||
sealed class F extends A {}
|
||||
sealed class F extends A {}
|
||||
|
||||
final class E extends F {}
|
||||
+3
-1
@@ -6,4 +6,6 @@ final class D extends A {}
|
||||
|
||||
non-sealed class C extends A {}
|
||||
|
||||
sealed class F extends A {}
|
||||
sealed class F extends A {}
|
||||
|
||||
final class E extends F {}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.intention;
|
||||
|
||||
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class FillPermitsListActionTest extends LightIntentionActionTestCase {
|
||||
|
||||
@Override
|
||||
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return LightJavaCodeInsightFixtureTestCase.JAVA_15;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/fillPermitsList";
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.FillPermitsListInspection;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class FillPermitsListInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
|
||||
@Override
|
||||
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new FillPermitsListInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LanguageLevel getLanguageLevel() {
|
||||
return LanguageLevel.JDK_15_PREVIEW;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/fillPermitsList";
|
||||
}
|
||||
}
|
||||
@@ -1273,9 +1273,9 @@ intention.error.make.sealed.class.inheritors.not.in.java.file=Some of the inheri
|
||||
intention.error.make.sealed.class.different.modules=Some of the inheritors are in different modules
|
||||
intention.error.make.sealed.class.interface.has.no.inheritors=Interface has no inheritors
|
||||
intention.make.sealed.class.task.title.set.inheritors.modifiers=Setting inheritors modifiers
|
||||
intention.family.name.fill.permits.list=Add missing inheritors to permits list
|
||||
intention.error.fill.permits.list.no.missing.inheritors=Sealed class has no missing inheritors
|
||||
intention.fill.permits.list.hint.title=Fill Permits List
|
||||
inspection.fill.permits.list.no.missing.inheritors=Sealed class has no missing inheritors
|
||||
inspection.fill.permits.list.display.name=Same file inheritors are missing from permits list
|
||||
inspection.fill.permits.list.fix.name=Add missing inheritors to permits list
|
||||
update.external.annotations=Update External Annotations
|
||||
intention.create.switch.statement=Create switch statement
|
||||
sort.threads.by.type=Sort Threads by Type
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports sealed class which permits list doesn't contain some of the inheritors of this class from the same file.
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2020.3</small></p>
|
||||
</body>
|
||||
</html>
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
public sealed class X permits Y {
|
||||
}
|
||||
|
||||
final class Y extends X {}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
public sealed class <spot>X</spot> {
|
||||
}
|
||||
|
||||
final class Y extends X {}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Adds missing inheritors to permits list of a sealed class.
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user