diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java index c9c2eb49e9e0..69c066cd4bb3 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java @@ -28,14 +28,7 @@ import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiReference; import com.jetbrains.python.PyPsiBundle; import com.jetbrains.python.inspections.quickfix.AddCallSuperQuickFix; -import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.psi.PyCallExpression; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyExpression; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.PyQualifiedExpression; -import com.jetbrains.python.psi.PyRecursiveElementVisitor; -import com.jetbrains.python.psi.PyUtil; +import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.TypeEvalContext; import java.util.Objects; import java.util.Optional; @@ -67,7 +60,7 @@ public class PyMissingConstructorInspection extends PyInspection { if (superClasses.length == 0 || superClasses.length == 1 && OBJECT.equals(superClasses[0].getText()) || - !superHasConstructor(node, myTypeEvalContext)) { + !superHasNonAbstractConstructor(node, myTypeEvalContext)) { return; } @@ -86,14 +79,16 @@ public class PyMissingConstructorInspection extends PyInspection { } } - private static boolean superHasConstructor(@NotNull PyClass cls, @NotNull TypeEvalContext context) { + private static boolean superHasNonAbstractConstructor(@NotNull PyClass cls, @NotNull TypeEvalContext context) { final String className = cls.getName(); for (PyClass baseClass : cls.getAncestorClasses(context)) { if (!PyUtil.isObjectClass(baseClass) && - !Objects.equals(className, baseClass.getName()) && - baseClass.findMethodByName(INIT, false, context) != null) { - return true; + !Objects.equals(className, baseClass.getName())) { + var initMethod = baseClass.findMethodByName(INIT, false, context); + if (initMethod != null && !PyKnownDecoratorUtil.hasAbstractDecorator(initMethod, context)) { + return true; + } } } diff --git a/python/testData/inspections/PyMissingConstructorInspection/abstractConstructor.py b/python/testData/inspections/PyMissingConstructorInspection/abstractConstructor.py new file mode 100644 index 000000000000..007e9236cfb3 --- /dev/null +++ b/python/testData/inspections/PyMissingConstructorInspection/abstractConstructor.py @@ -0,0 +1,46 @@ +from abc import abstractmethod + +class A1: + @abstractmethod + def __init__(self): + pass + + +class A2: + @abstractmethod + def __init__(self): + pass + + +class B1: + def __init__(self): + pass + + +class B2: + pass + + +class C1(A1): + def __init__(self): + pass + + +class C2(A1, A2): + def __init__(self): + pass + + +class C3(A1, B1): + def __init__(self): + pass + + +class C4(B1, A1): + def __init__(self): + pass + + +class D(B2, A1): + def __init__(self): + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java b/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java index 3c19e6dccab0..8bcfd0aec800 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java @@ -71,6 +71,11 @@ public class PyMissingConstructorTest extends PyTestCase { doTest(); } + // PY-33265 + public void testAbstractConstructor() { + doTest(); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyMissingConstructorInspection.class);