Quick fix to add abc.ABC to superclasses (PY-30789)

This commit is contained in:
Semyon Proshev
2018-07-10 19:46:03 +03:00
parent b8f5298ee3
commit e2855325d1
8 changed files with 96 additions and 8 deletions

View File

@@ -210,6 +210,7 @@ public class PyNames {
public static final String ABSTRACTMETHOD = "abstractmethod";
public static final String ABSTRACTPROPERTY = "abstractproperty";
public static final String ABC_META_CLASS = "ABCMeta";
public static final String ABC = "abc.ABC";
public static final String TUPLE = "tuple";
public static final String SET = "set";

View File

@@ -2,18 +2,21 @@
package com.jetbrains.python.inspections;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.util.SmartList;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.override.PyOverrideImplementUtil;
import com.jetbrains.python.codeInsight.typing.PyProtocolsKt;
import com.jetbrains.python.inspections.quickfix.PyImplementMethodsQuickFix;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyKnownDecoratorUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyClassLikeType;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -49,9 +52,14 @@ public class PyAbstractClassInspection extends PyInspection {
final List<PyFunction> toImplement = PyOverrideImplementUtil.getAllSuperAbstractMethods(pyClass, myTypeEvalContext);
final ASTNode nameNode = pyClass.getNameNode();
if (!toImplement.isEmpty() && nameNode != null) {
final SmartList<LocalQuickFix> quickFixes = new SmartList<>(new PyImplementMethodsQuickFix(pyClass, toImplement));
if (LanguageLevel.forElement(pyClass).isPy3K()) {
quickFixes.add(new AddABCToSuperclassesQuickFix());
}
registerProblem(nameNode.getPsi(),
PyBundle.message("INSP.NAME.abstract.class.$0.must.implement", pyClass.getName()),
new PyImplementMethodsQuickFix(pyClass, toImplement));
quickFixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
}
@@ -61,7 +69,7 @@ public class PyAbstractClassInspection extends PyInspection {
return true;
}
for (PyClassLikeType superClassType : pyClass.getSuperClassTypes(myTypeEvalContext)) {
if (superClassType != null && "abc.ABC".equals(superClassType.getClassQName())) {
if (superClassType != null && PyNames.ABC.equals(superClassType.getClassQName())) {
return true;
}
}
@@ -72,5 +80,26 @@ public class PyAbstractClassInspection extends PyInspection {
}
return false;
}
private static class AddABCToSuperclassesQuickFix implements LocalQuickFix {
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return "Add '" + PyNames.ABC + "' to superclasses";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PyClass cls = PyUtil.as(descriptor.getPsiElement().getParent(), PyClass.class);
if (cls == null) return;
final PyClass abcClass = PyPsiFacade.getInstance(project).createClassByQName(PyNames.ABC, cls);
if (abcClass == null) return;
PyClassRefactoringUtil.addSuperclasses(project, cls, abcClass);
}
}
}
}

View File

@@ -0,0 +1,4 @@
from PyAbstractClassInspection.quickFix.AddABCToSuperclasses.main_import import A1
class A<caret>2(A1):
pass

View File

@@ -0,0 +1,6 @@
from abc import ABC
from PyAbstractClassInspection.quickFix.AddABCToSuperclasses.main_import import A1
class A2(A1, ABC):
pass

View File

@@ -0,0 +1,6 @@
import abc
class A1(abc.ABC):
@abc.abstractmethod
def m1(self):
pass

View File

@@ -0,0 +1,9 @@
from abc import ABC, abstractmethod
class A1(ABC):
@abstractmethod
def m1(self):
pass
class A<caret>2(A1):
pass

View File

@@ -0,0 +1,9 @@
from abc import ABC, abstractmethod
class A1(ABC):
@abstractmethod
def m1(self):
pass
class A2(A1, ABC):
pass

View File

@@ -20,6 +20,7 @@ import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.TestDataFile;
import com.intellij.testFramework.TestDataPath;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.PyAbstractClassInspection;
import com.jetbrains.python.inspections.PyMissingConstructorInspection;
import com.jetbrains.python.inspections.PyStatementEffectInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
@@ -94,19 +95,19 @@ public class Py3QuickFixTest extends PyTestCase {
runWithLanguageLevel(LanguageLevel.PYTHON34,
() -> doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true));
}
// PY-16421
public void testAddCallSuperSingleStarParamPreserved() {
runWithLanguageLevel(LanguageLevel.PYTHON34,
() -> doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true));
}
// PY-15867
public void testAddCallSuperRequiredKeywordOnlyParamAfterSingleStarInSuperInitIsMerged() {
runWithLanguageLevel(LanguageLevel.PYTHON34,
() -> doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true));
}
// PY-16428
public void testAddParameterNotAvailableInsideAnnotation() {
runWithLanguageLevel(LanguageLevel.PYTHON34, () -> doInspectionTest(PyUnresolvedReferencesInspection.class,
@@ -137,6 +138,29 @@ public class Py3QuickFixTest extends PyTestCase {
});
}
// PY-30789
public void testAddABCToSuperclasses() {
final String[] testFiles = {
"PyAbstractClassInspection/quickFix/AddABCToSuperclasses/main.py",
"PyAbstractClassInspection/quickFix/AddABCToSuperclasses/main_import.py"
};
doInspectionTest(testFiles,
PyAbstractClassInspection.class,
"Add '" + PyNames.ABC + "' to superclasses",
true,
true);
}
// PY-30789
public void testAddImportedABCToSuperclasses() {
doInspectionTest("PyAbstractClassInspection/quickFix/AddImportedABCToSuperclasses/main.py",
PyAbstractClassInspection.class,
"Add '" + PyNames.ABC + "' to superclasses",
true,
true);
}
@Override
@NonNls
protected String getTestDataPath() {