diff --git a/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java b/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java index 2a10d66995df..50c539f4541b 100644 --- a/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java @@ -10,6 +10,7 @@ import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyReferenceExpression; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.resolve.QualifiedResolveResult; +import com.jetbrains.python.psi.types.PyABCUtil; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; import org.jetbrains.annotations.Nls; @@ -57,7 +58,7 @@ public class PyCallingNonCallableInspection extends PyInspection { if (isMethodType(node, classType)) { return; } - if (cls != null && !cls.isSubclass(PyNames.CALLABLE)) { + if (cls != null && !PyABCUtil.isSubclass(cls, PyNames.CALLABLE)) { registerProblem(node, String.format("'%s' object is not callable", cls.getName())); } } diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index 88c5a2d767dd..fc0057749b98 100644 --- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java @@ -178,7 +178,7 @@ public class PyTypeCheckerInspection extends PyInspection { } private static boolean matchClasses(@Nullable PyClass superClass, @Nullable PyClass subClass) { - if (superClass == null || subClass == null || subClass.isSubclass(superClass)) { + if (superClass == null || subClass == null || subClass.isSubclass(superClass) || PyABCUtil.isSubclass(subClass, superClass)) { return true; } else { diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 7f762a519b09..338b678e5b98 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -157,7 +157,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement } public boolean isSubclass(PyClass parent) { - if (this == parent || isSubclassOfABC(new PyClassRef(parent))) { + if (this == parent) { return true; } for (PyClass superclass : iterateAncestorClasses()) { @@ -168,7 +168,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement @Override public boolean isSubclass(String superClassQName) { - if (getQualifiedName().equals(superClassQName) || isSubclassOfABC(new PyClassRef(superClassQName))) { + if (getQualifiedName().equals(superClassQName)) { return true; } for (PyClassRef superclass : iterateAncestors()) { @@ -567,43 +567,6 @@ public class PyClassImpl extends PyPresentableElementImpl implement return null; } - private boolean isSubclassOfABC(PyClassRef superRef) { - final String superQName = superRef.getQualifiedName(); - final PyClass superClass = superRef.getPyClass(); - final String superName = superClass != null ? superClass.getName() : superQName; - if (superName != null) { - final boolean isContainer = hasMethod(PyNames.CONTAINS); - if (PyNames.CONTAINER.equals(superName)) { - return isContainer; - } - if (PyNames.HASHABLE.equals(superName)) { - return hasMethod(PyNames.HASH); - } - final boolean isIterable = hasMethod(PyNames.ITER); - if (PyNames.ITERABLE.equals(superName)) { - return isIterable; - } - if (PyNames.ITERATOR.equals(superName)) { - return isIterable && hasMethod(PyNames.NEXT); - } - final boolean isSized = hasMethod(PyNames.LEN); - if (PyNames.SIZED.equals(superName)) { - return isSized; - } - if (PyNames.CALLABLE.equals(superName)) { - return hasMethod(PyNames.CALL); - } - if (PyNames.SEQUENCE.equals(superName)) { - return isSized && isIterable && isContainer && hasMethod(PyNames.GETITEM); - } - } - return false; - } - - private boolean hasMethod(String name) { - return findMethodByName(name, true) != null; - } - private static class PropertyImpl extends PropertyBunch implements Property { private PropertyImpl(Maybe getter, Maybe setter, Maybe deleter, String doc, PyTargetExpression site) { diff --git a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java new file mode 100644 index 000000000000..b535ec4dbd0e --- /dev/null +++ b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java @@ -0,0 +1,52 @@ +package com.jetbrains.python.psi.types; + +import com.jetbrains.python.PyNames; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyClassRef; +import org.jetbrains.annotations.NotNull; + +/** + * @author vlan + */ +public class PyABCUtil { + public static boolean isSubclass(@NotNull PyClass subClass, @NotNull PyClass superClass) { + final String superName = superClass.getName(); + if (superName != null) { + return isSubclass(subClass, superName); + } + return false; + } + + public static boolean isSubclass(@NotNull PyClass subClass, @NotNull String superClassName) { + if (PyNames.CALLABLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.CALL); + } + if (PyNames.HASHABLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.HASH); + } + final boolean isIterable = hasMethod(subClass, PyNames.ITER); + if (PyNames.ITERABLE.equals(superClassName)) { + return isIterable; + } + if (PyNames.ITERATOR.equals(superClassName)) { + return isIterable && hasMethod(subClass, PyNames.NEXT); + } + final boolean isSized = hasMethod(subClass, PyNames.LEN); + if (PyNames.SIZED.equals(superClassName)) { + return isSized; + } + final boolean isContainer = hasMethod(subClass, PyNames.CONTAINS); + if (PyNames.CONTAINER.equals(superClassName)) { + return isContainer; + } + if (PyNames.SEQUENCE.equals(superClassName)) { + return isSized && isIterable && isContainer && hasMethod(subClass, PyNames.GETITEM); + } + return false; + + } + + private static boolean hasMethod(PyClass cls, String name) { + return cls.findMethodByName(name, true) != null; + } +}