diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java index c5054bee8cee..bb0eb8d5d682 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -122,17 +122,6 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine @NotNull List getAncestorTypes(@NotNull TypeEvalContext context); - /** - * Return the method resolution order list for this class. - *
- * see http://www.python.org/download/releases/2.3/mro/ - *
- * Note: the list begins with this class. It ends with the builtin 'object'. - * If class hierarchy is incorrect, e.g. badly looped, assertions may fail in implementation. - * @return list of classes in method resolution order for this class, at least one element long. - */ - @NotNull List getMRO(); - /** * Scan properties in order of definition, until processor returns true for one of them. * @param processor to check properties diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 1f0243284ebe..9c51e1a04a0e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -2,6 +2,7 @@ package com.jetbrains.python.psi.impl; import com.intellij.codeInsight.completion.CompletionUtil; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -283,30 +284,22 @@ public class PyClassImpl extends PyPresentableElementImpl implement return result.toArray(new PyClass[result.size()]); } - - public @NotNull - List getMRO() { - // see http://www.python.org/download/releases/2.3/mro/ for a muddy explanation. - // see http://hackage.haskell.org/packages/archive/MetaObject/latest/doc/html/src/MO-Util-C3.html#linearize for code to port from. - return mroLinearize(this, Collections.emptyList()); - } - - private static List mroMerge(List> sequences) { - List result = new LinkedList(); // need to insert to 0th position on linearize + private static List mroMerge(@NotNull List> sequences) { + List result = new LinkedList(); // need to insert to 0th position on linearize while (true) { // filter blank sequences - List> nonBlankSequences = new ArrayList>(sequences.size()); - for (List item : sequences) { + List> nonBlankSequences = new ArrayList>(sequences.size()); + for (List item : sequences) { if (item.size() > 0) nonBlankSequences.add(item); } if (nonBlankSequences.isEmpty()) return result; // find a clean head - PyClass head = null; // to keep compiler happy; really head is assigned in the loop at least once. - for (List seq : nonBlankSequences) { + PyClassLikeType head = null; // to keep compiler happy; really head is assigned in the loop at least once. + for (List seq : nonBlankSequences) { head = seq.get(0); boolean head_in_tails = false; - for (List tail_seq : nonBlankSequences) { + for (List tail_seq : nonBlankSequences) { if (tail_seq.indexOf(head) > 0) { // -1 is not found, 0 is head, >0 is tail. head_in_tails = true; break; @@ -319,31 +312,36 @@ public class PyClassImpl extends PyPresentableElementImpl implement head = null; // as a signal } } - assert head != null : "Inconsistent hierarchy!"; // TODO: better diagnostics? // our head is clean; result.add(head); // remove it from heads of other sequences - for (List seq : nonBlankSequences) { - if (seq.get(0) == head) seq.remove(0); + for (List seq : nonBlankSequences) { + if (Comparing.equal(seq.get(0), head)) seq.remove(0); } } // we either return inside the loop or die by assertion } - private static List mroLinearize(PyClass cls, List seen) { - assert (seen.indexOf(cls) < 0) : "Circular import structure on " + PyUtil.nvl(cls); - PyClass[] bases = cls.getSuperClasses(); - List> lins = new ArrayList>(bases.length * 2); - ArrayList new_seen = new ArrayList(seen.size() + 1); - new_seen.add(cls); - for (PyClass base : bases) { - List lin = mroLinearize(base, new_seen); - if (!lin.isEmpty()) lins.add(lin); + @NotNull + private static List mroLinearize(@NotNull PyClassLikeType type, @NotNull List seen, boolean addThisType, + @NotNull TypeEvalContext context) { + assert (seen.indexOf(type) < 0) : "Circular import structure on " + PyUtil.nvl(type); + final List bases = type.getSuperClassTypes(context); + List> lins = new ArrayList>(bases.size() * 2); + ArrayList new_seen = new ArrayList(seen.size() + 1); + new_seen.add(type); + for (PyClassLikeType base : bases) { + if (base != null) { + List lin = mroLinearize(base, new_seen, true, context); + if (!lin.isEmpty()) lins.add(lin); + } } - for (PyClass base : bases) { - lins.add(new SmartList(base)); + for (PyClassLikeType base : bases) { + lins.add(new SmartList(base)); + } + List result = mroMerge(lins); + if (addThisType) { + result.add(0, type); } - List result = mroMerge(lins); - result.add(0, cls); return result; } @@ -891,7 +889,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement final PyClass objClass = PyBuiltinCache.getInstance(this).getClass("object"); if (this == objClass) return true; // a rare but possible case if (hasNewStyleMetaClass(this)) return true; - for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + for (PyClassLikeType type : getOldStyleAncestorTypes(TypeEvalContext.fastStubOnly(null))) { if (type == null) { // unknown, assume new-style class return true; @@ -1057,6 +1055,20 @@ public class PyClassImpl extends PyPresentableElementImpl implement @NotNull @Override public List getAncestorTypes(@NotNull TypeEvalContext context) { + return isNewStyleClass() ? getMROAncestorTypes(context) : getOldStyleAncestorTypes(context); + } + + @NotNull + private List getMROAncestorTypes(@NotNull TypeEvalContext context) { + final PyType thisType = context.getType(this); + if (thisType instanceof PyClassLikeType) { + return mroLinearize((PyClassLikeType)thisType, Collections.emptyList(), false, context); + } + return Collections.emptyList(); + } + + @NotNull + private List getOldStyleAncestorTypes(@NotNull TypeEvalContext context) { final List results = new ArrayList(); final List toProcess = new ArrayList(); final Set seen = new HashSet(); diff --git a/python/testData/inspections/PyArgumentListInspection/multipleInheritedConstructorsMRO.py b/python/testData/inspections/PyArgumentListInspection/multipleInheritedConstructorsMRO.py new file mode 100644 index 000000000000..7d363befa61e --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/multipleInheritedConstructorsMRO.py @@ -0,0 +1,18 @@ +class A(object): + pass + + +class B(object): + def __init__(self, *args): + pass + + +class C(B): + pass + + +class D(A, C): + pass + + +D(42) diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index 4050f3139e6f..0454cd0d322f 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -98,6 +98,11 @@ public class PyArgumentListInspectionTest extends PyTestCase { doTest(); } + // PY-9080 + public void testMultipleInheritedConstructorsMRO() { + doTest(); + } + public void testPy3k() { PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30); try {