diff --git a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java index 5f4fbcf6a2b8..9bbe46eb4250 100644 --- a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java @@ -7,12 +7,10 @@ import com.intellij.util.containers.Stack; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.resolve.PyResolveContext; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; -import java.util.HashSet; -import java.util.Set; - /** * User: catherine * @@ -41,27 +39,16 @@ public class PyMissingConstructorInspection extends PyInspection { @Override public void visitPyClass(final PyClass node) { PsiElement[] superClasses = node.getSuperClassExpressions(); - if (superClasses.length == 0) - return; - Set superNames = new HashSet(); - if (node.isNewStyleClass()) - superNames.add(PyNames.SUPER); - for (PsiElement cl : superClasses) { - if (!PyNames.OBJECT.equals(cl.getText())) - superNames.add(cl.getText()); - } - if (superClasses.length == 1 && PyNames.OBJECT.equals(superClasses[0].getText())) + if (superClasses.length == 0 || (superClasses.length == 1 && PyNames.OBJECT.equals(superClasses[0].getText()))) return; - boolean superHasConstructor = false; - PyClass[] supers = node.getSuperClasses(); - for (PyClass cl : supers) { - if (cl.findMethodByName(PyNames.INIT, false) != null) { - superHasConstructor = true; - break; - } - } - if (!superHasConstructor) return; + Stack superNames = new Stack(); + if (node.isNewStyleClass()) + superNames.push(PyNames.SUPER); + + addSuperNames(superNames, superClasses); + + if (!superHasConstructor(node)) return; PyFunction initMethod = node.findMethodByName(PyNames.INIT, false); if (initMethod != null) { if (hasConstructorCall(initMethod, superNames)) @@ -70,7 +57,39 @@ public class PyMissingConstructorInspection extends PyInspection { } } - private static boolean hasConstructorCall(PyFunction initMethod, Set superNames) { + private static boolean superHasConstructor(PyClass node) { + Stack st = new Stack(); + addSuperClasses(st, node.getSuperClasses()); + + while (!st.empty()) { + if ((st.pop()).findMethodByName(PyNames.INIT, false) != null) { + return true; + } + } + return false; + } + private static void addSuperClasses(Stack st, PyClass[] superClasses) { + for (PyClass cl : superClasses) { + st.push(cl); + addSuperClasses(st, cl.getSuperClasses()); + } + } + + private static void addSuperNames(Stack st, PsiElement[] superClasses) { + for (PsiElement cl : superClasses) { + if (!PyNames.OBJECT.equals(cl.getText())) + st.push(cl.getText()); + + if (cl instanceof PyReferenceExpression) { + PyReferenceExpression ref = (PyReferenceExpression) cl; + final PsiElement result = ref.getReference(PyResolveContext.noProperties()).resolve(); + if (result instanceof PyClass) + addSuperNames(st, ((PyClass)result).getSuperClassExpressions()); + } + } + } + + private static boolean hasConstructorCall(PyFunction initMethod, Stack superNames) { Stack stack = new Stack(); PyStatementList statementList = initMethod.getStatementList(); boolean hasConstructor = false; @@ -95,7 +114,7 @@ public class PyMissingConstructorInspection extends PyInspection { return hasConstructor; } - private static boolean isConstructorCall(PyCallExpression expression, Set superNames) { + private static boolean isConstructorCall(PyCallExpression expression, Stack superNames) { PyExpression callee = expression.getCallee(); if (callee instanceof PyQualifiedExpression) { PyExpression qualifier = ((PyQualifiedExpression)callee).getQualifier(); diff --git a/python/testData/inspections/PyMissingConstructorInspection/test.py b/python/testData/inspections/PyMissingConstructorInspection/test.py index b906b114680d..2aee1177cadf 100644 --- a/python/testData/inspections/PyMissingConstructorInspection/test.py +++ b/python/testData/inspections/PyMissingConstructorInspection/test.py @@ -50,4 +50,16 @@ class Kl: class Kl2(Kl): def __init__(self): - pass \ No newline at end of file + pass + +#PY-3313 +class A(object): + def __init__(self): + print ("Constructor A was called") + +class B(A): + pass + +class C(B): + def __init__(self): + print ("Constructor C was called") \ No newline at end of file