mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
ConvertInterfaceToClassFix: fixes after review IDEA-216274
GitOrigin-RevId: cd0ac910a3e49f5329e34aee2751fd78c133e31f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
494abe4f2a
commit
f75bc6d37a
@@ -513,7 +513,7 @@ public abstract class QuickFixFactory {
|
||||
public abstract @NotNull IntentionAction createReceiverParameterTypeFix(@NotNull PsiReceiverParameter receiverParameter,
|
||||
@NotNull PsiType enclosingClassType);
|
||||
|
||||
public abstract @NotNull IntentionAction createConvertInterfaceContainingNotAllowedToClassFix(@NotNull PsiClass aClass);
|
||||
public abstract @NotNull IntentionAction createConvertInterfaceToClassFix(@NotNull PsiClass aClass);
|
||||
|
||||
public abstract @NotNull IntentionAction createUnwrapArrayInitializerMemberValueAction(@NotNull PsiArrayInitializerMemberValue arrayValue);
|
||||
}
|
||||
+1
-1
@@ -758,7 +758,7 @@ public final class HighlightClassUtil {
|
||||
String description = JavaErrorBundle.message("not.allowed.in.interface");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createDeleteFix(element));
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createConvertInterfaceContainingNotAllowedToClassFix(aClass));
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createConvertInterfaceToClassFix(aClass));
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -1244,6 +1244,7 @@ public final class HighlightMethodUtil {
|
||||
boolean isExtension = method.hasModifierProperty(PsiModifier.DEFAULT);
|
||||
boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
|
||||
boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE);
|
||||
boolean isConstructor = method.isConstructor();
|
||||
|
||||
final List<IntentionAction> additionalFixes = new ArrayList<>();
|
||||
String description = null;
|
||||
@@ -1275,6 +1276,9 @@ public final class HighlightMethodUtil {
|
||||
IntentionAction makeDefaultFix = QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.DEFAULT, true, false);
|
||||
additionalFixes.add(PriorityIntentionActionWrapper.highPriority(makeDefaultFix));
|
||||
additionalFixes.add(QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.STATIC, true, false));
|
||||
if (isConstructor) {
|
||||
additionalFixes.add(QUICK_FIX_FACTORY.createConvertInterfaceToClassFix(aClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-19
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2021 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.siyeh.ipp.interfacetoclass;
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.intention.PriorityAction;
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
@@ -7,14 +7,17 @@ import com.intellij.codeInspection.util.IntentionFamilyName;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.siyeh.IntentionPowerPackBundle;
|
||||
import com.siyeh.ipp.interfacetoclass.ConvertInterfaceToClassIntention;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ConvertInterfaceContainingNotAllowedToClassFix extends LocalQuickFixAndIntentionActionOnPsiElement implements PriorityAction {
|
||||
public class ConvertInterfaceToClassFix extends LocalQuickFixAndIntentionActionOnPsiElement implements PriorityAction {
|
||||
|
||||
public ConvertInterfaceContainingNotAllowedToClassFix(@Nullable PsiClass aClass) {
|
||||
public ConvertInterfaceToClassFix(@Nullable PsiClass aClass) {
|
||||
super(aClass);
|
||||
}
|
||||
|
||||
@@ -24,9 +27,8 @@ public class ConvertInterfaceContainingNotAllowedToClassFix extends LocalQuickFi
|
||||
@Nullable Editor editor,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
final PsiElement nameIdentifier = getNameIdentifier(startElement);
|
||||
if (nameIdentifier == null) return false;
|
||||
return new ConvertInterfaceToClassIntention().getElementPredicate().satisfiedBy(nameIdentifier);
|
||||
if (!(startElement instanceof PsiClass)) return false;
|
||||
return ConvertInterfaceToClassIntention.canConvertToClass((PsiClass)startElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,9 +37,8 @@ public class ConvertInterfaceContainingNotAllowedToClassFix extends LocalQuickFi
|
||||
@Nullable Editor editor,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
final PsiElement nameIdentifier = getNameIdentifier(startElement);
|
||||
if (nameIdentifier == null) return;
|
||||
new ConvertInterfaceToClassIntention().processIntention(nameIdentifier);
|
||||
if (!(startElement instanceof PsiClass)) return;
|
||||
ConvertInterfaceToClassIntention.convert((PsiClass)startElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,19 +46,13 @@ public class ConvertInterfaceContainingNotAllowedToClassFix extends LocalQuickFi
|
||||
return Priority.LOW;
|
||||
}
|
||||
|
||||
private static PsiIdentifier getNameIdentifier(@NotNull PsiElement element) {
|
||||
final PsiClass aClass = ObjectUtils.tryCast(element, PsiClass.class);
|
||||
if (aClass == null) return null;
|
||||
return aClass.getNameIdentifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @IntentionName @NotNull String getText() {
|
||||
return new ConvertInterfaceToClassIntention().getText();
|
||||
return IntentionPowerPackBundle.message("convert.interface.to.class.intention.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @IntentionFamilyName @NotNull String getFamilyName() {
|
||||
return new ConvertInterfaceToClassIntention().getFamilyName();
|
||||
return IntentionPowerPackBundle.message("convert.interface.to.class.intention.family.name");
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -46,7 +46,6 @@ import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.siyeh.ig.fixes.CreateDefaultBranchFix;
|
||||
import com.siyeh.ig.fixes.CreateMissingSwitchBranchesFix;
|
||||
import com.siyeh.ipp.interfacetoclass.ConvertInterfaceContainingNotAllowedToClassFix;
|
||||
import com.siyeh.ipp.modifiers.ChangeModifierIntention;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -1002,8 +1001,8 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionAction createConvertInterfaceContainingNotAllowedToClassFix(@NotNull PsiClass aClass) {
|
||||
return new ConvertInterfaceContainingNotAllowedToClassFix(aClass);
|
||||
public @NotNull IntentionAction createConvertInterfaceToClassFix(@NotNull PsiClass aClass) {
|
||||
return new ConvertInterfaceToClassFix(aClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Convert to 'class'" "true"
|
||||
|
||||
abstract class A {
|
||||
public A() {
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Convert to 'class'" "true"
|
||||
|
||||
interface A {
|
||||
A()<caret> {
|
||||
System.out.println(42);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,10 +3,10 @@ package com.intellij.java.codeInsight.daemon.quickFix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
|
||||
public class ConvertInterfaceContainingNotAllowedToClassFixTest extends LightQuickFixParameterizedTestCase {
|
||||
public class ConvertInterfaceToClassFixTest extends LightQuickFixParameterizedTestCase {
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/convertInterfaceContainingNotAllowedToClass";
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/convertInterfaceToClass";
|
||||
}
|
||||
}
|
||||
+63
-44
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.siyeh.ipp.interfacetoclass;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -48,6 +49,67 @@ public class ConvertInterfaceToClassIntention extends Intention {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void convert(@NotNull PsiClass anInterface) {
|
||||
final SearchScope searchScope = anInterface.getUseScope();
|
||||
final Collection<PsiClass> inheritors = ClassInheritorsSearch.search(anInterface, searchScope, false).findAll();
|
||||
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
|
||||
inheritors.forEach(aClass -> {
|
||||
final PsiReferenceList extendsList = aClass.getExtendsList();
|
||||
if (extendsList == null) {
|
||||
return;
|
||||
}
|
||||
final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements();
|
||||
if (referenceElements.length > 0) {
|
||||
final PsiElement target = referenceElements[0].resolve();
|
||||
if (target instanceof PsiClass && !CommonClassNames.JAVA_LANG_OBJECT.equals(((PsiClass)target).getQualifiedName())) {
|
||||
conflicts.putValue(aClass, IntentionPowerPackBundle.message(
|
||||
"0.already.extends.1.and.will.not.compile.after.converting.2.to.a.class",
|
||||
RefactoringUIUtil.getDescription(aClass, true), RefactoringUIUtil.getDescription(target, true),
|
||||
RefactoringUIUtil.getDescription(anInterface, false)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final PsiFunctionalExpression functionalExpression = FunctionalExpressionSearch.search(anInterface, searchScope).findFirst();
|
||||
if (functionalExpression != null) {
|
||||
final String conflictMessage = ClassPresentationUtil.getFunctionalExpressionPresentation(functionalExpression, true) +
|
||||
" will not compile after converting " +
|
||||
RefactoringUIUtil.getDescription(anInterface, false) +
|
||||
" to a class";
|
||||
conflicts.putValue(functionalExpression, conflictMessage);
|
||||
}
|
||||
final boolean conflictsDialogOK;
|
||||
if (conflicts.isEmpty()) {
|
||||
conflictsDialogOK = true;
|
||||
}
|
||||
else {
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
if (application.isUnitTestMode()) {
|
||||
throw new BaseRefactoringProcessor.ConflictsInTestsException(conflicts.values());
|
||||
}
|
||||
final ConflictsDialog conflictsDialog =
|
||||
new ConflictsDialog(anInterface.getProject(), conflicts, () -> convertInterfaceToClass(anInterface, inheritors));
|
||||
conflictsDialogOK = conflictsDialog.showAndGet();
|
||||
}
|
||||
if (conflictsDialogOK) {
|
||||
convertInterfaceToClass(anInterface, inheritors);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canConvertToClass(@NotNull PsiClass aClass) {
|
||||
if (!aClass.isInterface() || aClass.isAnnotationType()) {
|
||||
return false;
|
||||
}
|
||||
final SearchScope useScope = aClass.getUseScope();
|
||||
for (PsiClass inheritor :
|
||||
ClassInheritorsSearch.search(aClass, useScope, true)) {
|
||||
if (inheritor.isInterface()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !AnnotationUtil.isAnnotated(aClass, CommonClassNames.JAVA_LANG_FUNCTIONAL_INTERFACE, 0);
|
||||
}
|
||||
|
||||
public static void changeInterfaceToClass(PsiClass anInterface) {
|
||||
final PsiIdentifier nameIdentifier = anInterface.getNameIdentifier();
|
||||
assert nameIdentifier != null;
|
||||
@@ -109,50 +171,7 @@ public class ConvertInterfaceToClassIntention extends Intention {
|
||||
@Override
|
||||
protected void processIntention(@NotNull PsiElement element) {
|
||||
final PsiClass anInterface = (PsiClass)element.getParent();
|
||||
final SearchScope searchScope = anInterface.getUseScope();
|
||||
final Collection<PsiClass> inheritors = ClassInheritorsSearch.search(anInterface, searchScope, false).findAll();
|
||||
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
|
||||
inheritors.forEach(aClass -> {
|
||||
final PsiReferenceList extendsList = aClass.getExtendsList();
|
||||
if (extendsList == null) {
|
||||
return;
|
||||
}
|
||||
final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements();
|
||||
if (referenceElements.length > 0) {
|
||||
final PsiElement target = referenceElements[0].resolve();
|
||||
if (target instanceof PsiClass && !CommonClassNames.JAVA_LANG_OBJECT.equals(((PsiClass)target).getQualifiedName())) {
|
||||
conflicts.putValue(aClass, IntentionPowerPackBundle.message(
|
||||
"0.already.extends.1.and.will.not.compile.after.converting.2.to.a.class",
|
||||
RefactoringUIUtil.getDescription(aClass, true), RefactoringUIUtil.getDescription(target, true),
|
||||
RefactoringUIUtil.getDescription(anInterface, false)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final PsiFunctionalExpression functionalExpression = FunctionalExpressionSearch.search(anInterface, searchScope).findFirst();
|
||||
if (functionalExpression != null) {
|
||||
final String conflictMessage = ClassPresentationUtil.getFunctionalExpressionPresentation(functionalExpression, true) +
|
||||
" will not compile after converting " +
|
||||
RefactoringUIUtil.getDescription(anInterface, false) +
|
||||
" to a class";
|
||||
conflicts.putValue(functionalExpression, conflictMessage);
|
||||
}
|
||||
final boolean conflictsDialogOK;
|
||||
if (conflicts.isEmpty()) {
|
||||
conflictsDialogOK = true;
|
||||
}
|
||||
else {
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
if (application.isUnitTestMode()) {
|
||||
throw new BaseRefactoringProcessor.ConflictsInTestsException(conflicts.values());
|
||||
}
|
||||
final ConflictsDialog conflictsDialog =
|
||||
new ConflictsDialog(anInterface.getProject(), conflicts, () -> convertInterfaceToClass(anInterface, inheritors));
|
||||
conflictsDialogOK = conflictsDialog.showAndGet();
|
||||
}
|
||||
if (conflictsDialogOK) {
|
||||
convertInterfaceToClass(anInterface, inheritors);
|
||||
}
|
||||
convert(anInterface);
|
||||
}
|
||||
|
||||
private static void convertInterfaceToClass(PsiClass anInterface, Collection<PsiClass> inheritors) {
|
||||
|
||||
+5
-22
@@ -15,39 +15,22 @@
|
||||
*/
|
||||
package com.siyeh.ipp.interfacetoclass;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.ipp.base.PsiElementPredicate;
|
||||
|
||||
class ConvertInterfaceToClassPredicate implements PsiElementPredicate {
|
||||
|
||||
@Override
|
||||
public boolean satisfiedBy(PsiElement element) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (!(parent instanceof PsiClass)) {
|
||||
return false;
|
||||
}
|
||||
final PsiClass aClass = (PsiClass)parent;
|
||||
if (!aClass.isInterface() || aClass.isAnnotationType()) {
|
||||
return false;
|
||||
}
|
||||
final PsiClass aClass = ObjectUtils.tryCast(element.getParent(), PsiClass.class);
|
||||
if (aClass == null) return false;
|
||||
final PsiElement leftBrace = aClass.getLBrace();
|
||||
final int offsetInParent = element.getStartOffsetInParent();
|
||||
if (leftBrace == null ||
|
||||
offsetInParent >= leftBrace.getStartOffsetInParent()) {
|
||||
if (leftBrace == null || offsetInParent >= leftBrace.getStartOffsetInParent()) {
|
||||
return false;
|
||||
}
|
||||
final SearchScope useScope = aClass.getUseScope();
|
||||
for (PsiClass inheritor :
|
||||
ClassInheritorsSearch.search(aClass, useScope, true)) {
|
||||
if (inheritor.isInterface()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !AnnotationUtil.isAnnotated(aClass, CommonClassNames.JAVA_LANG_FUNCTIONAL_INTERFACE, 0);
|
||||
return ConvertInterfaceToClassIntention.canConvertToClass(aClass);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user