mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-16035 Warning about not implemented abstract methods is hidden for abstract subclasses
This commit is contained in:
@@ -141,6 +141,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 TUPLE = "tuple";
|
||||
public static final String SET = "set";
|
||||
|
||||
@@ -307,13 +307,11 @@ public class PyOverrideImplementUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<PyFunction> getAllSuperFunctions(@NotNull final PyClass pyClass) {
|
||||
public static Collection<PyFunction> getAllSuperFunctions(@NotNull PyClass pyClass) {
|
||||
final Map<String, PyFunction> superFunctions = new HashMap<String, PyFunction>();
|
||||
for (PyClass aClass : pyClass.getAncestorClasses()) {
|
||||
for (PyFunction function : aClass.getMethods(false)) {
|
||||
if (!superFunctions.containsKey(function.getName())) {
|
||||
superFunctions.put(function.getName(), function);
|
||||
}
|
||||
for (PyFunction function : pyClass.getMethods(true)) {
|
||||
if (!superFunctions.containsKey(function.getName())) {
|
||||
superFunctions.put(function.getName(), function);
|
||||
}
|
||||
}
|
||||
return superFunctions.values();
|
||||
|
||||
@@ -20,11 +20,13 @@ import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.override.PyOverrideImplementUtil;
|
||||
import com.jetbrains.python.inspections.quickfix.PyImplementMethodsQuickFix;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.types.PyClassLikeType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -33,6 +35,8 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* User: ktisha
|
||||
*/
|
||||
@@ -58,25 +62,49 @@ public class PyAbstractClassInspection extends PyInspection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyClass(PyClass node) {
|
||||
public void visitPyClass(PyClass pyClass) {
|
||||
if (isAbstract(pyClass)) {
|
||||
return;
|
||||
}
|
||||
final Set<PyFunction> toBeImplemented = new HashSet<PyFunction>();
|
||||
final Collection<PyFunction> functions = PyOverrideImplementUtil.getAllSuperFunctions(node);
|
||||
final Collection<PyFunction> functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass);
|
||||
for (PyFunction method : functions) {
|
||||
if (isAbstractMethodForClass(method, node)) {
|
||||
if (isAbstractMethodForClass(method, pyClass)) {
|
||||
toBeImplemented.add(method);
|
||||
}
|
||||
}
|
||||
final ASTNode nameNode = node.getNameNode();
|
||||
final ASTNode nameNode = pyClass.getNameNode();
|
||||
if (!toBeImplemented.isEmpty() && nameNode != null) {
|
||||
registerProblem(nameNode.getPsi(),
|
||||
PyBundle.message("INSP.NAME.abstract.class.$0.must.implement", node.getName()),
|
||||
new PyImplementMethodsQuickFix(node, toBeImplemented));
|
||||
PyBundle.message("INSP.NAME.abstract.class.$0.must.implement", pyClass.getName()),
|
||||
new PyImplementMethodsQuickFix(pyClass, toBeImplemented));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAbstract(@NotNull PyClass pyClass) {
|
||||
final PyType metaClass = pyClass.getMetaClassType(TypeEvalContext.userInitiated(pyClass.getProject(), pyClass.getContainingFile()));
|
||||
if (metaClass instanceof PyClassLikeType && PyNames.ABC_META_CLASS.equals(metaClass.getName())) {
|
||||
return true;
|
||||
}
|
||||
if (metaClass == null) {
|
||||
final PyExpression metaClassExpr = as(pyClass.getMetaClassExpression(), PyReferenceExpression.class);
|
||||
if (metaClassExpr != null && PyNames.ABC_META_CLASS.equals(metaClassExpr.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (PyFunction method : pyClass.getMethods(false)) {
|
||||
if (PyUtil.isDecoratedAsAbstract(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isAbstractMethodForClass(@NotNull PyFunction method, @NotNull PyClass cls) {
|
||||
final String methodName = method.getName();
|
||||
if (methodName == null || cls.findMethodByName(methodName, false) != null || cls.findClassAttribute(methodName, false) != null) {
|
||||
if (methodName == null ||
|
||||
cls.findMethodByName(methodName, false) != null ||
|
||||
cls.findClassAttribute(methodName, false) != null) {
|
||||
return false;
|
||||
}
|
||||
return PyUtil.isDecoratedAsAbstract(method) || PyOverrideImplementUtil.raisesNotImplementedError(method);
|
||||
|
||||
+3
-4
@@ -42,7 +42,6 @@ import java.util.*;
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class MethodsManager extends MembersManager<PyFunction> {
|
||||
private static final String ABC_META_CLASS = "ABCMeta";
|
||||
|
||||
/**
|
||||
* Some decorators should be copied with methods if method is marked abstract. Here is list.
|
||||
@@ -123,7 +122,7 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
// Add imports for ABC if needed
|
||||
for (final PsiFile file : filesToCheckImport) {
|
||||
addImportFromAbc(file, PyNames.ABSTRACTMETHOD);
|
||||
addImportFromAbc(file, ABC_META_CLASS);
|
||||
addImportFromAbc(file, PyNames.ABC_META_CLASS);
|
||||
PyClassRefactoringUtil.optimizeImports(file); //To remove redundant imports
|
||||
}
|
||||
}
|
||||
@@ -146,11 +145,11 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
// Add (metaclass= for Py3K
|
||||
PyClassRefactoringUtil
|
||||
.addSuperClassExpressions(aClass.getProject(), aClass, null, Collections.singletonList(Pair.create(PyNames.METACLASS,
|
||||
ABC_META_CLASS)));
|
||||
PyNames.ABC_META_CLASS)));
|
||||
}
|
||||
else {
|
||||
// Add __metaclass__ for Py2
|
||||
PyClassRefactoringUtil.addClassAttributeIfNotExist(aClass, PyNames.DUNDER_METACLASS, ABC_META_CLASS);
|
||||
PyClassRefactoringUtil.addClassAttributeIfNotExist(aClass, PyNames.DUNDER_METACLASS, PyNames.ABC_META_CLASS);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import abc
|
||||
|
||||
|
||||
class A1(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def m1(self):
|
||||
pass
|
||||
|
||||
|
||||
class A2(A1):
|
||||
@abc.abstractmethod
|
||||
def m2(self):
|
||||
pass
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import abc
|
||||
|
||||
|
||||
class A1(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def m1(self):
|
||||
pass
|
||||
|
||||
|
||||
class A2(A1):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import abc
|
||||
|
||||
|
||||
class A1(metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def m1(self):
|
||||
pass
|
||||
|
||||
|
||||
class A2(A1, metaclass=abc.ABCMeta):
|
||||
pass
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.jetbrains.python.fixtures.PyInspectionTestCase;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PyAbstractClassInspectionTest extends PyInspectionTestCase {
|
||||
@@ -32,6 +33,25 @@ public class PyAbstractClassInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-16035
|
||||
public void testHiddenForAbstractSubclassWithExplicitMetaclass() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-16035
|
||||
public void testHiddenForAbstractSubclassWithExplicitMetaclassPy3() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() {
|
||||
public void run() {
|
||||
doTest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// PY-16035
|
||||
public void testHiddenForAbstractSubclassWithAbstractMethod() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user