FillPermitsListInspection: intention is inspection now, compilation error fix added (IDEA-243901)

GitOrigin-RevId: cbab81c1117410e6bb56bd7066ebd074bbe1d015
This commit is contained in:
Artemiy Sartakov
2020-07-22 06:12:11 +00:00
committed by intellij-monorepo-bot
parent 33d9645cc5
commit 4eba7180d7
25 changed files with 236 additions and 199 deletions
@@ -474,4 +474,6 @@ public abstract class QuickFixFactory {
public abstract IntentionAction createCreateFieldFromParameterFix();
@NotNull
public abstract IntentionAction createAssignFieldFromParameterFix();
public abstract IntentionAction createFillPermitsListFix(PsiIdentifier classIdentifier);
}
@@ -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;
}
+5 -4
View File
@@ -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>
@@ -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";
}
@@ -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
@@ -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 {}
@@ -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 {}
@@ -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";
}
}
@@ -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>
@@ -1,4 +0,0 @@
public sealed class X permits Y {
}
final class Y extends X {}
@@ -1,4 +0,0 @@
public sealed class <spot>X</spot> {
}
final class Y extends X {}
@@ -1,5 +0,0 @@
<html>
<body>
Adds missing inheritors to permits list of a sealed class.
</body>
</html>